Compare commits

...

231 Commits
0.9.2 ... 0.9.3

Author SHA1 Message Date
Djamil Legato
c276457390 Merge branch 'release/0.9.3' 2014-10-10 15:13:29 -07:00
Djamil Legato
538fa23b9d Rereleasing 0.9.3 2014-10-10 15:13:19 -07:00
Djamil Legato
d2bfacad4e Resetting permissions of binaries after an upgrade 2014-10-10 15:07:47 -07:00
Andy Miller
cc68065f94 Fix for extended image url handling in markdown 2014-10-10 15:57:29 -06:00
Djamil Legato
00f6738b7e Merge branch 'release/0.9.3' 2014-10-10 14:02:11 -07:00
Djamil Legato
9310c3f12f Merge branch 'release/0.9.3' into develop 2014-10-10 14:02:11 -07:00
Djamil Legato
0fc1f5492f Prepearing for release 2014-10-10 14:02:01 -07:00
Andy Miller
4562de083d Merge branch 'feature/0.9.3_changelog' into develop 2014-10-10 14:05:21 -06:00
Matias Griese
7250a3ecc1 Fix theme overrides 2014-10-10 21:12:11 +03:00
Matias Griese
ac1874795b Modify some blueprints 2014-10-10 15:25:37 +03:00
Matias Griese
2fb8fb62c6 Add function to load user account 2014-10-10 15:02:56 +03:00
Matias Griese
fd9816c177 Bluprints: Move validation parameter inside the form 2014-10-10 12:27:41 +03:00
Matias Griese
5a6e32fce7 Add option to create modular content 2014-10-10 11:57:57 +03:00
Djamil Legato
774850f30d Inverted order of questions on self upgrade. Now first is change log view and then if you want to upgrade. Makes more sense :) 2014-10-09 20:42:57 -07:00
Andy Miller
41f4d269b0 Various tweaks and hopefully improvements to file change detection 2014-10-09 21:04:30 -06:00
Djamil Legato
18e79ce4fe Typo 2014-10-09 18:45:59 -07:00
Djamil Legato
92ce0aa816 Added getChangelog method with option to set a diff starting point version
Selfupgrade command is now going to prompt you before continuing upgrading and optionally can show the change log
Added -y|--all-yes option in grav self upgrade command to skip any prompt and just upgrade
2014-10-09 18:44:30 -07:00
Andy Miller
5ca9180033 return state for compatibility with functions that look for true/false 2014-10-09 18:43:23 -06:00
Andy Miller
1856d1f8c7 Stupidly forgot we already had a recursive delete method in Folder class! 2014-10-09 18:42:53 -06:00
Djamil Legato
af9810d1b3 Now resetting permissions for all binaries inside bin/ when running sandbox 2014-10-09 17:21:46 -07:00
Andy Miller
a454312bd2 Updated change log to reference updates today 2014-10-09 18:07:31 -06:00
Andy Miller
88063c2a60 Merge branch 'develop' of github.com:getgrav/grav into develop 2014-10-09 18:03:54 -06:00
Djamil Legato
8c010fbd3c Updated distributed composer.phar to 73e9db5d9952d52a46ecbc20a269a8c5f9c5b011 2014-10-09 17:03:48 -07:00
Andy Miller
7f615f19f7 Unified multiple versions of "remove directory" method 2014-10-09 18:03:32 -06:00
Djamil Legato
5f7b70748b GPM installer now extracts into the grav instance cache folder rather than system tmp folder 2014-10-09 16:49:08 -07:00
Andy Miller
79268130f4 clear-cache CLI command deletes specific folders. new --all flag added 2014-10-09 16:50:43 -06:00
Andy Miller
8b78b4997e Switched GPM repo to use custom Doctrine cache 2014-10-09 16:50:08 -06:00
Andy Miller
e224841650 switched Doctrine cache to use new stream locator for directory and moved to sub folder 2014-10-09 14:53:37 -06:00
Djamil Legato
a69e5fa115 Switched deprecated Parsedown parse method to text 2014-10-09 12:34:35 -07:00
Andy Miller
539b7931e6 Revert "Moved File Page cache into /cache/pages subfolder"
This reverts commit f303c1243f.
2014-10-09 13:18:32 -06:00
Andy Miller
f303c1243f Moved File Page cache into /cache/pages subfolder 2014-10-09 13:13:24 -06:00
Andy Miller
347d8ecebe Moved Twig cache into /cache/twig subfolder 2014-10-09 13:05:55 -06:00
Andy Miller
f977092115 New changelog work in progress 2014-10-09 12:38:57 -06:00
Matias Griese
5430792687 Disable markdown file compilation for now 2014-10-09 20:40:34 +03:00
Andy Miller
e591212166 typo in descriptions 2014-10-08 14:29:22 -06:00
Andy Miller
77f19974f5 Merge branch 'develop' of github.com:getgrav/grav into develop 2014-10-08 13:24:52 -06:00
Andy Miller
8b34e7dc19 Add inline CSS and JS support to Assets 2014-10-08 13:24:37 -06:00
Djamil Legato
3d4fb6a7d4 Detecting symbolically linked plugins and themes, flagging them and ignoring updates for them 2014-10-08 11:59:25 -07:00
Andy Miller
af7d3ccf83 New change log format 2014-10-08 12:13:15 -06:00
Matias Griese
a8b6841923 Simplify modular page logic 2014-10-08 19:54:59 +03:00
Matias Griese
e09ab139c8 Simplify modular page loading 2014-10-08 19:43:21 +03:00
Matias Griese
fe6a0a27b3 Remove unneeded debugging 2014-10-08 19:00:05 +03:00
Matias Griese
2f4c32e682 Add session close before redirect 2014-10-08 18:59:32 +03:00
Matias Griese
fdf2884f97 Fix Page::filePath() for modular content 2014-10-08 15:12:22 +03:00
Matias Griese
8d8129b49e Add support for modular raw template 2014-10-08 14:33:27 +03:00
Matias Griese
a811ee67cc Add blueprints support for modular content 2014-10-08 14:21:24 +03:00
Matias Griese
f9bfed2b22 Use blueprints from plugins 2014-10-08 13:24:26 +03:00
Matias Griese
3991f51cdf Remove unused file 2014-10-08 12:26:59 +03:00
Matias Griese
0902840aa4 Add support for listing custom page templates/types 2014-10-08 12:26:45 +03:00
Matias Griese
df475af2fd Add back Twig::processString() as it is used in plugins 2014-10-07 20:47:44 +03:00
Matias Griese
9fb56df9a0 Add back configuration key to caching (was accidentally removed) 2014-10-07 14:03:53 +03:00
Matias Griese
b11b39752f Compile markdown files into PHP and cache them 2014-10-07 12:32:31 +03:00
Matias Griese
8cd361ca41 Fix undefined variable in Data\Blueprint class 2014-10-07 12:31:42 +03:00
Matias Griese
4c04dfc747 Do not check blueprint updates when caching configuration 2014-10-07 12:31:01 +03:00
Matias Griese
6cc3d15ee7 Fix warnings when sorting empty collection of pages 2014-10-07 12:22:41 +03:00
Matias Griese
c0bf787dc7 Fix undefined variable in Uri::query() part 2 2014-10-07 12:06:30 +03:00
Matias Griese
4452cf3d1b Fix undefined variable in Uri::query() 2014-10-07 12:02:18 +03:00
Matias Griese
9fa97ad46b Use compiled Yaml files in medium and plugins 2014-10-07 11:04:52 +03:00
Matias Griese
6c3e1d83a7 Merge branch 'develop' of https://github.com/getgrav/grav into develop 2014-10-07 11:02:18 +03:00
Matias Griese
9f2d1b48e6 Use session class in shutdown 2014-10-07 11:02:09 +03:00
Djamil Legato
fd7fd2fb0c Including description_html and description_plain in the Local Packages 2014-10-06 14:58:33 -07:00
Djamil Legato
a58ea17867 Merge branch 'feature/gpm' of github.com:getgrav/grav into feature/gpm 2014-10-06 11:51:31 -07:00
Andy Miller
093e101f1b fix for close connection 2014-10-06 12:50:09 -06:00
Andy Miller
93dd673639 added type bool for assets 2014-10-06 12:46:34 -06:00
Djamil Legato
71b25190b8 Merge branch 'feature/gpm' of github.com:getgrav/grav into feature/gpm 2014-10-06 11:44:42 -07:00
Djamil Legato
1d90cecc11 Now initializing Remote\Grav with GPM 2014-10-06 10:59:58 -07:00
Djamil Legato
e771393489 Cleanup 2014-10-06 10:59:35 -07:00
Matias Griese
122107d1f8 Merge remote-tracking branch 'origin/feature/gpm' into feature/gpm 2014-10-06 20:58:54 +03:00
Matias Griese
ebd81a0dc8 Fix a bug in caching where configuration update had no effect 2014-10-06 20:58:42 +03:00
Djamil Legato
69282e4423 Added isUpdatable method for GPM\Remote\Grav 2014-10-06 10:58:34 -07:00
Djamil Legato
ce71ac352e Typo I suppose 2014-10-06 10:55:11 -07:00
Matias Griese
7738e052e1 Rename CompiledYaml class to CompiledYamlFile 2014-10-06 20:42:34 +03:00
Matias Griese
f43e047497 Fix a bug causing admin not to fill up the data fields 2014-10-06 14:37:04 +03:00
Andy Miller
9eb06da9a9 Added support for spaces in page and media urls 2014-10-05 18:43:33 -06:00
Andy Miller
ce4a9a02d4 Fix for file checking not updating the last modified time 2014-10-04 17:25:39 -06:00
Matias Griese
a8a82e1a3c Fix regression in blueprints 2014-10-02 00:58:47 +03:00
Matias Griese
5abc9b320b Fix missing user stream in gpm 2014-10-02 00:30:26 +03:00
Djamil Legato
0aba432688 Fixed Iterator default $items value 2014-10-01 14:12:45 -07:00
Djamil Legato
d80429a0ea Initializing themes and plugins only in the gpm cli, we don't need that in the classes 2014-10-01 14:12:09 -07:00
Matias Griese
d9145b0ebc Fix infinite loop 2014-10-01 23:28:39 +03:00
Djamil Legato
c05252a570 Removed locator initialization as it's no needed anymore 2014-10-01 12:43:12 -07:00
Djamil Legato
acd81eb7c3 Merge branch 'develop' into feature/gpm 2014-10-01 12:42:30 -07:00
Matias Griese
a3c58fcc5a Fix base directory for streams 2014-10-01 22:23:29 +03:00
Matias Griese
16b541a8ee Merge remote-tracking branch 'origin/develop' into develop 2014-10-01 22:12:26 +03:00
Matias Griese
d705530e64 Add stream "plugin" (alias of "plugins") 2014-10-01 22:12:13 +03:00
Andy Miller
84873484d5 Typo fix for constant 2014-10-01 12:54:10 -06:00
Djamil Legato
eb1883854e Merge branch 'develop' into feature/gpm
Conflicts:
	system/src/Grav/Common/Grav.php
	system/src/Grav/Common/Plugins.php
2014-10-01 11:37:56 -07:00
Matias Griese
9cb83ba368 Merge branch 'develop' of https://github.com/getgrav/grav into feature/multi-config 2014-10-01 20:35:41 +03:00
Andy Miller
82bc6fb308 Renaming 2014-09-30 15:49:09 -06:00
Andy Miller
5aa95c0b7e more flexible children() options 2014-09-30 15:48:36 -06:00
Matias Griese
419b46afb0 Minor fixes 2014-09-30 20:47:18 +03:00
Matias Griese
0b607c5197 Rename Grav\Component back to Grav\Common (unnessessary change) 2014-09-30 19:27:42 +03:00
Matias Griese
9da1ec836e Merge branch 'develop' of https://github.com/getgrav/grav into feature/multi-config
Conflicts:
	system/src/Grav/Common/Filesystem/File/Markdown.php
	system/src/Grav/Common/Grav.php
	system/src/Grav/Common/TwigExtension.php
2014-09-30 19:16:19 +03:00
Djamil Legato
5639941ff3 Fixed reset of the index when listing updates available 2014-09-26 17:57:02 -07:00
Djamil Legato
9d59db5adc Moved $container back to where it belongs 2014-09-26 16:25:19 -07:00
Djamil Legato
033f43b82f Minor space fix 2014-09-26 16:24:29 -07:00
Djamil Legato
91f4dc1a79 Fixed limit not working in the update command 2014-09-26 16:24:15 -07:00
Djamil Legato
9fe24272e3 Merge branch 'develop' into feature/gpm 2014-09-25 18:37:36 -07:00
Djamil Legato
b156c8752a Returning empty array if input wasn't provided. 2014-09-25 18:37:08 -07:00
Djamil Legato
93c51584db Deleted VERSION, no longer needed. 2014-09-25 18:35:45 -07:00
Djamil Legato
f36f31dfcf Added Selfupgrade command for upgrading Grav to the latest available version 2014-09-25 18:35:09 -07:00
Djamil Legato
5cef486981 Added Upgrader GPM class which allows to get details about the latest version of Grav available including version, release date and available assets to download 2014-09-25 18:33:47 -07:00
Djamil Legato
5db100ae49 Updated installer to support sophisticated and non sophisticated methods. Non sophisticated is delete, unzip, move. Sophisticated unzip and loops through each file in the zip (1 level deep only) and replace the node in the destination 2014-09-25 18:30:39 -07:00
Djamil Legato
46bcd1b095 Added new Grav Remote for fetching details about latest version 2014-09-25 18:28:26 -07:00
Djamil Legato
1fc668f0fc Merge branch 'develop' into feature/gpm 2014-09-25 18:27:09 -07:00
Andy Miller
5589b48e01 added columns in raw and ordering 2014-09-25 17:24:49 -06:00
Djamil Legato
cc28f85fde Merge branch 'develop' into feature/gpm 2014-09-25 15:34:58 -07:00
Djamil Legato
a6d46ebcd7 Forcing 2 space indentation on all yaml files 2014-09-25 15:34:29 -07:00
Matias Griese
ac3b0ba3ec Fix saving page as a new type 2014-09-25 23:31:59 +03:00
Andy Miller
e4ff2ea39d new visibile() and routable() filters to Collections 2014-09-25 12:16:54 -06:00
Djamil Legato
4653df1350 Merge branch 'develop' into feature/gpm 2014-09-24 16:00:13 -07:00
Djamil Legato
e3c5234038 Oops, the reference was not meant to be there 2014-09-24 15:59:56 -07:00
Djamil Legato
4f0e1ea8b0 Merge branch 'develop' into feature/gpm 2014-09-24 15:50:40 -07:00
Djamil Legato
eaa05ee252 Added new 'sort_by_key' twig filter which sorts an array by the desired key. It also takes in a direction as second argument which can be any of the predefined sorting flags (http://php.net/manual/en/array.constants.php) 2014-09-24 15:50:21 -07:00
Djamil Legato
0c4d0b5646 Merge branch 'develop' into feature/gpm 2014-09-24 13:54:34 -07:00
Andy Miller
00ac1da29d Add modular/regular page filtering to the children() method. Issue #60 2014-09-24 14:04:49 -06:00
Djamil Legato
6ac126867e Merge branch 'develop' into feature/gpm 2014-09-23 11:40:04 -07:00
Andy Miller
ea7ddeaf55 Added dynamic media support via the URL 2014-09-23 12:34:49 -06:00
Djamil Legato
f536dbfc20 Merge branch 'develop' into feature/gpm 2014-09-22 15:41:58 -07:00
Andy Miller
7261cea92f add filesize for medium 2014-09-22 15:58:12 -06:00
Andy Miller
41d9b52005 added ability to set default file extension 2014-09-22 15:57:46 -06:00
Djamil Legato
50529ae711 Merge branch 'develop' into feature/gpm 2014-09-22 10:57:59 -07:00
Andy Miller
1bfa2f31e7 Added uploads media field 2014-09-22 11:31:23 -06:00
Andy Miller
2b8e3d16a3 updated raw format 2014-09-21 17:17:53 -06:00
Andy Miller
c1e9cc2f02 Merge branch 'feature/frontmatter_support' into develop 2014-09-20 15:34:59 -06:00
Andy Miller
a39656d536 refactored to make more reliable 2014-09-20 15:33:41 -06:00
Andy Miller
103369cc64 sample implementation of frontmatter in pages 2014-09-19 17:51:29 -06:00
Matias Griese
0ab39a9b85 Fix page saving using html instead of raw input 2014-09-19 19:36:13 +03:00
Djamil Legato
af582f88fc Merge branch 'develop' into feature/gpm 2014-09-18 17:32:35 -07:00
Andy Miller
cccd084a55 working on blueprints 2014-09-18 18:05:00 -06:00
Matias Griese
bde5e65d10 Fix a bug on calling raw() from non-existing page 2014-09-18 11:42:27 +03:00
Matias Griese
874bfe47b8 Move page related blueprints into its own folder 2014-09-18 11:33:13 +03:00
Djamil Legato
31c12c9c0d Fixed infoCommand author url since it's now optional 2014-09-17 23:32:36 -07:00
Djamil Legato
ed8903ece3 Updated GPM to use the new repository logic 2014-09-17 17:15:56 -07:00
Djamil Legato
5c92069e52 Cleanup 2014-09-17 17:13:40 -07:00
Djamil Legato
79229f6b35 Fixed count of installed packages 2014-09-17 17:11:32 -07:00
Djamil Legato
f9272887f5 Added new method isUpdatable($slug) to know if a generic slug is updatable or not 2014-09-17 17:11:20 -07:00
Djamil Legato
aa9af76f02 Internal cache for the class to speed up retrieving plugins and themes 2014-09-17 17:08:36 -07:00
Djamil Legato
967202cbc3 Merge branch 'develop' into feature/gpm 2014-09-17 17:05:19 -07:00
Djamil Legato
a7007eabfa Merge branch 'develop' into feature/gpm 2014-09-17 17:03:19 -07:00
Matias Griese
0e75f999cc Minor fix on general page blueprints 2014-09-17 11:24:02 +03:00
Andy Miller
4e3b5039da Added title to new page blueprint 2014-09-16 18:12:34 -06:00
Matias Griese
7a2af8e63e Merge branches 'develop' and 'feature/multi-config' of https://github.com/getgrav/grav into feature/multi-config
Conflicts:
	system/src/Grav/Component/Filesystem/ResourceLocator.php
2014-09-16 09:45:17 +03:00
Andy Miller
3fe18a9213 set taxonomy field to large 2014-09-15 21:28:02 -06:00
Djamil Legato
bfa9cc294e Merge branch 'release/0.9.2' into develop 2014-09-15 17:08:40 -07:00
Djamil Legato
1af0e7068e PSR 2014-09-11 14:09:42 -07:00
Djamil Legato
c8d6bfa455 Cleanup 2014-09-11 14:09:21 -07:00
Djamil Legato
1ad80acd72 Before exiting if a theme is not found, ensure we are not running it from the CLI 2014-09-11 13:08:22 -07:00
Djamil Legato
e19b15cacd Fixed InstallCommand to use the new Installer class 2014-09-11 12:45:17 -07:00
Djamil Legato
2178971e5e Added new Installer class 2014-09-11 12:44:53 -07:00
Djamil Legato
ffecd8bb87 PSR cleanup 2014-09-11 12:44:37 -07:00
Djamil Legato
4326e059a5 Themes now can get initialized by themselves! 2014-09-11 12:44:13 -07:00
Djamil Legato
adaadf3c60 Fixed static method call for all in GPM Plugins 2014-09-11 12:43:51 -07:00
Djamil Legato
c4d590674d Response cleanup and more docs 2014-09-11 12:41:24 -07:00
Djamil Legato
a86159a02c Fixed GPM cli to run the locator 2014-09-11 12:40:53 -07:00
Djamil Legato
88c9436df9 Added GPM docs 2014-09-11 12:40:29 -07:00
Djamil Legato
b26d938147 Folder::mkdir is now public 2014-09-11 12:09:38 -07:00
Djamil Legato
97808a8b6a Merge branch 'develop' into feature/gpm 2014-09-11 12:07:33 -07:00
Matias Griese
6d27da4f9b Add possibility to use /setup.php to customize site setup 2014-09-10 19:53:51 +03:00
Andy Miller
9df91e482e system.yaml updates and synchronization 2014-09-10 10:05:36 -06:00
Matias Griese
eba77a8028 After merge tweaks 2014-09-10 11:22:26 +03:00
Matias Griese
82533c17e6 Merge branch 'develop' of https://github.com/getgrav/grav into feature/multi-config
Conflicts:
	system/config/streams.yaml
	system/src/Grav/Common/Config.php
	system/src/Grav/Common/Grav.php
	system/src/Grav/Common/Themes.php
	vendor/autoload.php
	vendor/composer/autoload_classmap.php
	vendor/composer/autoload_files.php
	vendor/composer/autoload_psr4.php
	vendor/composer/autoload_real.php
	vendor/composer/installed.json
2014-09-10 11:21:03 +03:00
Djamil Legato
fd64fd0822 Merge branch 'develop' into feature/gpm 2014-09-08 22:41:04 -07:00
Djamil Legato
04fb7326e5 Added index count 2014-09-08 22:40:17 -07:00
Djamil Legato
1d8f41df35 Fixed index of packages 2014-09-08 22:40:00 -07:00
Djamil Legato
1b01dff466 Removed FetchCommand since it's not needed anymore 2014-09-08 22:35:56 -07:00
Djamil Legato
75d0b687c1 Converted InstallCommand and UpdateCommand to use the new GPM class 2014-09-08 22:28:44 -07:00
Djamil Legato
d0e6b23637 Throwing exception if fopen fails 2014-09-08 22:27:53 -07:00
Djamil Legato
e9f4c7d9a5 Added countInstalled method
Added findPackages method
2014-09-08 22:27:13 -07:00
Djamil Legato
a3b76252d1 Updated InfoCommand to use the new GPM class 2014-09-08 18:04:36 -07:00
Djamil Legato
231e278c76 Removed fetch logic from the ConsoleTrait as it's not needed anymore 2014-09-08 18:04:09 -07:00
Djamil Legato
5f41beccde Added method to find a package in the repository 2014-09-08 18:02:28 -07:00
Djamil Legato
a44193db16 Updated IndexCommand to use the new GPM class 2014-09-08 17:22:11 -07:00
Djamil Legato
c6dcb22d56 Added methods to retrieve single plugin/theme (remote or local) and all updatables.
Updatable items have now `version` defaulting to the local installed version and `available` as the remote version
2014-09-08 17:21:37 -07:00
Djamil Legato
8e489f6f09 Inverted order of $refresh and $callback arguments for fetching data ($refresh comes first now)
Can now pass $refresh and $callback when instantiating a new GPM
2014-09-08 16:07:09 -07:00
Djamil Legato
618a461835 Merge branch 'develop' into feature/gpm 2014-09-08 15:00:10 -07:00
Djamil Legato
281eb14178 Setting slug as key to the items array 2014-09-08 14:59:12 -07:00
Djamil Legato
1cbb39a38b Initial Grav Package Manager for handling local installed packages and get a status of available packages in the repository 2014-09-08 14:54:45 -07:00
Djamil Legato
da0fafa800 Why does this keep getting reverted on my merges! 2014-09-06 15:31:45 -07:00
Djamil Legato
bfb5a74197 Merge branch 'develop' into feature/gpm 2014-09-06 15:23:36 -07:00
Djamil Legato
cceaeb8d42 Manual merge from develop 2014-09-06 15:20:15 -07:00
Djamil Legato
162409053f Merge branch 'develop' into feature/gpm 2014-09-05 22:42:00 -07:00
Djamil Legato
9d1b50ffbb Merge branch 'develop' into feature/gpm 2014-09-05 22:33:50 -07:00
Djamil Legato
e7568bf8d0 Merge branch 'develop' into feature/gpm 2014-09-05 18:58:48 -07:00
Djamil Legato
353f69deb3 Merge branch 'develop' into feature/gpm 2014-09-05 18:55:51 -07:00
Djamil Legato
24d1d4774e Cleanup police: using traits for GPM 2014-09-04 20:34:36 -07:00
Djamil Legato
b0f37079b0 Removed unused var declaration 2014-09-04 20:33:22 -07:00
Djamil Legato
026646cc46 Forgot to stage the gpm with last commit 2014-09-04 16:29:48 -07:00
Djamil Legato
258f55fe96 Vendors get now installed via composer automatically through first run of GPM or Grav cli. 2014-09-04 16:29:10 -07:00
Djamil Legato
d2cd2a3772 Including composer phar in bin/ 2014-09-04 16:26:55 -07:00
Djamil Legato
c8db29ce46 Removed copying of vendor/ since it's now vendor-free. Install command will take care of it. 2014-09-04 16:26:04 -07:00
Djamil Legato
8a4a810771 Fixed old setupCommand references 2014-09-04 16:19:24 -07:00
Djamil Legato
412216188d Ignoring vendors from now on 2014-09-04 15:41:46 -07:00
Djamil Legato
9103ea0c52 Finally vendor free! 2014-09-04 15:40:38 -07:00
Djamil Legato
ea1c26caa3 Added more info for the BackupCommand help 2014-09-04 15:13:08 -07:00
Djamil Legato
0977d330e6 Reworded description and help of Install and NewProject commands 2014-09-04 14:48:58 -07:00
Djamil Legato
f7c2df0050 Renamed setup command to sandbox. 2014-09-04 14:48:24 -07:00
Djamil Legato
e690e50d86 Merge branch 'develop' into feature/vendor_free 2014-09-04 14:41:14 -07:00
Matias Griese
0f87946276 Move new Blueprints class into RocketTheme\Toolbox 2014-09-03 19:13:57 +03:00
Matias Griese
9e032a7120 Fix wrong namespace declaration 2014-09-03 15:50:17 +03:00
Matias Griese
17e218bdb3 Turn debugger into production mode 2014-09-03 15:23:18 +03:00
Matias Griese
cb705f2d96 Remove debug message 2014-09-03 14:48:53 +03:00
Matias Griese
ac8483f894 Start using RocketTheme\Toolbox and remove local copies of the Toolbox classes 2014-09-03 14:34:40 +03:00
Matias Griese
3098ac9998 Update autoload files 2014-09-03 10:39:39 +03:00
Matias Griese
30246a8666 Post merge fixes 2014-09-03 10:33:45 +03:00
Matias Griese
f606999c40 Merge branch 'develop' of https://github.com/getgrav/grav into feature/multi-config
Conflicts:
	composer.json
	system/config/streams.yaml
	system/src/Grav/Common/Page/Page.php
	system/src/Grav/Common/Theme.php
	system/src/Grav/Common/Themes.php
	system/src/Grav/Component/Filesystem/ResourceLocator.php
	vendor/autoload.php
	vendor/composer/autoload_classmap.php
	vendor/composer/autoload_files.php
	vendor/composer/autoload_real.php
	vendor/composer/installed.json
2014-09-03 10:33:02 +03:00
Matias Griese
affa768efb Ground work for allowing multiple configurations 2014-09-03 09:57:46 +03:00
Djamil Legato
615c91b1c5 Minor change in the output 2014-09-02 22:52:00 -07:00
Djamil Legato
a0bc5bf765 Added Update command that scans for installed extensions and look for available updates (as well as install them!) 2014-09-02 22:41:07 -07:00
Djamil Legato
2ab433c339 Added an --all-yes option to assume yes to all questions (or automatically skip if it's a better approach) 2014-09-02 22:39:00 -07:00
Djamil Legato
267fa23d51 Super minor aesthetic change 2014-09-02 22:35:43 -07:00
Djamil Legato
e6565d9394 Fixed InstallCommand description 2014-09-02 17:51:28 -07:00
Djamil Legato
3b848e4a91 Merge branch 'develop' into feature/vendor_free 2014-09-02 17:40:56 -07:00
Djamil Legato
2c4a724fc0 Merge branch 'develop' into feature/vendor_free 2014-09-02 11:02:32 -07:00
Djamil Legato
3568c25ef1 Merge branch 'develop' into feature/vendor_free 2014-09-01 23:21:41 -07:00
Djamil Legato
3948aa77a8 Cleanup 2014-09-01 23:21:18 -07:00
Djamil Legato
0d0c22c940 Added destination option to allow using the IndexCommand on a different grav instance
IndexCommand displays the local and remote versions. It also detects if the package is already installed
2014-09-01 22:12:55 -07:00
Djamil Legato
1ac4014d64 Updated autoload 2014-09-01 17:59:03 -07:00
Djamil Legato
627646bf35 Merge branch 'develop' into feature/vendor_free 2014-09-01 17:57:48 -07:00
Djamil Legato
67a325e8e9 Removing the tmp folder when finished installing 2014-09-01 17:57:13 -07:00
Djamil Legato
69e345f64f Added InstallCommand that allows to install packages from the index list of grav 2014-09-01 17:53:34 -07:00
Djamil Legato
37980d15e4 Added InfoCommand that provides more detailed informations about a package like description, author, version, etc. 2014-08-31 22:59:01 -07:00
Djamil Legato
3fc6c47b5e Added IndexCommand that lists all the resources available to download and install 2014-08-31 22:58:10 -07:00
Djamil Legato
dee24787ba Added FetchCommand to grab all the resources informations from getgrav.org and cache it for a day 2014-08-31 22:57:18 -07:00
Djamil Legato
7d9142fa03 Initial commit for GPM (Grav Package Manager) 2014-08-31 22:56:25 -07:00
Djamil Legato
51cca81bc8 Moved all console commands under the Cli folder for better organization (this requires a composer update -o to pick up) 2014-08-31 22:54:35 -07:00
Djamil Legato
8de6231116 Added fallbacks for URI class so that it can be used in the CLI 2014-08-31 22:52:04 -07:00
Matias Griese
b507815eef Fix some random bugs 2014-08-22 19:30:17 +03:00
Matias Griese
c56efb9a10 Use stream wrapper in pages, plugins and themes 2014-08-21 13:06:48 +03:00
Matias Griese
eba9002400 Implement ArrayTraits on Blueprint 2014-08-21 13:01:30 +03:00
Matias Griese
7d5426144d Implement more ArrayTraits and use them in Iterator 2014-08-21 12:58:48 +03:00
108 changed files with 4643 additions and 3139 deletions

View File

@@ -11,3 +11,8 @@ trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 4
# 2 space indentation
[*.yaml, *.yml]
indent_style = space
indent_size = 2

2
.gitignore vendored
View File

@@ -1,7 +1,7 @@
# Composer
composer.lock
.composer
vendor
vendor/
# Sass
.sass-cache

115
CHANGELOG.md Normal file
View File

@@ -0,0 +1,115 @@
# v0.9.3 beta
## 10/09/2014
1. [](#new)
* GPM (Grav Package Manager) Added
* Support for multiple Grav configurations
* Dynamic media support via URL
* Added inlineCss and inlineJs support for Assets
2. [](#improved)
* YAML caching for increased performance
* Use stream wrapper in pages, plugins and themes
* Switched to RocketTheme toolbox for some core functionality
* Renamed `setup` CLI command to `sandbox`
* Broke cache types out into multiple directories in the cache folder
* Removed vendor libs from github repository
* Various PSR cleanup of code
* Various Blueprint updates to support upcoming Admin plugin
* Added ability to filter page children for normal/modular/all
* Added `sort_by_key` twig filter
* Added `visible()` and `routable()` filters to page collections
* Use session class in shutdown process
* Improvements to modular page loading
* Various code cleanup and optimizations
3. [](#bugfix)
* Fixed file checking not updating the last modified time. For real this time!
* Switched debugger to PRODUCTION mode by default
* Various fixes in URI class for increased reliability
# v0.9.2 beta
## 09/15/2014
1. [](#new)
* New flexible site and page metadata support including ObjectGraph and Facebook
* New method to get user IP address in URI object
* Added new onShutdown() event that fires after connection is closed for Async features
2. [](#improved)
* Skip assets pipeline minify on Windows platforms by default due to PHP issue 47689
* Fixed multiple level menus not highlighting correctly
* Updated some blueprints in preparation for admin plugin
* Fail gracefully when theme does not exist
* Add stream support into ResourceLocator::addPath()
* Separate themes from plugins, add themes:// stream and onTask events
* Added barDump() to Debugger
* Removed stray test page
* Override modified only if a non-markdown file was modified
* Added assets attributes support
* Auto-run composer install when running the Grav CLI
* Vendor folder removed from repository
* Minor configuration performance optimizations
* Minor debugger performance optimizations
3. [](#bugfix)
* Fix url() twig function when Grav isn't installed at root
* Workaround for PHP bug 52065
* Fixed getList() method on Pages object that was not working
* Fix for open_basedir error
* index.php now warns if not running on PHP 5.4
* Removed memcached option (redundant)
* Removed memcache from auto setup, added memcache server configuration option
* Fix broken password validation
* Back to proper PSR-4 Autoloader
# v0.9.1 beta
## 09/02/2014
1. [](#new)
* Added new `theme://` PHP stream for current theme
2. [](#improved)
* Default to new `file` modification checking rather than `folder`
* Added support for various markdown link formats to convert to Grav-friendly URLs
* Moved configure() from Theme to Themes class
* Fix autoloading without composer update -o
* Added support for Twig url method
* Minor code cleanup
3. [](#bugfix)
* Fixed issue with page changes not being picked up
* Fixed Minify to provide `@supports` tag compatibility
* Fixed ResourceLocator not working with multiple paths
* Fixed issue with Markdown process not stripping LFs
* Restrict file type extensions for added security
* Fixed template inheritance
* Moved Browser class to proper location
# v0.9.0 beta
## 08/25/2014
1. [](#new)
* Addition of Dependency Injection Container
* Refactored plugins to use Symfony Event Dispatcher
* New Asset Manager to provide unified management of JavaScript and CSS
* Asset Pipelining to provide unification, minify, and optimazation of JavaScript and CSS
* Grav Media support directly in Markdown syntax
* Additional Grav Generator meta tag in default themes
* Added support for PHP Stream Wrapper for resource location
* Markdown Extra support
* Browser object for fast browser detection
2. [](#improved)
* PSR-4 Autoloader mechanism
* Tracy Debugger new `detect` option to detect running environment
* Added new `random` collection sort option
* Make media images progressive by default
* Additional URI filtering for improved security
* Safety checks to ensure PHP 5.4.0+
* Move to Slidebars side navigation in default Antimatter theme
* Updates to `.htaccess` including section on `RewriteBase` which is needed for some hosting providers
3. [](#bugfix)
* Fixed issue when installing in an apache userdir (~username) folder
* Various mobile CSS issues in default themes
* Various minor bug fixes
# v0.8.0 beta
## 08/13/2014
1. [](#new)
* Initial Release

View File

@@ -1,6 +1,6 @@
# ![](https://avatars1.githubusercontent.com/u/8237355?v=2&s=50) Grav
Grav is a **Fast**, **Simple**, and **Flexible**, file-based Web-platform. There is **Zero** installation required. Just extract the ZIP archive, and you are already up and running. It follows similar principals to other flat-file CMS platforms, but has a different design philosophy than most.
Grav is a **Fast**, **Simple**, and **Flexible**, file-based Web-platform. There is **Zero** installation required. Just extract the ZIP archive, and you are already up and running. It follows similar principals to other flat-file CMS platforms, but has a different design philosophy than most. Grav comes with a powerful **Package Management System** to allow for simple installation and upgrading of plugins and themes, as well as simple updating of Grav itself.
The underlying architecture of Grav has been designed to use well-established and _best-in-class_ technologies, where applicable, to ensure that Grav is simple to use and easy to extend. Some of these key technologies include:

View File

@@ -1 +0,0 @@
0.9.2

Binary file not shown.

42
bin/gpm Executable file
View File

@@ -0,0 +1,42 @@
#!/usr/bin/env php
<?php
define('GRAV_CLI', true);
if (version_compare($ver = PHP_VERSION, $req = '5.4.0', '<')) {
exit(sprintf("You are running PHP %s, but Grav needs at least PHP %s to run.\n", $ver, $req));
}
if (!file_exists(__DIR__ . '/../vendor')){
// Before we can even start, we need to run composer first
echo "Preparing to install vendor dependencies...\n\n";
echo system('php bin/composer.phar --working-dir="'.__DIR__.'/../" --no-interaction install');
echo "\n\n";
}
use Symfony\Component\Console\Application;
use Grav\Common\Grav;
$autoload = require_once(__DIR__ . '/../vendor/autoload.php');
if (!ini_get('date.timezone')) {
date_default_timezone_set('UTC');
}
if (!file_exists(ROOT_DIR . 'index.php')) {
exit('FATAL: Must be run from ROOT directory of Grav!');
}
$grav = Grav::instance(array('loader' => $autoload));
$grav['config']->init();
$grav['plugins']->init();
$grav['themes']->init();
$app = new Application('Grav Package Manager', GRAV_VERSION);
$app->addCommands(array(
new \Grav\Console\Gpm\IndexCommand(),
new \Grav\Console\Gpm\InfoCommand(),
new \Grav\Console\Gpm\InstallCommand(),
new \Grav\Console\Gpm\UpdateCommand(),
new \Grav\Console\Gpm\SelfupgradeCommand(),
));
$app->run();

View File

@@ -1,5 +1,6 @@
#!/usr/bin/env php
<?php
define('GRAV_CLI', true);
if (version_compare($ver = PHP_VERSION, $req = '5.4.0', '<')) {
exit(sprintf("You are running PHP %s, but Grav needs at least PHP %s to run.\n", $ver, $req));
@@ -8,13 +9,13 @@ if (version_compare($ver = PHP_VERSION, $req = '5.4.0', '<')) {
if (!file_exists(__DIR__ . '/../vendor')){
// Before we can even start, we need to run composer first
echo "Preparing to install vendor dependencies...\n\n";
echo system('php bin/composer.phar --working-dir="'.__DIR__.'/../" --no-interaction install -o');
echo system('php bin/composer.phar --working-dir="'.__DIR__.'/../" --no-interaction install');
echo "\n\n";
}
use Symfony\Component\Console\Application;
require_once(__DIR__ . '/../vendor/autoload.php');
require_once __DIR__ . '/../vendor/autoload.php';
if (!ini_get('date.timezone')) {
date_default_timezone_set('UTC');
@@ -26,11 +27,11 @@ if (!file_exists(ROOT_DIR . 'index.php')) {
$app = new Application('Grav CLI Application', '0.1.0');
$app->addCommands(array(
new Grav\Console\InstallCommand(),
new Grav\Console\SetupCommand(),
new Grav\Console\CleanCommand(),
new Grav\Console\ClearCacheCommand(),
new Grav\Console\BackupCommand(),
new Grav\Console\NewProjectCommand(),
new Grav\Console\Cli\InstallCommand(),
new Grav\Console\Cli\SandboxCommand(),
new Grav\Console\Cli\CleanCommand(),
new Grav\Console\Cli\ClearCacheCommand(),
new Grav\Console\Cli\BackupCommand(),
new Grav\Console\Cli\NewProjectCommand(),
));
$app->run();

View File

@@ -18,8 +18,19 @@
"ircmaxell/password-compat": "1.0.*",
"mrclay/minify": "dev-master",
"donatj/phpuseragentparser": "dev-master",
"pimple/pimple": "~3.0"
"pimple/pimple": "~3.0",
"rockettheme/toolbox": "dev-develop"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/rockettheme/toolbox"
},
{
"type": "vcs",
"url": "https://github.com/rhukster/minify"
}
],
"autoload": {
"psr-4": {
"Grav\\": "system/src/Grav"
@@ -28,11 +39,5 @@
},
"archive": {
"exclude": ["VERSION"]
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/rhukster/minify"
}
]
}
}

View File

@@ -1,5 +1,5 @@
title: Media
validation: loose
form:
validation: loose
fields:

View File

@@ -1,5 +1,6 @@
title: Site
form:
validation: loose
fields:
content:
@@ -26,6 +27,7 @@ form:
taxonomies:
type: text
size: large
label: Taxonomy Types
classes: fancy
validate:

View File

@@ -0,0 +1,7 @@
title: File Streams
form:
validation: loose
fields:
schemes.xxx:
type: array

View File

@@ -1,6 +1,7 @@
title: System
form:
validation: loose
fields:
content:
@@ -233,6 +234,8 @@ form:
options:
1: Yes
0: No
validate:
type: bool
assets.css_minify:
type: toggle
@@ -241,6 +244,8 @@ form:
options:
1: Yes
0: No
validate:
type: bool
assets.css_minify_windows:
type: toggle
@@ -249,6 +254,8 @@ form:
options:
1: Yes
0: No
validate:
type: bool
assets.css_rewrite:
type: toggle
@@ -257,6 +264,8 @@ form:
options:
1: Yes
0: No
validate:
type: bool
assets.js_pipeline:
type: toggle
@@ -265,6 +274,8 @@ form:
options:
1: Yes
0: No
validate:
type: bool
assets.js_minify:
type: toggle
@@ -273,6 +284,8 @@ form:
options:
1: Yes
0: No
validate:
type: bool
debugger:
type: section
@@ -340,7 +353,7 @@ form:
validate:
type: bool
debugger.shutdown.close_conection:
debugger.shutdown.close_connection:
type: toggle
label: Shutdown Close Connection
highlight: 1
@@ -349,5 +362,3 @@ form:
0: No
validate:
type: bool

View File

@@ -0,0 +1,54 @@
rules:
slug:
pattern: "[a-z][a-z0-9_\-]+"
min: 2
max: 80
form:
validation: loose
fields:
section:
type: section
title: Add Modular Content
title:
type: text
label: Page Title
validate:
required: true
folder:
type: text
label: Folder Name
validate:
type: slug
required: true
route:
type: select
label: Page
classes: fancy
@data-options: '\Grav\Common\Page\Pages::parents'
@data-default: '\Grav\Plugin\admin::route'
options:
'': '- Select -'
validate:
required: true
type:
type: select
classes: fancy
label: Modular Template
default: default
@data-options: '\Grav\Common\Page\Pages::modularTypes'
validate:
required: true
modular:
type: hidden
default: 1
validate:
type: bool

View File

@@ -0,0 +1,84 @@
rules:
slug:
pattern: "[a-z][a-z0-9_\-]+"
min: 2
max: 80
form:
validation: loose
fields:
tabs:
type: tabs
active: 1
fields:
content:
type: tab
title: Content
fields:
frontmatter:
type: frontmatter
label: Frontmatter
content:
type: markdown
label: Content
uploads:
type: uploads
label: Page Media
options:
type: tab
title: Options
fields:
columns:
type: columns
fields:
column1:
type: column
fields:
folder:
type: text
label: Filename
validate:
type: slug
required: true
route:
type: select
label: Parent
classes: fancy
@data-options: '\Grav\Common\Page\Pages::parents'
@data-default: '\Grav\Plugin\admin::route'
options:
'/': '- Root -'
validate:
required: true
type:
type: select
classes: fancy
label: Modular Template
default: default
@data-options: '\Grav\Common\Page\Pages::modularTypes'
validate:
required: true
column2:
type: column
fields:
order:
type: order
label: Ordering

View File

@@ -0,0 +1,48 @@
rules:
slug:
pattern: "[a-z][a-z0-9_\-]+"
min: 2
max: 80
form:
validation: loose
fields:
section:
type: section
title: Add Page
title:
type: text
label: Page Title
validate:
required: true
folder:
type: text
label: Folder Name
validate:
type: slug
required: true
route:
type: select
label: Parent Page
classes: fancy
@data-options: '\Grav\Common\Page\Pages::parents'
@data-default: '\Grav\Plugin\admin::route'
options:
'/': '- Root -'
validate:
required: true
type:
type: select
classes: fancy
label: Display Template
default: default
@data-options: '\Grav\Common\Page\Pages::types'
validate:
required: true

View File

@@ -5,14 +5,14 @@ rules:
max: 80
form:
validation: loose
fields:
route:
type: select
label: Parent
@data-options: '\Grav\Common\Page\Pages::parents'
@data-default: '\Grav\Plugin\admin::route'
options:
'': '- Root -'
title:
type: text
label: Title
validate:
required: true
folder:
type: text
@@ -21,8 +21,22 @@ form:
type: slug
required: true
route:
type: select
label: Parent
classes: fancy
@data-options: '\Grav\Common\Page\Pages::parents'
@data-default: '\Grav\Plugin\admin::route'
options:
'/': '- Root -'
validate:
required: true
type:
type: select
label: Page Type
classes: fancy
label: Display Template
default: default
@data-options: '\Grav\Common\Page\Pages::types'
validate:
required: true

View File

@@ -0,0 +1,84 @@
rules:
slug:
pattern: "[a-z][a-z0-9_\-]+"
min: 2
max: 80
form:
validation: loose
fields:
tabs:
type: tabs
active: 1
fields:
content:
type: tab
title: Content
fields:
frontmatter:
type: frontmatter
label: Frontmatter
content:
type: markdown
label: Content
uploads:
type: uploads
label: Page Media
options:
type: tab
title: Options
fields:
columns:
type: columns
fields:
column1:
type: column
fields:
folder:
type: text
label: Folder Name
validate:
type: slug
required: true
route:
type: select
label: Parent
classes: fancy
@data-options: '\Grav\Common\Page\Pages::parents'
@data-default: '\Grav\Plugin\admin::route'
options:
'/': '- Root -'
validate:
required: true
type:
type: select
classes: fancy
label: Display Template
default: default
@data-options: '\Grav\Common\Page\Pages::types'
validate:
required: true
column2:
type: column
fields:
order:
type: order
label: Ordering

View File

@@ -0,0 +1,81 @@
title: Site
form:
validation: loose
fields:
content:
type: section
title: Account
fields:
username:
type: unset
password:
type: password
size: large
label: Password
validate:
required: true
email:
type: text
size: large
label: Email
validate:
required: true
fullname:
type: text
size: large
label: Full name
validate:
required: true
title:
type: text
size: large
label: Title
admin:
type: section
title: Admin Access
fields:
access.admin.super:
type: toggle
label: Super user
default: 0
highlight: 1
options:
1: Yes
0: No
validate:
type: bool
access.admin.login:
type: toggle
label: Admin login
default: 0
highlight: 1
options:
1: Yes
0: No
validate:
type: bool
site:
type: section
title: Site Access
fields:
access.site.login:
type: toggle
label: Site login
default: 1
highlight: 1
options:
1: Yes
0: No
validate:
type: bool

View File

@@ -0,0 +1,15 @@
title: Add Account
form:
validation: loose
fields:
content:
type: section
title: Add Account
username:
type: text
label: Username
validate:
required: true

View File

@@ -1,46 +1,25 @@
schemes:
plugin:
type: ReadOnlyStream
paths:
- user/plugins
- system/plugins
asset:
type: ReadOnlyStream
paths:
- assets
cache:
image:
type: ReadOnlyStream
paths:
- cache
log:
type: ReadOnlyStream
paths:
- logs
user:
type: ReadOnlyStream
paths:
- user
- user://images
page:
type: ReadOnlyStream
paths:
- user/pages
- user://pages
account:
type: ReadOnlyStream
paths:
- user/accounts
- user://accounts
data:
type: ReadOnlyStream
paths:
- user/data
themes:
type: ReadOnlyStream
paths:
- user/themes
- user://data

View File

@@ -2,7 +2,7 @@
// Some standard defines
define('GRAV', true);
define('GRAV_VERSION', '0.9.2');
define('GRAV_VERSION', '0.9.3');
define('DS', '/');
// Directories and Paths
@@ -14,7 +14,7 @@ define('USER_PATH', 'user/');
define('USER_DIR', ROOT_DIR . USER_PATH);
define('SYSTEM_DIR', ROOT_DIR .'system/');
define('ASSETS_DIR', ROOT_DIR . 'assets/');
define('CACHE_DIR', ROOT_DIR .'cache/');
define('CACHE_DIR', ROOT_DIR . 'cache/');
define('IMAGES_DIR', ROOT_DIR . 'images/');
define('LOG_DIR', ROOT_DIR .'logs/');
define('VENDOR_DIR', ROOT_DIR .'vendor/');

View File

@@ -4,6 +4,7 @@ namespace Grav\Common;
use Closure;
use Exception;
use FilesystemIterator;
use Grav\Common\Config\Config;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use RegexIterator;
@@ -64,6 +65,8 @@ class Assets
protected $collections = array();
protected $css = array();
protected $js = array();
protected $inline_css = array();
protected $inline_js = array();
// Some configuration variables
protected $config;
@@ -201,6 +204,25 @@ class Assets
return $this;
}
/**
* Add an inline CSS asset.
*
* It checks for duplicates.
* For adding chunks of string-based inline CSS
*
* @param mixed $asset
* @param int $priority the priority, bigger comes first
* @return $this
*/
public function addInlineCss($asset, $priority = 10) {
if (is_string($asset) && !in_array($asset, $this->inline_css)) {
$this->inline_css[] = $asset;
}
return $this;
}
/**
* Add a CSS asset.
*
@@ -222,11 +244,32 @@ class Assets
return $this;
}
if( ! $this->isRemoteLink($asset))
if( !$this->isRemoteLink($asset)) {
$asset = $this->buildLocalLink($asset);
}
if( ! in_array($asset, $this->css))
$this->css[] = ['asset'=>$asset, 'priority'=>$priority, 'pipeline'=>$pipeline];
if( !array_key_exists($asset, $this->css)) {
$this->css[$asset] = ['asset'=>$asset, 'priority'=>$priority, 'pipeline'=>$pipeline];
}
return $this;
}
/**
* Add an inline JS asset.
*
* It checks for duplicates.
* For adding chunks of string-based inline JS
*
* @param mixed $asset
* @param int $priority the priority, bigger comes first
* @return $this
*/
public function addInlineJs($asset) {
if (is_string($asset) && !in_array($asset, $this->inline_js)) {
$this->inline_js[] = $asset;
}
return $this;
}
@@ -252,11 +295,13 @@ class Assets
return $this;
}
if( ! $this->isRemoteLink($asset))
if( !$this->isRemoteLink($asset)) {
$asset = $this->buildLocalLink($asset);
}
if( ! in_array($asset, $this->js))
$this->js[] = ['asset'=>$asset, 'priority'=>$priority, 'pipeline'=>$pipeline];
if( !array_key_exists($asset, $this->js)) {
$this->js[$asset] = ['asset' => $asset, 'priority' => $priority, 'pipeline' => $pipeline];
}
return $this;
}
@@ -289,8 +334,19 @@ class Assets
}
foreach($this->css as $file)
$output .= '<link href="'.$file['asset'].'"'.$attributes.' />'."\n";
foreach($this->css as $file) {
$output .= '<link href="' . $file['asset'] . '"' . $attributes . ' />' . "\n";
}
// Render Inline CSS
if (count($this->inline_css) > 0) {
$output .= "<style>\n";
foreach($this->inline_css as $inline) {
$output .= $inline . "\n";
}
$output .= "</style>\n";
}
return $output;
}
@@ -322,8 +378,18 @@ class Assets
}
foreach($this->js as $file)
$output .= '<script src="'.$file['asset'].'"'.$attributes.' ></script>'."\n";
foreach($this->js as $file) {
$output .= '<script src="' . $file['asset'] . '"' . $attributes . ' ></script>' . "\n";
}
// Render Inline JS
if (count($this->inline_js) > 0) {
$output .= "<script>\n";
foreach($this->inline_js as $inline) {
$output .= $inline . "\n";
}
$output .= "</script>\n";
}
return $output;
}

View File

@@ -2,6 +2,7 @@
namespace Grav\Common;
use \Doctrine\Common\Cache\Cache as DoctrineCache;
use Grav\Common\Config\Config;
/**
* The GravCache object is used throughout Grav to store and retrieve cached data.
@@ -34,6 +35,8 @@ class Cache extends Getters
*/
protected $enabled;
protected $cache_dir;
/**
* Constructor
*
@@ -55,6 +58,8 @@ class Cache extends Getters
/** @var Config $config */
$this->config = $grav['config'];
$this->cache_dir = $grav['locator']->findResource('cache://doctrine', true, true);
/** @var Uri $uri */
$uri = $grav['uri'];
@@ -63,7 +68,7 @@ class Cache extends Getters
$this->enabled = (bool) $this->config->get('system.cache.enabled');
// Cache key allows us to invalidate all cache on configuration changes.
$this->key = substr(md5(($prefix ? $prefix : 'g') . $uri->rootUrl(true) . $this->config->key . GRAV_VERSION), 2, 8);
$this->key = substr(md5(($prefix ? $prefix : 'g') . $uri->rootUrl(true) . $this->config->key() . GRAV_VERSION), 2, 8);
$this->driver = $this->getCacheDriver();
@@ -117,7 +122,7 @@ class Cache extends Getters
break;
default:
$driver = new \Doctrine\Common\Cache\FilesystemCache(CACHE_DIR);
$driver = new \Doctrine\Common\Cache\FilesystemCache($this->cache_dir);
break;
}

View File

@@ -1,259 +0,0 @@
<?php
namespace Grav\Common;
use Grav\Common\Data\Blueprints;
use Grav\Common\Data\Data;
use Grav\Common\Filesystem\File;
use Grav\Common\Filesystem\Folder;
/**
* The Config class contains configuration information.
*
* @author RocketTheme
* @license MIT
*/
class Config extends Data
{
/**
* @var string Configuration location in the disk.
*/
public $filename;
/**
* @var string MD5 from the files.
*/
public $key;
/**
* @var array Configuration file list.
*/
public $files = array();
/**
* @var bool Flag to tell if configuration needs to be saved.
*/
public $updated = false;
public $issues = array();
/**
* Constructor.
*/
public function __construct($filename)
{
$this->filename = realpath(dirname($filename)) . '/' . basename($filename);
$this->reload(false);
}
/**
* Force reload of the configuration from the disk.
*
* @param bool $force
* @return $this
*/
public function reload($force = true)
{
// Build file map.
$files = $this->build();
$key = md5(json_encode($files) . GRAV_VERSION);
if ($force || $key != $this->key) {
// First take non-blocking lock to the file.
File\Config::instance($this->filename)->lock(false);
// Reset configuration.
$this->items = array();
$this->files = array();
$this->init($files);
$this->key = $key;
}
return $this;
}
/**
* Save configuration into file.
*
* Note: Only saves the file if updated flag is set!
*
* @return $this
* @throws \RuntimeException
*/
public function save()
{
// If configuration was updated, store it as cached version.
try {
$file = File\Config::instance($this->filename);
// Only save configuration file if it wasn't locked. Also invalidate opcache after saving.
// This prevents us from saving the file multiple times in a row and gives faster recovery.
if ($file->locked() !== false) {
$file->save($this);
$file->unlock();
}
$this->updated = false;
} catch (\Exception $e) {
$this->issues[] = 'Writing configuration into cache failed.';
//throw new \RuntimeException('Writing configuration into cache failed.', 500, $e);
}
return $this;
}
/**
* Load configuration.
*
* @param Grav $grav
* @return \Grav\Common\Config
*/
public static function instance(Grav $grav)
{
$filename = $grav['config_path'];
// Load cached version if available..
if (file_exists($filename)) {
require_once $filename;
if (class_exists('\Grav\Config')) {
$instance = new \Grav\Config($filename);
}
}
// Or initialize new configuration object..
if (!isset($instance)) {
$instance = new static($filename);
}
// If configuration was updated, store it as cached version.
if ($instance->updated) {
$instance->save();
}
// If not set, add manually current base url.
if (empty($instance->items['system']['base_url_absolute'])) {
$instance->items['system']['base_url_absolute'] = $grav['uri']->rootUrl(true);
}
if (empty($instance->items['system']['base_url_relative'])) {
$instance->items['system']['base_url_relative'] = $grav['uri']->rootUrl(false);
}
return $instance;
}
/**
* Convert configuration into an array.
*
* @return array
*/
public function toArray()
{
return array('key' => $this->key, 'files' => $this->files, 'items' => $this->items);
}
/**
* Initialize object by loading all the configuration files.
*
* @param array $files
*/
protected function init(array $files)
{
$this->updated = true;
// Combine all configuration files into one larger lookup table (only keys matter).
$allFiles = $files['user'] + $files['plugins'] + $files['system'];
// Then sort the files to have all parent nodes first.
// This is to make sure that child nodes override parents content.
uksort(
$allFiles,
function($a, $b) {
$diff = substr_count($a, '/') - substr_count($b, '/');
return $diff ? $diff : strcmp($a, $b);
}
);
$systemBlueprints = new Blueprints(SYSTEM_DIR . 'blueprints');
$pluginBlueprints = new Blueprints(USER_DIR);
$items = array();
foreach ($allFiles as $name => $dummy) {
$lookup = array(
'system' => SYSTEM_DIR . 'config/' . $name . YAML_EXT,
'plugins' => USER_DIR . $name . '/' . basename($name) . YAML_EXT,
'user' => USER_DIR . 'config/' . $name . YAML_EXT,
);
if (strpos($name, 'plugins/') === 0) {
$blueprint = $pluginBlueprints->get("{$name}/blueprints");
} else {
$blueprint = $systemBlueprints->get($name);
}
$data = new Data(array(), $blueprint);
foreach ($lookup as $key => $path) {
if (is_file($path)) {
$data->merge(File\Yaml::instance($path)->content());
}
}
// $data->validate();
// $data->filter();
// Find the current sub-tree location.
$current = &$items;
$parts = explode('/', $name);
foreach ($parts as $part) {
if (!isset($current[$part])) {
$current[$part] = array();
}
$current = &$current[$part];
}
// Handle both updated and deleted configuration files.
$current = $data->toArray();
}
$this->items = $items;
$this->files = $files;
}
/**
* Build a list of configuration files with their timestamps. Used for loading settings and caching them.
*
* @return array
* @internal
*/
protected function build()
{
// Find all plugins with default configuration options.
$plugins = array();
$iterator = new \DirectoryIterator(PLUGINS_DIR);
/** @var \DirectoryIterator $plugin */
foreach ($iterator as $plugin) {
if ($iterator->isFile() || $iterator->isDot()) continue;
$name = $plugin->getBasename();
$file = $plugin->getPathname() . DS . $name . YAML_EXT;
if (!file_exists($file)) continue;
$modified = filemtime($file);
$plugins["plugins/{$name}"] = $modified;
}
// Find all system and user configuration files.
$options = array(
'compare' => 'Filename',
'pattern' => '|\.yaml$|',
'filters' => array('key' => '|\.yaml$|'),
'key' => 'SubPathname',
'value' => 'MTime'
);
$system = Folder::all(SYSTEM_DIR . 'config', $options);
$user = Folder::all(USER_DIR . 'config', $options);
return array('system' => $system, 'plugins' => $plugins, 'user' => $user);
}
}

View File

@@ -0,0 +1,208 @@
<?php
namespace Grav\Common\Config;
use Grav\Common\File\CompiledYamlFile;
use Grav\Common\Grav;
use Grav\Common\GravTrait;
use Grav\Common\Filesystem\Folder;
use RocketTheme\Toolbox\Blueprints\Blueprints as BaseBlueprints;
use RocketTheme\Toolbox\File\PhpFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
/**
* The Blueprints class contains configuration rules.
*
* @author RocketTheme
* @license MIT
*/
class Blueprints extends BaseBlueprints
{
protected $grav;
protected $files = [];
protected $blueprints;
public function __construct(array $serialized = null, Grav $grav = null)
{
parent::__construct($serialized);
$this->grav = $grav ?: Grav::instance();
}
public function init()
{
/** @var UniformResourceLocator $locator */
$locator = $this->grav['locator'];
$blueprints = $locator->findResources('blueprints://config');
$plugins = $locator->findResources('plugins://');
$blueprintFiles = $this->getBlueprintFiles($blueprints, $plugins);
$this->loadCompiledBlueprints($plugins + $blueprints, $blueprintFiles);
}
protected function loadCompiledBlueprints($blueprints, $blueprintFiles)
{
$checksum = md5(serialize($blueprints));
$filename = CACHE_DIR . 'compiled/blueprints/' . $checksum .'.php';
$checksum .= ':'.md5(serialize($blueprintFiles));
$class = get_class($this);
$file = PhpFile::instance($filename);
if ($file->exists()) {
$cache = $file->exists() ? $file->content() : null;
} else {
$cache = null;
}
// Load real file if cache isn't up to date (or is invalid).
if (
!is_array($cache)
|| empty($cache['checksum'])
|| empty($cache['$class'])
|| $cache['checksum'] != $checksum
|| $cache['@class'] != $class
) {
// Attempt to lock the file for writing.
$file->lock(false);
// Load blueprints.
$this->blueprints = new Blueprints();
foreach ($blueprintFiles as $key => $files) {
$this->loadBlueprints($key);
}
$cache = [
'@class' => $class,
'checksum' => $checksum,
'files' => $blueprintFiles,
'data' => $this->blueprints->toArray()
];
// If compiled file wasn't already locked by another process, save it.
if ($file->locked() !== false) {
$file->save($cache);
$file->unlock();
}
} else {
$this->blueprints = new Blueprints($cache['data']);
}
}
/**
* Load global blueprints.
*
* @param string $key
* @param array $files
*/
public function loadBlueprints($key, array $files = null)
{
if (is_null($files)) {
$files = $this->files[$key];
}
foreach ($files as $name => $item) {
$file = CompiledYamlFile::instance($item['file']);
$this->blueprints->embed($name, $file->content(), '/');
}
}
/**
* Get all blueprint files (including plugins).
*
* @param array $blueprints
* @param array $plugins
* @return array
*/
protected function getBlueprintFiles(array $blueprints, array $plugins)
{
$list = [];
foreach (array_reverse($plugins) as $folder) {
$list += $this->detectPlugins($folder, true);
}
foreach (array_reverse($blueprints) as $folder) {
$list += $this->detectConfig($folder, true);
}
return $list;
}
/**
* Detects all plugins with a configuration file and returns last modification time.
*
* @param string $lookup Location to look up from.
* @param bool $blueprints
* @return array
* @internal
*/
protected function detectPlugins($lookup = SYSTEM_DIR, $blueprints = false)
{
$find = $blueprints ? 'blueprints.yaml' : '.yaml';
$location = $blueprints ? 'blueprintFiles' : 'configFiles';
$path = trim(Folder::getRelativePath($lookup), '/');
if (isset($this->{$location}[$path])) {
return [$path => $this->{$location}[$path]];
}
$list = [];
if (is_dir($lookup)) {
$iterator = new \DirectoryIterator($lookup);
/** @var \DirectoryIterator $directory */
foreach ($iterator as $directory) {
if (!$directory->isDir() || $directory->isDot()) {
continue;
}
$name = $directory->getBasename();
$filename = "{$path}/{$name}/" . ($find && $find[0] != '.' ? $find : $name . $find);
if (is_file($filename)) {
$list["plugins/{$name}"] = ['file' => $filename, 'modified' => filemtime($filename)];
}
}
}
$this->{$location}[$path] = $list;
return [$path => $list];
}
/**
* Detects all plugins with a configuration file and returns last modification time.
*
* @param string $lookup Location to look up from.
* @param bool $blueprints
* @return array
* @internal
*/
protected function detectConfig($lookup = SYSTEM_DIR, $blueprints = false)
{
$location = $blueprints ? 'blueprintFiles' : 'configFiles';
$path = trim(Folder::getRelativePath($lookup), '/');
if (isset($this->{$location}[$path])) {
return [$path => $this->{$location}[$path]];
}
if (is_dir($lookup)) {
// Find all system and user configuration files.
$options = [
'compare' => 'Filename',
'pattern' => '|\.yaml$|',
'filters' => [
'key' => '|\.yaml$|',
'value' => function (\RecursiveDirectoryIterator $file) use ($path) {
return ['file' => "{$path}/{$file->getSubPathname()}", 'modified' => $file->getMTime()];
}],
'key' => 'SubPathname'
];
$list = Folder::all($lookup, $options);
} else {
$list = [];
}
$this->{$location}[$path] = $list;
return [$path => $list];
}
}

View File

@@ -0,0 +1,463 @@
<?php
namespace Grav\Common\Config;
use Grav\Common\File\CompiledYamlFile;
use Grav\Common\Grav;
use Grav\Common\GravTrait;
use Grav\Common\Uri;
use Grav\Common\Data\Data;
use Grav\Common\Filesystem\Folder;
use RocketTheme\Toolbox\Blueprints\Blueprints;
use RocketTheme\Toolbox\File\PhpFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
/**
* The Config class contains configuration information.
*
* @author RocketTheme
* @license MIT
*/
class Config extends Data
{
protected $grav;
protected $streams = [
'user' => [
'type' => 'ReadOnlyStream',
'prefixes' => [
'' => ['user'],
]
],
'blueprints' => [
'type' => 'ReadOnlyStream',
'prefixes' => [
'' => ['user://blueprints', 'system/blueprints'],
]
],
'config' => [
'type' => 'ReadOnlyStream',
'prefixes' => [
'' => ['user://config', 'system/config'],
]
],
'plugins' => [
'type' => 'ReadOnlyStream',
'prefixes' => [
'' => ['user://plugins'],
]
],
'plugin' => [
'type' => 'ReadOnlyStream',
'prefixes' => [
'' => ['user://plugins'],
]
],
'themes' => [
'type' => 'ReadOnlyStream',
'prefixes' => [
'' => ['user://themes'],
]
],
'cache' => [
'type' => 'Stream',
'prefixes' => [
'' => ['cache']
]
],
'log' => [
'type' => 'Stream',
'prefixes' => [
'' => ['logs']
]
]
];
protected $blueprintFiles = [];
protected $configFiles = [];
protected $checksum;
protected $timestamp;
protected $configLookup;
protected $blueprintLookup;
protected $pluginLookup;
public function __construct(array $items = array(), Grav $grav = null)
{
$this->grav = $grav ?: Grav::instance();
if (isset($items['@class'])) {
if ($items['@class'] != get_class($this)) {
throw new \InvalidArgumentException('Unrecognized config cache file!');
}
// Loading pre-compiled configuration.
$this->timestamp = (int) $items['timestamp'];
$this->checksum = (string) $items['checksum'];
$this->items = (array) $items['data'];
} else {
// Make sure that
if (!isset($items['streams']['schemes'])) {
$items['streams']['schemes'] = [];
}
$items['streams']['schemes'] += $this->streams;
parent::__construct($items);
}
$this->check();
}
public function key()
{
return $this->checksum;
}
public function reload()
{
$this->check();
$this->init();
return $this;
}
protected function check()
{
$streams = isset($this->items['streams']['schemes']) ? $this->items['streams']['schemes'] : null;
if (!is_array($streams)) {
throw new \InvalidArgumentException('Configuration is missing streams.schemes!');
}
$diff = array_keys(array_diff_key($this->streams, $streams));
if ($diff) {
throw new \InvalidArgumentException(
sprintf('Configuration is missing keys %s from streams.schemes!', implode(', ', $diff))
);
}
}
public function init()
{
/** @var UniformResourceLocator $locator */
$locator = $this->grav['locator'];
$this->configLookup = $locator->findResources('config://');
$this->blueprintLookup = $locator->findResources('blueprints://config');
$this->pluginLookup = $locator->findResources('plugins://');
$checksum = $this->checksum();
if ($checksum == $this->checksum) {
return;
}
$this->checksum = $checksum;
/** @var Uri $uri */
$uri = $this->grav['uri'];
// If not set, add manually current base url.
$this->def('system.base_url_absolute', $uri->rootUrl(true));
$this->def('system.base_url_relative', $uri->rootUrl(false));
$this->loadCompiledBlueprints($this->blueprintLookup, $this->pluginLookup, 'master');
$this->loadCompiledConfig($this->configLookup, $this->pluginLookup, 'master');
}
public function checksum()
{
$checkBlueprints = $this->get('system.cache.check.blueprints', false);
$checkConfig = $this->get('system.cache.check.config', true);
$checkSystem = $this->get('system.cache.check.system', true);
if (!$checkBlueprints && !$checkConfig && !$checkSystem) {
return false;
}
// Generate checksum according to the configuration settings.
if (!$checkConfig) {
// Just check changes in system.yaml files and ignore all the other files.
$cc = $checkSystem ? $this->detectFile($this->configLookup, 'system') : [];
} else {
// Check changes in all configuration files.
$cc = $this->getConfigFiles($this->configLookup, $this->pluginLookup);
}
$cb = $checkBlueprints ? $this->getBlueprintFiles($this->blueprintLookup, $this->pluginLookup) : [];
return md5(json_encode([$cc, $cb]));
}
protected function loadCompiledBlueprints($blueprints, $plugins, $filename = null)
{
$checksum = md5(json_encode($blueprints));
$filename = $filename
? CACHE_DIR . 'compiled/blueprints/' . $filename .'.php'
: CACHE_DIR . 'compiled/blueprints/' . $checksum .'.php';
$file = PhpFile::instance($filename);
if ($file->exists()) {
$cache = $file->exists() ? $file->content() : null;
} else {
$cache = null;
}
$blueprintFiles = $this->getBlueprintFiles($blueprints, $plugins);
$checksum .= ':'.md5(json_encode($blueprintFiles));
$class = get_class($this);
// Load real file if cache isn't up to date (or is invalid).
if (
!is_array($cache)
|| empty($cache['checksum'])
|| empty($cache['$class'])
|| $cache['checksum'] != $checksum
|| $cache['@class'] != $class
) {
// Attempt to lock the file for writing.
$file->lock(false);
// Load blueprints.
$this->blueprints = new Blueprints;
foreach ($blueprintFiles as $key => $files) {
$this->loadBlueprints($key);
}
$cache = [
'@class' => $class,
'checksum' => $checksum,
'files' => $blueprintFiles,
'data' => $this->blueprints->toArray()
];
// If compiled file wasn't already locked by another process, save it.
if ($file->locked() !== false) {
$file->save($cache);
$file->unlock();
}
} else {
$this->blueprints = new Blueprints($cache['data']);
}
}
protected function loadCompiledConfig($configs, $plugins, $filename = null)
{
$checksum = md5(json_encode($configs));
$filename = $filename
? CACHE_DIR . 'compiled/config/' . $filename .'.php'
: CACHE_DIR . 'compiled/config/' . $checksum .'.php';
$file = PhpFile::instance($filename);
if ($file->exists()) {
$cache = $file->exists() ? $file->content() : null;
} else {
$cache = null;
}
$configFiles = $this->getConfigFiles($configs, $plugins);
$checksum .= ':'.md5(json_encode($configFiles));
$class = get_class($this);
// Load real file if cache isn't up to date (or is invalid).
if (
!is_array($cache)
|| $cache['checksum'] != $checksum
|| $cache['@class'] != $class
) {
// Attempt to lock the file for writing.
$file->lock(false);
// Load configuration.
foreach ($configFiles as $key => $files) {
$this->loadConfig($key);
}
$cache = [
'@class' => $class,
'timestamp' => time(),
'checksum' => $this->checksum,
'data' => $this->toArray()
];
// If compiled file wasn't already locked by another process, save it.
if ($file->locked() !== false) {
$file->save($cache);
$file->unlock();
}
}
$this->items = $cache['data'];
}
/**
* Load global blueprints.
*
* @param string $key
* @param array $files
*/
public function loadBlueprints($key, array $files = null)
{
if (is_null($files)) {
$files = $this->blueprintFiles[$key];
}
foreach ($files as $name => $item) {
$file = CompiledYamlFile::instance($item['file']);
$this->blueprints->embed($name, $file->content(), '/');
}
}
/**
* Load global configuration.
*
* @param string $key
* @param array $files
*/
public function loadConfig($key, array $files = null)
{
if (is_null($files)) {
$files = $this->configFiles[$key];
}
foreach ($files as $name => $item) {
$file = CompiledYamlFile::instance($item['file']);
$this->join($name, $file->content(), '/');
}
}
/**
* Get all blueprint files (including plugins).
*
* @param array $blueprints
* @param array $plugins
* @return array
*/
protected function getBlueprintFiles(array $blueprints, array $plugins)
{
$list = [];
foreach (array_reverse($plugins) as $folder) {
$list += $this->detectPlugins($folder, true);
}
foreach (array_reverse($blueprints) as $folder) {
$list += $this->detectConfig($folder, true);
}
return $list;
}
/**
* Get all configuration files.
*
* @param array $configs
* @param array $plugins
* @return array
*/
protected function getConfigFiles(array $configs, array $plugins)
{
$list = [];
foreach (array_reverse($plugins) as $folder) {
$list += $this->detectPlugins($folder);
}
foreach (array_reverse($configs) as $folder) {
$list += $this->detectConfig($folder);
}
return $list;
}
/**
* Detects all plugins with a configuration file and returns last modification time.
*
* @param string $lookup Location to look up from.
* @param bool $blueprints
* @return array
* @internal
*/
protected function detectPlugins($lookup = SYSTEM_DIR, $blueprints = false)
{
$find = $blueprints ? 'blueprints.yaml' : '.yaml';
$location = $blueprints ? 'blueprintFiles' : 'configFiles';
$path = trim(Folder::getRelativePath($lookup), '/');
if (isset($this->{$location}[$path])) {
return [$path => $this->{$location}[$path]];
}
$list = [];
if (is_dir($lookup)) {
$iterator = new \DirectoryIterator($lookup);
/** @var \DirectoryIterator $directory */
foreach ($iterator as $directory) {
if (!$directory->isDir() || $directory->isDot()) {
continue;
}
$name = $directory->getBasename();
$filename = "{$path}/{$name}/" . ($find && $find[0] != '.' ? $find : $name . $find);
if (file_exists($filename)) {
$list["plugins/{$name}"] = ['file' => $filename, 'modified' => filemtime($filename)];
}
}
}
$this->{$location}[$path] = $list;
return [$path => $list];
}
/**
* Detects all plugins with a configuration file and returns last modification time.
*
* @param string $lookup Location to look up from.
* @param bool $blueprints
* @return array
* @internal
*/
protected function detectConfig($lookup = SYSTEM_DIR, $blueprints = false)
{
$location = $blueprints ? 'blueprintFiles' : 'configFiles';
$path = trim(Folder::getRelativePath($lookup), '/');
if (isset($this->{$location}[$path])) {
return [$path => $this->{$location}[$path]];
}
if (is_dir($lookup)) {
// Find all system and user configuration files.
$options = [
'compare' => 'Filename',
'pattern' => '|\.yaml$|',
'filters' => [
'key' => '|\.yaml$|',
'value' => function (\RecursiveDirectoryIterator $file) use ($path) {
return ['file' => "{$path}/{$file->getSubPathname()}", 'modified' => $file->getMTime()];
}],
'key' => 'SubPathname'
];
$list = Folder::all($lookup, $options);
} else {
$list = [];
}
$this->{$location}[$path] = $list;
return [$path => $list];
}
/**
* Detects all instances of the file and returns last modification time.
*
* @param array $lookups Locations to look up from.
* @param string $name
* @return array
* @internal
*/
protected function detectFile(array $lookups, $name)
{
$list = [];
$filename = "{$name}.yaml";
foreach ($lookups as $lookup) {
$path = trim(Folder::getRelativePath($lookup), '/');
if (is_file("{$lookup}/{$filename}")) {
$modified = filemtime("{$lookup}/{$filename}");
} else {
$modified = 0;
}
$list[$path] = [$name => ['file' => "{$path}/{$filename}", 'modified' => $modified]];
}
return $list;
}
}

View File

@@ -1,7 +1,7 @@
<?php
namespace Grav\Common\Data;
use \Symfony\Component\Yaml\Yaml;
use RocketTheme\Toolbox\ArrayTraits\Export;
/**
* Blueprint handles the inside logic of blueprints.
@@ -11,26 +11,41 @@ use \Symfony\Component\Yaml\Yaml;
*/
class Blueprint
{
use Export;
public $name;
public $initialized = false;
protected $blueprints;
protected $items;
protected $context;
protected $fields;
protected $rules = array();
protected $nested = array();
protected $filter = ['validation' => 1];
/**
* @param string $name
* @param array $data
* @param Blueprints $context
*/
public function __construct($name, array $data, Blueprints $context)
public function __construct($name, array $data = array(), Blueprints $context = null)
{
$this->name = $name;
$this->blueprints = $data;
$this->items = $data;
$this->context = $context;
}
/**
* Set filter for inherited properties.
*
* @param array $filter List of field names to be inherited.
*/
public function setFilter(array $filter)
{
$this->filter = array_flip($filter);
}
/**
* Get value by using dot notation for nested arrays/objects.
*
@@ -45,7 +60,7 @@ class Blueprint
public function get($name, $default = null, $separator = '.')
{
$path = explode($separator, $name);
$current = $this->blueprints;
$current = $this->items;
foreach ($path as $field) {
if (is_object($current) && isset($current->{$field})) {
$current = $current->{$field};
@@ -71,7 +86,7 @@ class Blueprint
public function set($name, $value, $separator = '.')
{
$path = explode($separator, $name);
$current = &$this->blueprints;
$current = &$this->items;
foreach ($path as $field) {
if (is_object($current)) {
// Handle objects.
@@ -101,8 +116,8 @@ class Blueprint
public function fields()
{
if (!isset($this->fields)) {
$this->fields = isset($this->blueprints['form']['fields']) ? $this->blueprints['form']['fields'] : array();
$this->getFields($this->fields);
$this->fields = [];
$this->embed('', $this->items);
}
return $this->fields;
@@ -131,9 +146,10 @@ class Blueprint
*
* @param array $data1
* @param array $data2
* @param string $name
* @return array
*/
public function mergeData(array $data1, array $data2)
public function mergeData(array $data1, array $data2, $name = null)
{
// Initialize data
$this->fields();
@@ -167,6 +183,71 @@ class Blueprint
return $this->extraArray($data, $this->nested, $prefix);
}
/**
* Extend blueprint with another blueprint.
*
* @param Blueprint $extends
* @param bool $append
*/
public function extend(Blueprint $extends, $append = false)
{
$blueprints = $append ? $this->items : $extends->toArray();
$appended = $append ? $extends->toArray() : $this->items;
$bref_stack = array(&$blueprints);
$head_stack = array($appended);
do {
end($bref_stack);
$bref = &$bref_stack[key($bref_stack)];
$head = array_pop($head_stack);
unset($bref_stack[key($bref_stack)]);
foreach (array_keys($head) as $key) {
if (isset($key, $bref[$key]) && is_array($bref[$key]) && is_array($head[$key])) {
$bref_stack[] = &$bref[$key];
$head_stack[] = $head[$key];
} else {
$bref = array_merge($bref, array($key => $head[$key]));
}
}
} while(count($head_stack));
$this->items = $blueprints;
}
/**
* Convert object into an array.
*
* @return array
*/
public function getState()
{
return ['name' => $this->name, 'items' => $this->items, 'rules' => $this->rules, 'nested' => $this->nested];
}
/**
* Embed an array to the blueprint.
*
* @param $name
* @param array $value
* @param string $separator
*/
public function embed($name, array $value, $separator = '.')
{
if (!isset($value['form']['fields']) || !is_array($value['form']['fields'])) {
return;
}
// Initialize data
$this->fields();
$prefix = $name ? strtr($name, $separator, '.') . '.' : '';
$params = array_intersect_key($this->filter, $value);
$this->parseFormFields($value['form']['fields'], $params, $prefix, $this->fields);
}
/**
* @param array $data
* @param array $rules
@@ -187,7 +268,7 @@ class Blueprint
} elseif (is_array($field) && is_array($val)) {
// Array has been defined in blueprints.
$this->validateArray($field, $val);
} elseif (isset($this->blueprints['validation']) && $this->blueprints['validation'] == 'strict') {
} elseif (isset($this->items['form']['validation']) && $this->items['form']['validation'] == 'strict') {
// Undefined/extra item.
throw new \RuntimeException(sprintf('%s is not defined in blueprints', $key));
}
@@ -213,7 +294,7 @@ class Blueprint
} elseif (is_array($field) && is_array($val)) {
// Array has been defined in blueprints.
$field = $this->filterArray($field, $val);
} elseif (isset($this->blueprints['validation']) && $this->blueprints['validation'] == 'strict') {
} elseif (isset($this->items['form']['validation']) && $this->items['form']['validation'] == 'strict') {
$field = null;
}
@@ -281,26 +362,32 @@ class Blueprint
* Gets all field definitions from the blueprints.
*
* @param array $fields
* @param array $params
* @param string $prefix
* @param array $current
* @internal
*/
protected function getFields(array &$fields)
protected function parseFormFields(array &$fields, $params, $prefix, array &$current)
{
// Go though all the fields in current level.
foreach ($fields as $key => &$field) {
$current[$key] = &$field;
// Set name from the array key.
$field['name'] = $key;
$field['name'] = $prefix . $key;
$field += $params;
if (isset($field['fields'])) {
// Recursively get all the nested fields.
$this->getFields($field['fields']);
$newParams = array_intersect_key($this->filter, $field);
$this->parseFormFields($field['fields'], $newParams, $prefix, $current[$key]['fields']);
} else {
// Add rule.
$this->rules[$key] = &$field;
$this->addProperty($key);
$this->rules[$prefix . $key] = &$field;
$this->addProperty($prefix . $key);
foreach ($field as $name => $value) {
// Support nested blueprints.
if ($name == '@import') {
if ($this->context && $name == '@import') {
$values = (array) $value;
if (!isset($field['fields'])) {
$field['fields'] = array();
@@ -379,8 +466,8 @@ class Blueprint
*/
protected function getRule($rule)
{
if (isset($this->blueprints['rules'][$rule]) && is_array($this->blueprints['rules'][$rule])) {
return $this->blueprints['rules'][$rule];
if (isset($this->items['rules'][$rule]) && is_array($this->items['rules'][$rule])) {
return $this->items['rules'][$rule];
}
return array();
}
@@ -404,69 +491,4 @@ class Blueprint
}
}
}
/**
* Convert blueprints into an array.
*
* @return array
*/
public function toArray()
{
return $this->blueprints;
}
/**
* Convert blueprints into YAML string.
*
* @return string
*/
public function toYaml()
{
return Yaml::dump($this->blueprints);
}
/**
* Convert blueprints into JSON string.
*
* @return string
*/
public function toJson()
{
return json_encode($this->blueprints);
}
/**
* Extend blueprint with another blueprint.
*
* @param Blueprint $extends
* @param bool $append
*/
public function extend(Blueprint $extends, $append = false)
{
$blueprints = $append ? $this->blueprints : $extends->toArray();
$appended = $append ? $extends->toArray() : $this->blueprints;
$bref_stack = array(&$blueprints);
$head_stack = array($appended);
do {
end($bref_stack);
$bref = &$bref_stack[key($bref_stack)];
$head = array_pop($head_stack);
unset($bref_stack[key($bref_stack)]);
foreach (array_keys($head) as $key) {
if (isset($key, $bref[$key]) && is_array($bref[$key]) && is_array($head[$key])) {
$bref_stack[] = &$bref[$key];
$head_stack[] = $head[$key];
} else {
$bref = array_merge($bref, array($key => $head[$key]));
}
}
} while(count($head_stack));
$this->blueprints = $blueprints;
}
}

View File

@@ -1,7 +1,7 @@
<?php
namespace Grav\Common\Data;
use \Symfony\Component\Yaml\Yaml;
use Grav\Common\File\CompiledYamlFile;
/**
* Blueprints class keeps track on blueprint instances.
@@ -16,11 +16,15 @@ class Blueprints
protected $instances = array();
/**
* @param string $search Search path.
* @param string|array $search Search path.
*/
public function __construct($search)
{
$this->search = rtrim($search, '\\/') . '/';
if (!is_string($search)) {
$this->search = $search;
} else {
$this->search = rtrim($search, '\\/') . '/';
}
}
/**
@@ -33,11 +37,18 @@ class Blueprints
public function get($type)
{
if (!isset($this->instances[$type])) {
if (is_file($this->search . $type . YAML_EXT)) {
$blueprints = (array) Yaml::parse($this->search . $type . YAML_EXT);
if (is_string($this->search)) {
$filename = $this->search . $type . YAML_EXT;
} else {
$filename = isset($this->search[$type]) ? $this->search[$type] : '';
}
if ($filename && is_file($filename)) {
$file = CompiledYamlFile::instance($filename);
$blueprints = $file->content();
} else {
// throw new \RuntimeException("Blueprints for '{$type}' cannot be found! {$this->search}{$type}");
$blueprints = array();
$blueprints = [];
}
$blueprint = new Blueprint($type, $blueprints, $this);

View File

@@ -1,9 +1,11 @@
<?php
namespace Grav\Common\Data;
use Grav\Common\Filesystem\FileInterface;
use \Grav\Common\Getters;
use \Grav\Common\Filesystem\File;
use RocketTheme\Toolbox\ArrayTraits\ArrayAccessWithGetters;
use RocketTheme\Toolbox\ArrayTraits\Countable;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\File\File;
use RocketTheme\Toolbox\File\FileInterface;
/**
* Recursive data object
@@ -11,8 +13,10 @@ use \Grav\Common\Filesystem\File;
* @author RocketTheme
* @license MIT
*/
class Data extends Getters implements DataInterface
class Data implements DataInterface
{
use ArrayAccessWithGetters, Countable, Export;
protected $gettersVariable = 'items';
protected $items;
@@ -22,7 +26,7 @@ class Data extends Getters implements DataInterface
protected $blueprints;
/**
* @var File\General
* @var File
*/
protected $storage;
@@ -121,17 +125,64 @@ class Data extends Getters implements DataInterface
* @param string $name Dot separated path to the requested value.
* @param mixed $default Default value (or null).
* @param string $separator Separator, defaults to '.'
* @return mixed Value.
*/
public function def($name, $default = null, $separator = '.')
{
$this->set($name, $this->get($name, $default, $separator), $separator);
}
/**
* Join two values together by using blueprints if available.
*
* @param string $name Dot separated path to the requested value.
* @param mixed $value Value to be joined.
* @param string $separator Separator, defaults to '.'
*/
public function join($name, $value, $separator = '.')
{
$old = $this->get($name, null, $separator);
if ($old === null) {
// Variable does not exist yet: just use the incoming value.
} elseif ($this->blueprints) {
// Blueprints: join values by using blueprints.
$value = $this->blueprints->mergeData($old, $value, $name, $separator);
} else {
// No blueprints: replace existing top level variables with the new ones.
$value = array_merge($old, $value);
}
$this->set($name, $value, $separator);
}
/**
* Join two values together by using blueprints if available.
*
* @param string $name Dot separated path to the requested value.
* @param mixed $value Value to be joined.
* @param string $separator Separator, defaults to '.'
*/
public function joinDefaults($name, $value, $separator = '.')
{
$old = $this->get($name, null, $separator);
if ($old === null) {
// Variable does not exist yet: just use the incoming value.
} elseif ($this->blueprints) {
// Blueprints: join values by using blueprints.
$value = $this->blueprints->mergeData($value, $old, $name, $separator);
} else {
// No blueprints: replace existing top level variables with the new ones.
$value = array_merge($value, $old);
}
$this->set($name, $value, $separator);
}
/**
* Merge two sets of data together.
*
* @param array $data
* @return void
*/
public function merge(array $data)
{
@@ -142,6 +193,21 @@ class Data extends Getters implements DataInterface
}
}
/**
* Add default data to the set.
*
* @param array $data
* @return void
*/
public function setDefaults(array $data)
{
if ($this->blueprints) {
$this->items = $this->blueprints->mergeData($data, $this->items);
} else {
$this->items = array_merge($data, $this->items);
}
}
/**
* Return blueprints.
*

View File

@@ -1,8 +1,7 @@
<?php
namespace Grav\Common\Data;
use Grav\Common\Filesystem\FileInterface;
use \Grav\Common\Filesystem\File;
use RocketTheme\Toolbox\File\FileInterface;
/**
* Data interface

View File

@@ -114,12 +114,6 @@ class Validation
protected static function filterText($value, array $params, array $field)
{
if (!is_string($value)) {
var_dump($value);
var_dump($params);
var_dump($field);
die();
}
return (string) $value;
}

View File

@@ -0,0 +1,65 @@
<?php
namespace Grav\Common\File;
use RocketTheme\Toolbox\File\PhpFile;
/**
* Class CompiledFile
* @package Grav\Common\File
*
* @property string $filename
* @property string $extension
* @property string $raw
* @property array|string $content
*/
trait CompiledFile
{
/**
* Get/set parsed file contents.
*
* @param mixed $var
* @return string
*/
public function content($var = null)
{
// If nothing has been loaded, attempt to get pre-compiled version of the file first.
if ($var === null && $this->raw === null && $this->content === null) {
$key = md5($this->filename);
$file = PhpFile::instance(CACHE_DIR . "/compiled/files/{$key}{$this->extension}.php");
$modified = $this->modified();
$class = get_class($this);
$cache = $file->exists() ? $file->content() : null;
// Load real file if cache isn't up to date (or is invalid).
if (
!isset($cache['@class'])
|| $cache['@class'] != $class
|| $cache['modified'] != $modified
|| $cache['filename'] != $this->filename
) {
// Attempt to lock the file for writing.
$file->lock(false);
// Decode RAW file into compiled array.
$data = $this->decode($this->raw());
$cache = [
'@class' => $class,
'filename' => $this->filename,
'modified' => $modified,
'data' => $data
];
// If compiled file wasn't already locked by another process, save it.
if ($file->locked() !== false) {
$file->save($cache);
$file->unlock();
}
}
$this->content = $cache['data'];
}
return parent::content($var);
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Grav\Common\File;
use RocketTheme\Toolbox\File\MarkdownFile;
class CompiledMarkdownFile extends MarkdownFile
{
use CompiledFile;
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Grav\Common\File;
use RocketTheme\Toolbox\File\YamlFile;
class CompiledYamlFile extends YamlFile
{
use CompiledFile;
}

View File

@@ -1,129 +0,0 @@
<?php
namespace Grav\Common\Filesystem\File;
/**
* File handling class.
*
* @author RocketTheme
* @license MIT
*/
class Config extends General
{
/**
* @var string
*/
protected $extension = '.php';
/**
* @var array|General[]
*/
static protected $instances = array();
/**
* Saves configuration file and invalidates opcache.
*
* @param mixed $data Optional data to be saved, usually array.
* @throws \RuntimeException
*/
public function save($data = null)
{
parent::save($data);
// Invalidate configuration file from the opcache.
if (function_exists('opcache_invalidate')) {
// PHP 5.5.5+
@opcache_invalidate($this->filename);
} elseif (function_exists('apc_invalidate')) {
// APC
@apc_invalidate($this->filename);
}
}
/**
* Check contents and make sure it is in correct format.
*
* @param \Grav\Common\Config $var
* @return \Grav\Common\Config
* @throws \RuntimeException
*/
protected function check($var)
{
if (!($var instanceof \Grav\Common\Config)) {
throw new \RuntimeException('Provided data is not configuration');
}
return $var;
}
/**
* Encode configuration object into RAW string (PHP class).
*
* @param \Grav\Common\Config $var
* @return string
* @throws \RuntimeException
*/
protected function encode($var)
{
if (!($var instanceof \Grav\Common\Config)) {
throw new \RuntimeException('Provided data is not configuration');
}
// Build the object variables string
$vars = array();
$options = $var->toArray();
foreach ($options as $k => $v) {
if (is_int($v)) {
$vars[] = "\tpublic $" . $k . " = " . $v . ";";
} elseif (is_bool($v)) {
$vars[] = "\tpublic $" . $k . " = " . ($v ? 'true' : 'false') . ";";
} elseif (is_scalar($v)) {
$vars[] = "\tpublic $" . $k . " = '" . addcslashes($v, '\\\'') . "';";
} elseif (is_array($v) || is_object($v)) {
$vars[] = "\tpublic $" . $k . " = " . $this->encodeArray((array) $v) . ";";
}
}
$vars = implode("\n", $vars);
return "<?php\nnamespace Grav;\n\nclass Config extends \\Grav\\Common\\Config {\n {$vars}\n}";
}
/**
* Method to get an array as an exported string.
*
* @param array $a The array to get as a string.
* @param int $level Used internally to indent rows.
*
* @return array
*/
protected function encodeArray($a, $level = 1)
{
$r = array();
foreach ($a as $k => $v) {
if (is_array($v) || is_object($v)) {
$r[] = '"' . $k . '" => ' . $this->encodeArray((array) $v, $level+1);
} elseif (is_int($v)) {
$r[] = "'" . $k . "' => " . $v;
} elseif (is_bool($v)) {
$r[] = "'" . $k . "' => " . ($v ? 'true' : 'false');
} else {
$r[] .= "'" . $k . "' => " . "'" . addslashes($v) . "'";
}
}
$tabs = str_repeat("\t", $level);
return "array(\n\t{$tabs}" . implode(",\n\t{$tabs}", $r) . "\n{$tabs})";
}
/**
* Decode RAW string into contents.
*
* @param string $var
* @return \Grav\Common\Config
*/
protected function decode($var)
{
// TODO: improve this one later, works only for single file...
return class_exists('\Grav\Config') ? new \Grav\Config($this->filename) : new Config($this->filename);
}
}

View File

@@ -1,352 +0,0 @@
<?php
namespace Grav\Common\Filesystem\File;
use Grav\Common\Filesystem\FileInterface;
/**
* General file handling class.
*
* @author RocketTheme
* @license MIT
*/
class General implements FileInterface
{
/**
* @var string
*/
protected $filename;
/**
* @var resource
*/
protected $handle;
/**
* @var bool|null
*/
protected $locked;
/**
* @var string
*/
protected $extension;
/**
* @var string Raw file contents.
*/
protected $raw;
/**
* @var array Parsed file contents.
*/
protected $content;
/**
* @var array|General[]
*/
static protected $instances = array();
/**
* Get file instance.
*
* @param string $filename
* @return FileInterface
*/
public static function instance($filename)
{
if (!isset(static::$instances[$filename])) {
static::$instances[$filename] = new static;
static::$instances[$filename]->init($filename);
}
return static::$instances[$filename];
}
/**
* Prevent constructor from being used.
*
* @internal
*/
protected function __construct()
{
}
/**
* Prevent cloning.
*
* @internal
*/
protected function __clone()
{
//Me not like clones! Me smash clones!
}
/**
* Set filename.
*
* @param $filename
*/
protected function init($filename)
{
$this->filename = $filename;
}
/**
* Get/set the file location.
*
* @param string $var
* @return string
*/
public function filename($var = null)
{
if ($var !== null) {
$this->filename = $var;
}
return $this->filename;
}
/**
* Return basename of the file.
*
* @return string
*/
public function basename()
{
return basename($this->filename, $this->extension);
}
/**
* Check if file exits.
*
* @return bool
*/
public function exists()
{
return is_file($this->filename);
}
/**
* Return file modification time.
*
* @return int|bool Timestamp or false if file doesn't exist.
*/
public function modified()
{
return is_file($this->filename) ? filemtime($this->filename) : false;
}
/**
* Lock file for writing. You need to manually unlock().
*
* @param bool $block For non-blocking lock, set the parameter to false.
* @return bool
*/
public function lock($block = true)
{
if (!$this->handle) {
if (!$this->mkdir(dirname($this->filename))) {
throw new \RuntimeException('Creating directory failed for ' . $this->filename);
}
$this->handle = fopen($this->filename, 'wb+');
}
$lock = $block ? LOCK_EX : LOCK_EX | LOCK_NB;
return $this->locked = $this->handle ? flock($this->handle, $lock) : false;
}
/**
* Returns true if file has been locked for writing.
*
* @return bool|null True = locked, false = failed, null = not locked.
*/
public function locked()
{
return $this->locked;
}
/**
* Unlock file.
*
* @return bool
*/
public function unlock()
{
if (!$this->handle) {
return;
}
if ($this->locked) {
flock($this->handle, LOCK_UN);
$this->locked = null;
}
fclose($this->handle);
}
/**
* Check if file can be written.
*
* @return bool
*/
public function writable()
{
return is_writable($this->filename) || $this->writableDir(dirname($this->filename));
}
/**
* (Re)Load a file and return RAW file contents.
*
* @return string
*/
public function load()
{
$this->raw = $this->exists() ? (string) file_get_contents($this->filename) : '';
$this->content = null;
return $this->raw;
}
/**
* Get/set raw file contents.
*
* @param string $var
* @return string
*/
public function raw($var = null)
{
if ($var !== null) {
$this->raw = (string) $var;
$this->content = null;
}
if (!is_string($this->raw)) {
$this->raw = $this->load();
}
return $this->raw;
}
/**
* Get/set parsed file contents.
*
* @param mixed $var
* @return string
*/
public function content($var = null)
{
if ($var !== null) {
$this->content = $this->check($var);
// Update RAW, too.
$this->raw = $this->encode($this->content);
} elseif ($this->content === null) {
// Decode RAW file.
$this->content = $this->decode($this->raw());
}
return $this->content;
}
/**
* Save file.
*
* @param mixed $data Optional data to be saved, usually array.
* @throws \RuntimeException
*/
public function save($data = null)
{
if ($data !== null) {
$this->content($data);
}
if (!$this->locked) {
// Obtain blocking lock or fail.
if (!$this->lock()) {
throw new \RuntimeException('Obtaining write lock failed on file: ' . $this->filename);
}
$lock = true;
}
if (@fwrite($this->handle, $this->raw()) === false) {
$this->unlock();
throw new \RuntimeException('Saving file failed: ' . $this->filename);
}
if (isset($lock)) {
$this->unlock();
}
// Touch the directory as well, thus marking it modified.
@touch(dirname($this->filename));
}
/**
* Delete file from filesystem.
*
* @return bool
*/
public function delete()
{
return unlink($this->filename);
}
/**
* Check contents and make sure it is in correct format.
*
* Override in derived class.
*
* @param string $var
* @return string
*/
protected function check($var)
{
return (string) $var;
}
/**
* Encode contents into RAW string.
*
* Override in derived class.
*
* @param string $var
* @return string
*/
protected function encode($var)
{
return (string) $var;
}
/**
* Decode RAW string into contents.
*
* Override in derived class.
*
* @param string $var
* @return string mixed
*/
protected function decode($var)
{
return (string) $var;
}
/**
* @param string $dir
* @return bool
* @internal
*/
protected function mkdir($dir)
{
return is_dir($dir) || mkdir($dir, 0777, true);
}
/**
* @param string $dir
* @return bool
* @internal
*/
protected function writableDir($dir)
{
if ($dir && !file_exists($dir)) {
return $this->writableDir(dirname($dir));
}
return $dir && is_dir($dir) && is_writable($dir);
}
}

View File

@@ -1,54 +0,0 @@
<?php
namespace Grav\Common\Filesystem\File;
/**
* File handling class for JSON.
*
* @author RocketTheme
* @license MIT
*/
class Json extends General
{
/**
* @var string
*/
protected $extension = '.json';
/**
* @var array|General[]
*/
static protected $instances = array();
/**
* Check contents and make sure it is in correct format.
*
* @param array $var
* @return array
*/
protected function check($var)
{
return (array) $var;
}
/**
* Encode contents into RAW string.
*
* @param string $var
* @return string
*/
protected function encode($var)
{
return (string) json_encode($var);
}
/**
* Decode RAW string into contents.
*
* @param string $var
* @return array mixed
*/
protected function decode($var)
{
return (array) json_decode($var);
}
}

View File

@@ -1,69 +0,0 @@
<?php
namespace Grav\Common\Filesystem\File;
/**
* File handling class for Log files.
*
* @author RocketTheme
* @license MIT
*/
class Log extends General
{
/**
* @var array|General[]
*/
static protected $instances = array();
/**
* Constructor.
*/
protected function __construct()
{
parent::__construct();
$this->extension = '.log';
}
/**
* Check contents and make sure it is in correct format.
*
* @param array $var
* @return array
*/
protected function check($var)
{
return (array) $var;
}
/**
* Encode contents into RAW string (unsupported).
*
* @param string $var
* @throws \Exception
*/
protected function encode($var)
{
throw new \Exception('Saving log file is forbidden.');
}
/**
* Decode RAW string into contents.
*
* @param string $var
* @return array mixed
*/
protected function decode($var)
{
$lines = (array) preg_split('#(\r\n|\n|\r)#', $var);
$results = array();
foreach ($lines as $line) {
preg_match('#^\[(.*)\] (.*) @ (.*) @@ (.*)$#', $line, $matches);
if ($matches) {
$results[] = ['date' => $matches[1], 'message' => $matches[2], 'url' => $matches[3], 'file' => $matches[4]];
}
}
return $results;
}
}

View File

@@ -1,120 +0,0 @@
<?php
namespace Grav\Common\Filesystem\File;
use \Symfony\Component\Yaml\Yaml as YamlParser;
/**
* File handling class.
*
* @author RocketTheme
* @license MIT
*/
class Markdown extends General
{
/**
* @var string
*/
protected $extension = '.md';
/**
* @var array|General[]
*/
static protected $instances = array();
/**
* Get/set file header.
*
* @param array $var
*
* @return array
*/
public function header(array $var = null)
{
$content = $this->content();
if ($var !== null) {
$content['header'] = $var;
$this->content($content);
}
return $content['header'];
}
/**
* Get/set markdown content.
*
* @param string $var
*
* @return string
*/
public function markdown($var = null)
{
$content = $this->content();
if ($var !== null) {
$content['markdown'] = (string) $var;
$this->content($content);
}
return $content['markdown'];
}
/**
* Check contents and make sure it is in correct format.
*
* @param array $var
* @return array
*/
protected function check($var)
{
$var = (array) $var;
if (!isset($var['header']) || !is_array($var['header'])) {
$var['header'] = array();
}
if (!isset($var['markdown']) || !is_string($var['markdown'])) {
$var['markdown'] = '';
}
return $var;
}
/**
* Encode contents into RAW string.
*
* @param string $var
* @return string
*/
protected function encode($var)
{
// Create Markdown file with YAML header.
$o = (!empty($var['header']) ? "---\n" . trim(YamlParser::dump($var['header'])) . "\n---\n\n" : '') . $var['markdown'];
// Normalize line endings to Unix style.
$o = preg_replace("/(\r\n|\r)/", "\n", $o);
return $o;
}
/**
* Decode RAW string into contents.
*
* @param string $var
* @return array mixed
*/
protected function decode($var)
{
$content = array();
// Normalize line endings to Unix style.
$var = preg_replace("/(\r\n|\r)/", "\n", $var);
// Parse header.
preg_match("/---\n(.+?)\n---(\n\n|$)/uism", $var, $m);
$content['header'] = isset($m[1]) ? YamlParser::parse(preg_replace("/\n\t/", "\n ", $m[1])) : array();
// Strip header to get content.
$content['markdown'] = trim(preg_replace("/---\n(.+?)\n---(\n\n|$)/uism", '', $var));
return $content;
}
}

View File

@@ -1,61 +0,0 @@
<?php
namespace Grav\Common\Filesystem\File;
use \Symfony\Component\Yaml\Yaml as YamlParser;
/**
* File handling class for YAML.
*
* @author RocketTheme
* @license MIT
*/
class Yaml extends General
{
/**
* @var array|General[]
*/
static protected $instances = array();
/**
* Constructor.
*/
protected function __construct()
{
parent::__construct();
$this->extension = YAML_EXT;
}
/**
* Check contents and make sure it is in correct format.
*
* @param array $var
* @return array
*/
protected function check($var)
{
return (array) $var;
}
/**
* Encode contents into RAW string.
*
* @param string $var
* @return string
*/
protected function encode($var)
{
return (string) YamlParser::dump($var);
}
/**
* Decode RAW string into contents.
*
* @param string $var
* @return array mixed
*/
protected function decode($var)
{
return (array) YamlParser::parse($var);
}
}

View File

@@ -1,100 +0,0 @@
<?php
namespace Grav\Common\Filesystem;
/**
* File interface.
*
* @author RocketTheme
* @license MIT
*/
interface FileInterface
{
/**
* Get file instance.
*
* @param string $filename
* @return mixed
*/
public static function instance($filename);
/**
* Check if file exits.
*
* @return bool
*/
public function exists();
/**
* Return file modification time.
*
* @return int Timestamp
*/
public function modified();
/**
* Lock file for writing. Lock gets automatically released during the save().
*
* @param bool $block For non-blocking lock, set the parameter to false.
* @return bool
*/
public function lock($block = true);
/**
* Returns true if file has been locked for writing.
*
* @return bool|null True = locked, false = failed, null = not locked.
*/
public function locked();
/**
* Unlock file.
*
* @return bool
*/
public function unlock();
/**
* Check if file can be written.
*
* @return bool
*/
public function writable();
/**
* (Re)Load a file and return its contents.
*
* @return string
*/
public function load();
/**
* Get/set raw file contents.
*
* @param string $var
* @return string
*/
public function raw($var = null);
/**
* Get/set parsed file contents.
*
* @param string $var
* @return string
*/
public function content($var = null);
/**
* Save file.
*
* @param string $data Optional data to be saved.
* @throws \RuntimeException
*/
public function save($data = null);
/**
* Delete file from filesystem.
*
* @return bool
*/
public function delete();
}

View File

@@ -12,7 +12,7 @@ abstract class Folder
/**
* Recursively find the last modified time under given path.
*
* @param string $path
* @param string $path
* @return int
*/
public static function lastModifiedFolder($path)
@@ -29,13 +29,25 @@ abstract class Folder
$last_modified = $dir_modified;
}
}
return $last_modified;
}
public static function getRelativePath($to, $from = ROOT_DIR)
{
$from = preg_replace('![\\|/]+!', '/', $from);
$to = preg_replace('![\\|/]+!', '/', $to);
if (strpos($to, $from) === 0) {
$to = substr($to, strlen($from));
}
return $to;
}
/**
* Recursively find the last modified time under given path by file.
*
* @param string $path
* @param string $path
* @return int
*/
public static function lastModifiedFile($path)
@@ -48,43 +60,43 @@ abstract class Folder
/** @var \RecursiveDirectoryIterator $file */
foreach ($itr as $file) {
if (!$file->isDir()) {
$file_modified = $file->getMTime();
if ($file_modified > $last_modified) {
$last_modified = $file_modified;
}
$file_modified = $file->getMTime();
if ($file_modified > $last_modified) {
$last_modified = $file_modified;
}
}
return $last_modified;
}
/**
* Return recursive list of all files and directories under given path.
*
* @param string $path
* @param array $params
* @param string $path
* @param array $params
* @return array
* @throws \RuntimeException
*/
public static function all($path, array $params = array())
{
$path = realpath($path);
if ($path === false) {
throw new \RuntimeException("Path to {$path} doesn't exist.");
}
$compare = $params['compare'] ? 'get' . $params['compare'] : null;
$pattern = $params['pattern'] ? $params['pattern'] : null;
$filters = $params['filters'] ? $params['filters'] : null;
$key = $params['key'] ? 'get' . $params['key'] : null;
$value = $params['value'] ? 'get' . $params['value'] : 'SubPathname';
$compare = isset($params['compare']) ? 'get' . $params['compare'] : null;
$pattern = isset($params['pattern']) ? $params['pattern'] : null;
$filters = isset($params['filters']) ? $params['filters'] : null;
$recursive = isset($params['recursive']) ? $params['recursive'] : true;
$key = isset($params['key']) ? 'get' . $params['key'] : null;
$value = isset($params['value']) ? 'get' . $params['value'] : ($recursive ? 'getSubPathname' : 'getFilename');
$directory = new \RecursiveDirectoryIterator($path,
\RecursiveDirectoryIterator::SKIP_DOTS + \FilesystemIterator::UNIX_PATHS + \FilesystemIterator::CURRENT_AS_SELF);
$iterator = new \RecursiveIteratorIterator($directory, \RecursiveIteratorIterator::SELF_FIRST);
if ($recursive) {
$directory = new \RecursiveDirectoryIterator($path,
\RecursiveDirectoryIterator::SKIP_DOTS + \FilesystemIterator::UNIX_PATHS + \FilesystemIterator::CURRENT_AS_SELF);
$iterator = new \RecursiveIteratorIterator($directory, \RecursiveIteratorIterator::SELF_FIRST);
} else {
$iterator = new \FilesystemIterator($path);
}
$results = array();
@@ -100,20 +112,30 @@ abstract class Folder
$fileKey = preg_replace($filters['key'], '', $fileKey);
}
if (isset($filters['value'])) {
$filePath = preg_replace($filters['value'], '', $filePath);
$filter = $filters['value'];
if (is_callable($filter)) {
$filePath = call_user_func($filter, $file);
} else {
$filePath = preg_replace($filter, '', $filePath);
}
}
}
if ($fileKey !== null) {
$results[$fileKey] = $filePath;
} else {
$results[] = $filePath;
}
}
return $results;
}
/**
* Recursively copy directory in filesystem.
*
* @param string $source
* @param string $target
* @param string $source
* @param string $target
* @throws \RuntimeException
*/
public static function copy($source, $target)
@@ -157,8 +179,8 @@ abstract class Folder
/**
* Move directory in filesystem.
*
* @param string $source
* @param string $target
* @param string $source
* @param string $target
* @throws \RuntimeException
*/
public static function move($source, $target)
@@ -186,7 +208,7 @@ abstract class Folder
/**
* Recursively delete directory from filesystem.
*
* @param string $target
* @param string $target
* @throws \RuntimeException
*/
public static function delete($target)
@@ -204,6 +226,26 @@ abstract class Folder
// Make sure that the change will be detected when caching.
@touch(dirname($target));
return $success;
}
/**
* @param string $folder
* @throws \RuntimeException
* @internal
*/
public static function mkdir($folder)
{
if (is_dir($folder)) {
return;
}
$success = @mkdir($folder, 0777, true);
if (!$success) {
$error = error_get_last();
throw new \RuntimeException($error['message']);
}
}
/**
@@ -227,34 +269,16 @@ abstract class Folder
return @rmdir($folder);
}
/**
* @param string $folder
* @throws \RuntimeException
* @internal
*/
protected static function mkdir($folder)
{
if (is_dir($folder)) {
return;
}
$success = @mkdir($folder, 0777, true);
if (!$success) {
$error = error_get_last();
throw new \RuntimeException($error['message']);
}
}
}
class GravRecursiveFilterIterator extends \RecursiveFilterIterator {
class GravRecursiveFilterIterator extends \RecursiveFilterIterator
{
public static $FILTERS = array(
'.', '..', '.DS_Store'
'..', '.DS_Store'
);
public function accept() {
public function accept()
{
return !in_array(
$this->current()->getFilename(),
self::$FILTERS,

View File

@@ -0,0 +1,364 @@
<?php
namespace Grav\Common\GPM;
use Grav\Common\Iterator;
class GPM extends Iterator
{
/**
* Local installed Packages
* @var Packages
*/
private $installed;
/**
* Remote available Packages
* @var Packages
*/
private $repository;
/**
* @var Remote\Grav
*/
public $grav;
/**
* Internal cache
* @var Iterator
*/
protected $cache;
/**
* Creates a new GPM instance with Local and Remote packages available
* @param boolean $refresh Applies to Remote Packages only and forces a refetch of data
* @param callable $callback Either a function or callback in array notation
*/
public function __construct($refresh = false, $callback = null)
{
$this->installed = new Local\Packages();
$this->repository = new Remote\Packages($refresh, $callback);
$this->grav = new Remote\Grav($refresh, $callback);
}
/**
* Returns the Locally installed packages
* @return Iterator The installed packages
*/
public function getInstalled()
{
return $this->installed;
}
/**
* Returns the amount of locally installed packages
* @return integer Amount of installed packages
*/
public function countInstalled()
{
$installed = $this->getInstalled();
return count($installed['plugins']) + count($installed['themes']);
}
/**
* Return the instance of a specific Plugin
* @param string $slug The slug of the Plugin
* @return Package The instance of the Plugin
*/
public function getInstalledPlugin($slug)
{
return $this->installed['plugins'][$slug];
}
/**
* Returns the Locally installed plugins
* @return Iterator The installed plugins
*/
public function getInstalledPlugins()
{
return $this->installed['plugins'];
}
/**
* Checks if a Plugin is installed
* @param string $slug The slug of the Plugin
* @return boolean True if the Plugin has been installed. False otherwise
*/
public function isPluginInstalled($slug)
{
return isset($this->installed['plugins'][$slug]);
}
/**
* Return the instance of a specific Theme
* @param string $slug The slug of the Theme
* @return Package The instance of the Theme
*/
public function getInstalledTheme($slug)
{
return $this->installed['themes'][$slug];
}
/**
* Returns the Locally installed themes
* @return Iterator The installed themes
*/
public function getInstalledThemes()
{
return $this->installed['themes'];
}
/**
* Checks if a Theme is installed
* @param string $slug The slug of the Theme
* @return boolean True if the Theme has been installed. False otherwise
*/
public function isThemeInstalled($slug)
{
return isset($this->installed['themes'][$slug]);
}
/**
* Returns the amount of updates available
* @return integer Amount of available updates
*/
public function countUpdates()
{
$count = 0;
$count += count($this->getUpdatablePlugins());
$count += count($this->getUpdatableThemes());
return $count;
}
/**
* Returns an array of Plugins and Themes that can be updated.
* Plugins and Themes are extended with the `available` property that relies to the remote version
* @return array Array of updatable Plugins and Themes.
* Format: ['total' => int, 'plugins' => array, 'themes' => array]
*/
public function getUpdatable()
{
$plugins = $this->getUpdatablePlugins();
$themes = $this->getUpdatableThemes();
$items = [
'total' => count($plugins)+count($themes),
'plugins' => $plugins,
'themes' => $themes
];
return $items;
}
/**
* Returns an array of Plugins that can be updated.
* The Plugins are extended with the `available` property that relies to the remote version
* @return Iterator Array of updatable Plugins
*/
public function getUpdatablePlugins()
{
$items = [];
$repository = $this->repository['plugins'];
// local cache to speed things up
if (isset($this->cache[__METHOD__])) {
return $this->cache[__METHOD__];
}
foreach ($this->installed['plugins'] as $slug => $plugin) {
if (!isset($repository[$slug]) || $plugin->symlink) {
continue;
}
$local_version = $plugin->version ? $plugin->version : 'Unknown';
$remote_version = $repository[$slug]->version;
if (version_compare($local_version, $remote_version) < 0) {
$repository[$slug]->available = $remote_version;
$repository[$slug]->version = $local_version;
$items[$slug] = $repository[$slug];
}
}
$this->cache[__METHOD__] = $items;
return $items;
}
/**
* Check if a Plugin or Theme is updatable
* @param string $slug The slug of the package
* @return boolean True if updatable. False otherwise or if not found
*/
public function isUpdatable($slug)
{
return $this->isPluginUpdatable($slug) || $this->isThemeUpdatable($slug);
}
/**
* Checks if a Plugin is updatable
* @param string $plugin The slug of the Plugin
* @return boolean True if the Plugin is updatable. False otherwise
*/
public function isPluginUpdatable($plugin)
{
return array_key_exists($plugin, $this->getUpdatablePlugins());
}
/**
* Returns an array of Themes that can be updated.
* The Themes are extended with the `available` property that relies to the remote version
* @return Iterator Array of updatable Themes
*/
public function getUpdatableThemes()
{
$items = [];
$repository = $this->repository['themes'];
// local cache to speed things up
if (isset($this->cache[__METHOD__])) {
return $this->cache[__METHOD__];
}
foreach ($this->installed['themes'] as $slug => $plugin) {
if (!isset($repository[$slug]) || $plugin->symlink) {
continue;
}
$local_version = $plugin->version ? $plugin->version : 'Unknown';
$remote_version = $repository[$slug]->version;
if (version_compare($local_version, $remote_version) < 0) {
$repository[$slug]->available = $remote_version;
$repository[$slug]->version = $local_version;
$items[$slug] = $repository[$slug];
}
}
$this->cache[__METHOD__] = $items;
return $items;
}
/**
* Checks if a Theme is Updatable
* @param string $theme The slug of the Theme
* @return boolean True if the Theme is updatable. False otherwise
*/
public function isThemeUpdatable($theme)
{
return array_key_exists($theme, $this->getUpdatableThemes());
}
/**
* Returns a Plugin from the repository
* @param string $slug The slug of the Plugin
* @return mixed Package if found, NULL if not
*/
public function getRepositoryPlugin($slug)
{
return @$this->repository['plugins'][$slug];
}
/**
* Returns the list of Plugins available in the repository
* @return Iterator The Plugins remotely available
*/
public function getRepositoryPlugins()
{
return $this->repository['plugins'];
}
/**
* Returns a Theme from the repository
* @param string $slug The slug of the Theme
* @return mixed Package if found, NULL if not
*/
public function getRepositoryTheme($slug)
{
return @$this->repository['themes'][$slug];
}
/**
* Returns the list of Themes available in the repository
* @return Iterator The Themes remotely available
*/
public function getRepositoryThemes()
{
return $this->repository['themes'];
}
/**
* Returns the list of Plugins and Themes available in the repository
* @return array Array of available Plugins and Themes
* Format: ['plugins' => array, 'themes' => array]
*/
public function getRepository()
{
return $this->repository;
}
/**
* Searches for a Package in the repository
* @param string $search Can be either the slug or the name
* @return Package Package if found, FALSE if not
*/
public function findPackage($search)
{
$search = strtolower($search);
if ($found = $this->getRepositoryTheme($search)) {
return $found;
}
if ($found = $this->getRepositoryPlugin($search)) {
return $found;
}
foreach ($this->getRepositoryThemes() as $slug => $theme) {
if ($search == $slug || $search == $theme->name) {
return $theme;
}
}
foreach ($this->getRepositoryPlugins() as $slug => $plugin) {
if ($search == $slug || $search == $plugin->name) {
return $plugin;
}
}
return false;
}
/**
* Returns the list of Plugins and Themes available in the repository
* @return array Array of available Plugins and Themes
* Format: ['plugins' => array, 'themes' => array]
*/
/**
* Searches for a list of Packages in the repository
* @param array $searches An array of either slugs or names
* @return array Array of found Packages
* Format: ['total' => int, 'not_found' => array, <found-slugs>]
*/
public function findPackages($searches = [])
{
$packages = ['total' => 0, 'not_found' => []];
foreach ($searches as $search) {
if ($found = $this->findPackage($search)) {
if (!isset($packages[$found->package_type])) {
$packages[$found->package_type] = [];
}
$packages[$found->package_type][$found->slug] = $found;
$packages['total']++;
} else {
$packages['not_found'][] = $search;
}
}
return $packages;
}
}

View File

@@ -0,0 +1,275 @@
<?php
namespace Grav\Common\GPM;
use Grav\Common\Filesystem\Folder;
class Installer
{
/** @const No error */
const OK = 0;
/** @const Target already exists */
const EXISTS = 1;
/** @const Target is a symbolic link */
const IS_LINK = 2;
/** @const Target doesn't exist */
const NOT_FOUND = 4;
/** @const Target is not a directory */
const NOT_DIRECTORY = 8;
/** @const Target is not a Grav instance */
const NOT_GRAV_ROOT = 16;
/** @const Error while trying to open the ZIP package */
const ZIP_OPEN_ERROR = 32;
/** @const Error while trying to extract the ZIP package */
const ZIP_EXTRACT_ERROR = 64;
/**
* Destination folder on which validation checks are applied
* @var string
*/
protected static $target;
/**
* Error Code
* @var integer
*/
protected static $error = 0;
/**
* Default options for the install
* @var array
*/
protected static $options = [
'overwrite' => true,
'ignore_symlinks' => true,
'sophisticated' => false,
'install_path' => '',
'exclude_checks' => [self::EXISTS, self::NOT_FOUND, self::IS_LINK]
];
/**
* Installs a given package to a given destination.
*
* @param string $package The local path to the ZIP package
* @param string $destination The local path to the Grav Instance
* @param array $options Options to use for installing. ie, ['install_path' => 'user/themes/antimatter']
*
* @return boolean True if everything went fine, False otherwise.
*/
public static function install($package, $destination, $options = [])
{
$destination = rtrim($destination, DS);
$options = array_merge(self::$options, $options);
$install_path = rtrim($destination . DS . ltrim($options['install_path'], DS), DS);
if (!self::isGravInstance($destination) || !self::isValidDestination($install_path, $options['exclude_checks'])
) {
return false;
}
if (
self::lastErrorCode() == self::IS_LINK && $options['ignore_symlinks'] ||
self::lastErrorCode() == self::EXISTS && !$options['overwrite']
) {
return false;
}
$zip = new \ZipArchive();
$archive = $zip->open($package);
$tmp = CACHE_DIR . DS . 'tmp/Grav-' . uniqid();
if ($archive !== true) {
self::$error = self::ZIP_OPEN_ERROR;
return false;
}
Folder::mkdir($tmp);
$unzip = $zip->extractTo($tmp);
if (!$unzip) {
self::$error = self::ZIP_EXTRACT_ERROR;
$zip->close();
Folder::delete($tmp);
return false;
}
if (!$options['sophisticated']) {
self::nonSophisticatedInstall($zip, $install_path, $tmp);
} else {
self::sophisticatedInstall($zip, $install_path, $tmp);
}
Folder::delete($tmp);
$zip->close();
self::$error = self::OK;
return true;
}
public static function nonSophisticatedInstall($zip, $install_path, $tmp)
{
$container = $zip->getNameIndex(0); // TODO: better way of determining if zip has container folder
if (file_exists($install_path)) {
Folder::delete($install_path);
}
Folder::move($tmp . DS . $container, $install_path);
return true;
}
public static function sophisticatedInstall($zip, $install_path, $tmp)
{
for ($i = 0, $l = $zip->numFiles; $i < $l; $i++) {
$filename = $zip->getNameIndex($i);
$fileinfo = pathinfo($filename);
$depth = count(explode(DS, rtrim($filename, '/')));
if ($depth > 2) {
continue;
}
$path = $install_path . DS . $fileinfo['basename'];
if (is_link($path)) {
continue;
} else {
if (is_dir($path)) {
Folder::delete($path);
Folder::move($tmp . DS . $filename, $path);
if ($fileinfo['basename'] == 'bin') {
foreach (glob($path . DS . '*') as $file) {
@chmod($file, 0755);
}
}
} else {
if (is_file($path)) {
@unlink($path);
@copy($tmp . DS . $filename, $path);
}
}
}
}
return true;
}
/**
* Runs a set of checks on the destination and sets the Error if any
*
* @param string $destination The directory to run validations at
* @param array $exclude An array of constants to exclude from the validation
*
* @return boolean True if validation passed. False otherwise
*/
public static function isValidDestination($destination, $exclude = [])
{
self::$error = 0;
self::$target = $destination;
if (is_link($destination)) {
self::$error = self::IS_LINK;
} elseif (file_exists($destination)) {
self::$error = self::EXISTS;
} elseif (!file_exists($destination)) {
self::$error = self::NOT_FOUND;
} elseif (!is_dir($destination)) {
self::$error = self::NOT_DIRECTORY;
}
if (count($exclude) && in_array(self::$error, $exclude)) {
return true;
}
return !(self::$error);
}
/**
* Validates if the given path is a Grav Instance
*
* @param string $target The local path to the Grav Instance
*
* @return boolean True if is a Grav Instance. False otherwise
*/
public static function isGravInstance($target)
{
self::$error = 0;
self::$target = $target;
if (
!file_exists($target . DS . 'index.php') ||
!file_exists($target . DS . 'bin') ||
!file_exists($target . DS . 'user') ||
!file_exists($target . DS . 'system' . DS . 'config' . DS . 'system.yaml')
) {
self::$error = self::NOT_GRAV_ROOT;
}
return !self::$error;
}
/**
* Returns the last error occurred in a string message format
* @return string The message of the last error
*/
public static function lastErrorMsg()
{
$msg = 'Unknown Error';
switch (self::$error) {
case 0:
$msg = 'No Error';
break;
case self::EXISTS:
$msg = 'The target path "' . self::$target . '" already exists';
break;
case self::IS_LINK:
$msg = 'The target path "' . self::$target . '" is a symbolic link';
break;
case self::NOT_FOUND:
$msg = 'The target path "' . self::$target . '" does not appear to exist';
break;
case self::NOT_DIRECTORY:
$msg = 'The target path "' . self::$target . '" does not appear to be a folder';
break;
case self::NOT_GRAV_ROOT:
$msg = 'The target path "' . self::$target . '" does not appear to be a Grav instance';
break;
case self::ZIP_OPEN_ERROR:
$msg = 'Unable to open the package file';
break;
case self::ZIP_EXTRACT_ERROR:
$msg = 'An error occurred while extracting the package';
break;
default:
return 'Unknown error';
break;
}
return $msg;
}
/**
* Returns the last error code of the occurred error
* @return integer The code of the last error
*/
public static function lastErrorCode()
{
return self::$error;
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Grav\Common\GPM\Local;
use Grav\Common\GravTrait;
use Grav\Common\Iterator;
class Collection extends Iterator
{
use GravTrait;
public function toJson()
{
$items = [];
foreach ($this->items as $name => $theme) {
$items[$name] = $theme->toArray();
}
return json_encode($items);
}
public function toArray()
{
$items = [];
foreach ($this->items as $name => $theme) {
$items[$name] = $theme->toArray();
}
return $items;
}
}

View File

@@ -0,0 +1,87 @@
<?php
namespace Grav\Common\GPM\Local;
use Grav\Common\Data\Data;
/**
* Class Package
* @package Grav\Common\GPM\Local
*/
class Package
{
/**
* @var Data
*/
protected $data;
/**
* @var \Grav\Common\Data\Blueprint
*/
protected $blueprints;
/**
* @param Data $package
* @param bool $package_type
*/
public function __construct(Data $package, $package_type = false)
{
$this->data = $package;
$this->blueprints = $this->data->blueprints();
if ($package_type) {
$html_description = \Parsedown::instance()->line($this->blueprints->get('description'));
$this->blueprints->set('package_type', $package_type);
$this->blueprints->set('description_html', $html_description);
$this->blueprints->set('description_plain', strip_tags($html_description));
$this->blueprints->set('symlink', is_link(USER_DIR . $package_type . DS . $this->blueprints->name));
}
}
/**
* @return mixed
*/
public function isEnabled()
{
return $this->data['enabled'];
}
/**
* @return Data
*/
public function getData()
{
return $this->data;
}
/**
* @param $key
* @return mixed
*/
public function __get($key)
{
return $this->blueprints->get($key);
}
/**
* @return string
*/
public function __toString()
{
return $this->toJson();
}
/**
* @return string
*/
public function toJson()
{
return $this->blueprints->toJson();
}
/**
* @return array
*/
public function toArray()
{
return $this->blueprints->toArray();
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Grav\Common\GPM\Local;
use Grav\Common\Iterator;
class Packages extends Iterator
{
private $plugins;
private $themes;
protected static $cache;
public function __construct()
{
// local cache to speed things up
if (!isset(self::$cache[__METHOD__])) {
self::$cache[__METHOD__] = [
'plugins' => new Plugins(),
'themes' => new Themes()
];
}
$this->plugins = self::$cache[__METHOD__]['plugins'];
$this->themes = self::$cache[__METHOD__]['themes'];
$this->append(['plugins' => $this->plugins]);
$this->append(['themes' => $this->themes]);
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Grav\Common\GPM\Local;
/**
* Class Plugins
* @package Grav\Common\GPM\Local
*/
class Plugins extends Collection
{
/**
* @var string
*/
private $type = 'plugins';
/**
* Local Plugins Constructor
*/
public function __construct()
{
$grav = self::$grav;
foreach ($grav['plugins']->all() as $name => $data) {
$this->items[$name] = new Package($data, $this->type);
}
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Grav\Common\GPM\Local;
class Themes extends Collection
{
private $type = 'themes';
public function __construct()
{
$grav = self::$grav;
foreach ($grav['themes']->all() as $name => $data) {
$this->items[$name] = new Package($data, $this->type);
}
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace Grav\Common\GPM\Remote;
use Grav\Common\GPM\Response;
use Grav\Common\GravTrait;
use Grav\Common\Iterator;
use \Doctrine\Common\Cache\Cache as DoctrineCache;
use \Doctrine\Common\Cache\FilesystemCache;
class Collection extends Iterator {
use GravTrait;
/**
* The cached data previously fetched
* @var string
*/
protected $raw;
/**
* The lifetime to store the entry in seconds
* @var integer
*/
private $lifetime = 86400;
private $repository;
private $cache;
private $plugins, $themes;
public function __construct($repository = null) {
if ($repository == null) {
throw new \RuntimeException("A repository is required for storing the cache");
}
$cache_dir = self::$grav['locator']->findResource('cache://gpm', true, true);
$this->cache = new FilesystemCache($cache_dir);
$this->repository = $repository;
$this->raw = $this->cache->fetch(md5($this->repository));
}
public function toJson() {
$items = [];
foreach ($this->items as $name => $theme) {
$items[$name] = $theme->toArray();
}
return json_encode($items);
}
public function toArray() {
$items = [];
foreach ($this->items as $name => $theme) {
$items[$name] = $theme->toArray();
}
return $items;
}
public function fetch($refresh = false, $callback = null) {
if (!$this->raw || $refresh) {
$response = Response::get($this->repository, [], $callback);
$this->raw = $response;
$this->cache->save(md5($this->repository), $this->raw, $this->lifetime);
}
return $this->raw;
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace Grav\Common\GPM\Remote;
class Grav extends Collection
{
private $repository = 'http://getgrav.org/downloads/grav.json';
private $data;
private $version;
private $date;
/**
* @param bool $refresh
* @param null $callback
*/
public function __construct($refresh = false, $callback = null)
{
parent::__construct($this->repository);
$this->fetch($refresh, $callback);
$this->data = json_decode($this->raw);
$this->version = @$this->data->version ?: '-';
$this->date = @$this->data->date ?: '-';
foreach ($this->data->assets as $slug => $data) {
$this->items[$slug] = new Package($data);
}
}
/**
* Returns the list of assets associated to the latest version of Grav
* @return array list of assets
*/
public function getAssets()
{
return $this->data->assets;
}
/**
* Returns the changelog list for each version of Grav
* @param string $diff the version number to start the diff from
*
* @return array changelog list for each version
*/
public function getChangelog($diff = null)
{
if (!$diff) {
return $this->data->changelog;
}
$diffLog = [];
foreach ($this->data->changelog as $version => $changelog) {
preg_match("/[\d\.]+/", $version, $cleanVersion);
if (!$cleanVersion || version_compare($diff, $cleanVersion[0], ">=")) { continue; }
$diffLog[$version] = $changelog;
}
return $diffLog;
}
/**
* Returns the latest version of Grav available remotely
* @return string
*/
public function getVersion()
{
return $this->version;
}
/**
* Return the release date of the latest Grav
* @return string
*/
public function getDate()
{
return $this->date;
}
public function isUpdatable()
{
return version_compare(GRAV_VERSION, $this->getVersion(), '<');
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Grav\Common\GPM\Remote;
class Package {
public function __construct($package, $package_type = false) {
$this->data = $package;
if ($package_type) {
$this->data->package_type = $package_type;
}
}
public function getData() {
return $this->data;
}
public function __get($key) {
return $this->data->$key;
}
public function __toString() {
return $this->toJson();
}
public function toJson() {
return json_encode($this->data);
}
public function toArray() {
return $this->data;
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Grav\Common\GPM\Remote;
use Grav\Common\Iterator;
class Packages extends Iterator
{
private $plugins;
private $themes;
protected static $cache;
public function __construct($refresh = false, $callback = null)
{
// local cache to speed things up
if (!isset(self::$cache[__METHOD__])) {
self::$cache[__METHOD__] = [
'plugins' => new Plugins($refresh, $callback),
'themes' => new Themes($refresh, $callback)
];
}
$this->plugins = self::$cache[__METHOD__]['plugins']->toArray();
$this->themes = self::$cache[__METHOD__]['themes']->toArray();
$this->append(['plugins' => $this->plugins]);
$this->append(['themes' => $this->themes]);
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Grav\Common\GPM\Remote;
class Plugins extends Collection
{
private $repository = 'http://getgrav.org/downloads/plugins.json';
private $type = 'plugins';
private $data;
public function __construct($refresh = false, $callback = null)
{
parent::__construct($this->repository);
$this->fetch($refresh, $callback);
$this->data = json_decode($this->raw);
foreach ($this->data as $slug => $data) {
$this->items[$slug] = new Package($data, $this->type);
}
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Grav\Common\GPM\Remote;
class Themes extends Collection
{
private $repository = 'http://getgrav.org/downloads/themes.json';
private $type = 'themes';
private $data;
public function __construct($refresh = false, $callback = null)
{
parent::__construct($this->repository);
$this->fetch($refresh, $callback);
$this->data = json_decode($this->raw);
foreach ($this->data as $slug => $data) {
$this->items[$slug] = new Package($data, $this->type);
}
}
}

View File

@@ -0,0 +1,220 @@
<?php
namespace Grav\Common\GPM;
class Response
{
/**
* The callback for the progress
* @var callable Either a function or callback in array notation
*/
public static $callback = null;
/**
* Which method to use for HTTP calls, can be 'curl', 'fopen' or 'auto'. Auto is default and fopen is the preferred method
* @var string
*/
private static $method = 'auto';
/**
* Default parameters for `curl` and `fopen`
* @var array
*/
private static $defaults = [
'curl' => [
CURLOPT_REFERER => 'Grav GPM',
CURLOPT_USERAGENT => 'Grav GPM',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_HEADER => false,
/**
* Example of callback parameters from within your own class
*/
//CURLOPT_NOPROGRESS => false,
//CURLOPT_PROGRESSFUNCTION => [$this, 'progress']
],
'fopen' => [
'method' => 'GET',
'user_agent' => 'Grav GPM',
'max_redirects' => 5,
'follow_location' => 1,
'timeout' => 15,
/**
* Example of callback parameters from within your own class
*/
//'notification' => [$this, 'progress']
]
];
/**
* Sets the preferred method to use for making HTTP calls.
* @param string $method Default is `auto`
*/
public static function setMethod($method = 'auto')
{
if (!in_array($method, ['auto', 'curl', 'fopen'])) {
$method = 'auto';
}
self::$method = $method;
return new self();
}
/**
* Makes a request to the URL by using the preferred method
* @param string $uri URL to call
* @param array $options An array of parameters for both `curl` and `fopen`
* @return string The response of the request
*/
public static function get($uri = '', $options = [], $callback = null)
{
if (!self::isCurlAvailable() && !self::isFopenAvailable()) {
throw new \RuntimeException('Could not start an HTTP request. `allow_url_open` is disabled and `cURL` is not available');
}
$options = array_replace_recursive(self::$defaults, $options);
$method = 'get' . ucfirst(strtolower(self::$method));
self::$callback = $callback;
return static::$method($uri, $options, $callback);
}
/**
* Progress normalized for cURL and Fopen
* @param args Variable length of arguments passed in by stream method
* @return array Normalized array with useful data.
* Format: ['code' => int|false, 'filesize' => bytes, 'transferred' => bytes, 'percent' => int]
*/
public static function progress()
{
static $filesize = null;
$args = func_get_args();
$isCurlResource = is_resource($args[0]) && get_resource_type($args[0]) == 'curl';
$notification_code = !$isCurlResource ? $args[0] : false;
$bytes_transferred = $isCurlResource ? $args[2] : $args[4];
if ($isCurlResource) {
$filesize = $args[1];
} elseif ($notification_code == STREAM_NOTIFY_FILE_SIZE_IS) {
$filesize = $args[5];
}
if ($bytes_transferred > 0) {
if ($notification_code == STREAM_NOTIFY_PROGRESS|STREAM_NOTIFY_COMPLETED || $isCurlResource) {
$progress = [
'code' => $notification_code,
'filesize' => $filesize,
'transferred' => $bytes_transferred,
'percent' => $filesize <= 0 ? '-' : round(($bytes_transferred * 100) / $filesize, 1)
];
if (self::$callback !== null) {
call_user_func_array(self::$callback, [$progress]);
}
}
}
}
/**
* Checks if cURL is available
* @return boolean
*/
public static function isCurlAvailable()
{
return function_exists('curl_version');
}
/**
* Checks if the remote fopen request is enabled in PHP
* @return boolean
*/
public static function isFopenAvailable()
{
return preg_match('/1|yes|on|true/i', ini_get('allow_url_fopen'));
}
/**
* Automatically picks the preferred method
* @return string The response of the request
*/
private static function getAuto()
{
if (self::isFopenAvailable()) {
return self::getFopen(func_get_args());
}
if (self::isCurlAvailable()) {
return self::getCurl(func_get_args());
}
}
/**
* Starts a HTTP request via cURL
* @return string The response of the request
*/
private static function getCurl()
{
$args = func_get_args();
$uri = $args[0];
$options = $args[1];
$callback = $args[2];
$ch = curl_init($uri);
curl_setopt_array($ch, $options['curl']);
if ($callback) {
curl_setopt_array(
$ch,
[
CURLOPT_NOPROGRESS => false,
CURLOPT_PROGRESSFUNCTION => ['self', 'progress']
]
);
}
$response = curl_exec($ch);
if ($errno = curl_errno($ch)) {
$error_message = curl_strerror($errno);
throw new \RuntimeException("cURL error ({$errno}):\n {$error_message}");
}
curl_close($ch);
return $response;
}
/**
* Starts a HTTP request via fopen
* @return string The response of the request
*/
private static function getFopen()
{
if (count($args = func_get_args()) == 1) {
$args = $args[0];
}
$uri = $args[0];
$options = $args[1];
$callback = $args[2];
if ($callback) {
$options['fopen']['notification'] = ['self', 'progress'];
}
$stream = stream_context_create(['http' => $options['fopen']], $options['fopen']);
$content = @file_get_contents($uri, false, $stream);
if ($content === false) {
throw new \RuntimeException("Error while trying to download '$uri'");
}
return $content;
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace Grav\Common\GPM;
use Grav\Common\Filesystem\Folder;
use Grav\Common\GPM\Installer;
class Upgrader
{
/**
* Remote details about latest Grav version
* @var Packages
*/
private $remote;
/**
* Internal cache
* @var Iterator
*/
protected $cache;
/**
* Creates a new GPM instance with Local and Remote packages available
* @param boolean $refresh Applies to Remote Packages only and forces a refetch of data
* @param callable $callback Either a function or callback in array notation
*/
public function __construct($refresh = false, $callback = null)
{
$this->remote = new Remote\Grav($refresh, $callback);
}
/**
* Returns the release date of the latest version of Grav
* @return string
*/
public function getReleaseDate()
{
return $this->remote->getDate();
}
/**
* Returns the version of the installed Grav
* @return string
*/
public function getLocalVersion()
{
return GRAV_VERSION;
}
/**
* Returns the version of the remotely available Grav
* @return string
*/
public function getRemoteVersion()
{
return $this->remote->getVersion();
}
/**
* Returns an array of assets available to download remotely
* @return array
*/
public function getAssets()
{
return $this->remote->getAssets();
}
/**
* Returns the changelog list for each version of Grav
* @param string $diff the version number to start the diff from
*
* @return array return the chagenlog list for each version
*/
public function getChangelog($diff = null)
{
return $this->remote->getChangelog($diff);
}
/**
* Checks if the currently installed Grav is upgradable to a newer version
* @return boolean True if it's upgradable, False otherwise.
*/
public function isUpgradable()
{
return version_compare($this->getLocalVersion(), $this->getRemoteVersion(), "<");
}
}

View File

@@ -1,10 +1,13 @@
<?php
namespace Grav\Common;
use Grav\Common\Page\Pages;
use Grav\Common\Service\ConfigServiceProvider;
use Grav\Common\Service\StreamsServiceProvider;
use Grav\Component\DI\Container;
use Grav\Component\EventDispatcher\Event;
use Grav\Component\EventDispatcher\EventDispatcher;
use RocketTheme\Toolbox\DI\Container;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\Event\EventDispatcher;
use Grav\Common\Page\Medium;
/**
* Grav
@@ -50,8 +53,6 @@ class Grav extends Container
{
$container = new static($values);
$container['config_path'] = CACHE_DIR . 'config.php';
$container['grav'] = $container;
$container['uri'] = function ($c) {
@@ -65,9 +66,6 @@ class Grav extends Container
$container['events'] = function ($c) {
return new EventDispatcher;
};
$container['config'] = function ($c) {
return Config::instance($c);
};
$container['cache'] = function ($c) {
return new Cache($c);
};
@@ -90,9 +88,36 @@ class Grav extends Container
return new Assets();
};
$container['page'] = function ($c) {
$page = $c['pages']->dispatch($c['uri']->route());
/** @var Pages $pages */
$pages = $c['pages'];
$page = $pages->dispatch($c['uri']->route());
if (!$page || !$page->routable()) {
// special case where a media file is requested
if (!$page) {
$path_parts = pathinfo($c['uri']->route());
$page = $c['pages']->dispatch($path_parts['dirname']);
if ($page) {
$media = $page->media()->all();
$media_file = urldecode($path_parts['basename']);
if (isset($media[$media_file])) {
$medium = $media[$media_file];
// loop through actions for the image and call them
foreach ($c['uri']->query(null,true) as $action => $params) {
if (in_array($action, Medium::$valid_actions)) {
call_user_func_array(array(&$medium, $action), explode(',', $params));
}
}
header('Content-type: '. $mime = $medium->get('mime'));
echo file_get_contents($medium->path());
die;
}
}
}
// If no page found, fire event
$event = $c->fireEvent('onPageNotFound');
if (isset($event->page)) {
@@ -111,6 +136,7 @@ class Grav extends Container
};
$container->register(new StreamsServiceProvider);
$container->register(new ConfigServiceProvider);
return $container;
}
@@ -120,8 +146,8 @@ class Grav extends Container
// Use output buffering to prevent headers from being sent too early.
ob_start();
// Initialize stream wrappers.
$this['locator'];
// Initialize configuration.
$this['config']->init();
$this['plugins']->init();
@@ -172,6 +198,11 @@ class Grav extends Container
{
/** @var Uri $uri */
$uri = $this['uri'];
if (isset($this['session'])) {
$this['session']->close();
}
header("Location: " . rtrim($uri->rootUrl(), '/') .'/'. trim($route, '/'), true, $code);
exit();
}
@@ -232,7 +263,10 @@ class Grav extends Container
if($this['config']->get('system.debugger.shutdown.close_connection')) {
set_time_limit(0);
ignore_user_abort(true);
session_write_close();
if (isset($this['session'])) {
$this['session']->close();
}
header('Content-length: ' . ob_get_length());
header("Connection: close\r\n");

View File

@@ -90,9 +90,8 @@ class Inflector
/**
* Singularizes English nouns.
*
* @access static public
* @static
* @param string $word English noun to singularize
* @param int $count
* @return string Singular noun.
*/
public static function singularize($word, $count = 1)
@@ -353,10 +352,10 @@ class Inflector
public static function monthize($days)
{
$now = new JDate();
$end = new JDate();
$now = new \DateTime();
$end = new \DateTime();
$duration = new DateInterval("P{$days}D");
$duration = new \DateInterval("P{$days}D");
$diff = $end->add($duration)->diff($now);

View File

@@ -1,7 +1,12 @@
<?php
namespace Grav\Common;
use Symfony\Component\Yaml\Yaml;
use RocketTheme\Toolbox\ArrayTraits\ArrayAccessWithGetters;
use RocketTheme\Toolbox\ArrayTraits\Iterator as ArrayIterator;
use RocketTheme\Toolbox\ArrayTraits\Constructor;
use RocketTheme\Toolbox\ArrayTraits\Countable;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\Serializable;
/**
* Class Iterator
@@ -9,25 +14,12 @@ use Symfony\Component\Yaml\Yaml;
*/
class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
{
use Constructor, ArrayAccessWithGetters, ArrayIterator, Countable, Serializable, Export;
/**
* @var array
*/
protected $items = array();
/**
* @var bool
*/
protected $unset = false;
/**
* Constructor.
*
* @param array $items Initial items inside the iterator.
*/
public function __construct(array $items = array())
{
$this->items = $items;
}
protected $items = [];
/**
* Convert function calls for the existing keys into their values.
@@ -41,49 +33,6 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
return (isset($this->items[$key])) ? $this->items[$key] : null;
}
/**
* Array getter shorthand to get items.
*
* @param string $key
* @return mixed
*/
public function __get($key)
{
return (isset($this->items[$key])) ? $this->items[$key] : null;
}
/**
* Array setter shorthand to set the value.
*
* @param string $key
* @param mixed $value
*/
public function __set($key, $value)
{
$this->items[$key] = $value;
}
/**
* Array isset shorthand to set the value.
*
* @param string $key
* @return bool
*/
public function __isset($key)
{
return isset($this->items[$key]);
}
/**
* Array unset shorthand to remove the key.
*
* @param string $key
*/
public function __unset($key)
{
$this->offsetUnset($key);
}
/**
* Clone the iterator.
*/
@@ -164,7 +113,7 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
shuffle($keys);
$new = array();
foreach($keys as $key) {
foreach ($keys as $key) {
$new[$key] = $this->items[$key];
}
@@ -215,180 +164,4 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable
return $this;
}
// Implements export functions to array, YAML and JSON.
/**
* Return items as an array.
*
* @return array Array presentation of the iterator.
*/
public function toArray()
{
return $this->items;
}
/**
* Return YAML encoded string of items.
*
* @return string YAML presentation of the iterator.
*/
public function toYaml()
{
return Yaml::dump($this->items);
}
/**
* Return JSON encoded string of items.
*
* @return string JSON presentation of the iterator.
*/
public function toJson()
{
return json_encode($this->items);
}
// Implements Iterator.
/**
* Returns the current element.
*
* @return mixed Can return any type.
*/
public function current()
{
return current($this->items);
}
/**
* Returns the key of the current element.
*
* @return mixed Returns scalar on success, or NULL on failure.
*/
public function key()
{
return key($this->items);
}
/**
* Moves the current position to the next element.
*
* @return void
*/
public function next()
{
if ($this->unset) {
// If current item was unset, position is already in the next element (do nothing).
$this->unset = false;
} else {
next($this->items);
}
}
/**
* Rewinds back to the first element of the Iterator.
*
* @return void
*/
public function rewind()
{
$this->unset = false;
reset($this->items);
}
/**
* This method is called after Iterator::rewind() and Iterator::next() to check if the current position is valid.
*
* @return bool Returns TRUE on success or FALSE on failure.
*/
public function valid()
{
return key($this->items) !== null;
}
// Implements ArrayAccess
/**
* Whether or not an offset exists.
*
* @param mixed $offset An offset to check for.
* @return bool Returns TRUE on success or FALSE on failure.
*/
public function offsetExists($offset)
{
return isset($this->items[$offset]);
}
/**
* Returns the value at specified offset.
*
* @param mixed $offset The offset to retrieve.
* @return mixed Can return all value types.
*/
public function offsetGet($offset)
{
return isset($this->items[$offset]) ? $this->items[$offset] : null;
}
/**
* Assigns a value to the specified offset.
*
* @param mixed $offset The offset to assign the value to.
* @param mixed $value The value to set.
*/
public function offsetSet($offset, $value)
{
if (is_null($offset)) {
$this->items[] = $value;
} else {
$this->items[$offset] = $value;
}
}
/**
* Unsets an offset.
*
* @param mixed $offset The offset to unset.
*/
public function offsetUnset($offset)
{
if ($offset == key($this->items)) {
$this->unset = true;
}
unset($this->items[$offset]);
}
// Implements Countable
/**
* This method is executed when using the count() function.
*
* @return int The count of items.
*/
public function count()
{
return count($this->items);
}
// Implements Serializable
/**
* Returns string representation of the object.
*
* @return string Returns the string representation of the object.
*/
public function serialize()
{
return serialize($this->items);
}
/**
* Called during unserialization of the object.
*
* @param string $serialized The string representation of the object.
*/
public function unserialize($serialized)
{
$this->items = unserialize($serialized);
}
}

View File

@@ -1,8 +1,11 @@
<?php
namespace Grav\Common\Markdown;
use Grav\Common\Config\Config;
use Grav\Common\Debugger;
use Grav\Common\GravTrait;
use Grav\Common\Page\Medium;
use Grav\Common\Uri;
/**
* A trait to add some custom processing to the identifyLink() method in Parsedown and ParsedownExtra
@@ -13,10 +16,13 @@ trait MarkdownGravLinkTrait
protected function identifyLink($Excerpt)
{
/** @var Config $config */
$config = self::$grav['config'];
// Run the parent method to get the actual results
$Excerpt = parent::identifyLink($Excerpt);
$actions = array();
$this->base_url = trim(self::$grav['config']->get('system.base_url_relative'));
$this->base_url = trim($config->get('system.base_url_relative'));
// if this is a link
if (isset($Excerpt['element']['attributes']['href'])) {
@@ -57,8 +63,8 @@ trait MarkdownGravLinkTrait
// loop through actions for the image and call them
foreach ($actions as $action => $params) {
// as long as it's not an html, url or ligtbox action
if (!in_array($action, ['html','url','lightbox'])) {
// as long as it's a valid action
if (in_array($action, Medium::$valid_actions)) {
call_user_func_array(array(&$medium, $action), explode(',', $params));
}
}
@@ -92,7 +98,7 @@ trait MarkdownGravLinkTrait
}
} else {
// not a current page media file, see if it needs converting to relative
$Excerpt['element']['attributes']['src'] = $this->convertUrl($url['path']);
$Excerpt['element']['attributes']['src'] = $this->convertUrl(Uri::build_url($url));
}
}
}

View File

@@ -192,4 +192,40 @@ class Collection extends Iterator
public function currentPosition($path) {
return array_search($path, array_keys($this->items));
}
/**
* Creates new collection with only visible pages
*
* @return Collection The collection with only visible pages
*/
public function visible()
{
$visible = [];
foreach ($this->items as $path => $slug) {
$page = $this->pages->get($path);
if ($page->visible()) {
$visible[$path] = $slug;
}
}
return new static($visible, $this->params, $this->pages);
}
/**
* Creates new collection with only routable pages
*
* @return Collection The collection with only routable pages
*/
public function routable()
{
$routable = [];
foreach (array_keys($this->items) as $path => $slug) {
$page = $this->pages->get($path);
if ($page->routable()) {
$routable[$path] = $slug;
}
}
return new static($routable, $this->params, $this->pages);
}
}

View File

@@ -3,7 +3,7 @@ namespace Grav\Common\Page;
use Grav\Common\Getters;
use Grav\Common\Grav;
use Grav\Common\Config;
use Grav\Common\Config\Config;
use Grav\Common\GravTrait;
/**
@@ -56,6 +56,9 @@ class Media extends Getters
continue;
}
//set file size
$medium->set('size',$info->getSize());
// Assign meta files to the medium.
if ($meta) {
$medium->addMetaFile($meta);

View File

@@ -1,12 +1,12 @@
<?php
namespace Grav\Common\Page;
use Grav\Common\Config;
use Grav\Common\Data\Blueprint;
use Grav\Common\Data\Data;
use Grav\Common\Filesystem\File\Yaml;
use Grav\Common\Config\Config;
use Grav\Common\File\CompiledYamlFile;
use Grav\Common\Grav;
use Grav\Common\GravTrait;
use Grav\Common\Data\Blueprint;
use Grav\Common\Data\Data;
use Gregwar\Image\Image as ImageFile;
/**
@@ -50,6 +50,8 @@ class Medium extends Data
protected $type = 'guess';
protected $quality = 80;
public static $valid_actions = ['resize', 'forceResize', 'cropResize', 'crop', 'cropZoom', 'negate', 'brightness', 'contrast', 'grayscale', 'emboss', 'smooth', 'sharp', 'edge', 'colorize', 'sepia' ];
/**
* @var array
*/
@@ -92,6 +94,26 @@ class Medium extends Data
return $this->linkImage ? $this->html() : $this->url();
}
/**
* Return PATH to file.
*
* @return string path to file
*/
public function path()
{
/** @var Config $config */
$config = self::$grav['config'];
if ($this->image) {
$output = $this->image->cacheFile($this->type, $this->quality);
$this->reset();
$output = ROOT_DIR . $output;
} else {
$output = $this->get('path') . '/' . $this->get('filename');
}
return $output;
}
/**
* Return URL to file.
*
@@ -204,9 +226,11 @@ class Medium extends Data
public function lightboxRaw($width = null, $height = null)
{
/** @var Config $config */
$config = self::$grav['config'];
$url = $this->url();
$this->link($width, $height);
$lightbox_url = self::$grav['config']->get('system.base_url_relative') . '/'. $this->linkTarget;
$lightbox_url = $config->get('system.base_url_relative') . '/'. $this->linkTarget;
return array('a_url' => $lightbox_url, 'a_rel' => 'lightbox', 'img_url' => $url);
}
@@ -309,7 +333,7 @@ class Medium extends Data
$path = $this->get('path') . '/' . $this->get('filename') . '.meta.' . $type;
if ($type == 'yaml') {
$this->merge(Yaml::instance($path)->content());
$this->merge(CompiledYamlFile::instance($path)->content());
} elseif (in_array($type, array('jpg', 'jpeg', 'png', 'gif'))) {
$this->set('thumb', $path);
}

View File

@@ -1,20 +1,20 @@
<?php
namespace Grav\Common\Page;
use Grav\Common\Config;
use Grav\Common\Filesystem\Folder;
use Grav\Common\Config\Config;
use Grav\Common\GravTrait;
use Grav\Common\Utils;
use Grav\Common\Cache;
use Grav\Common\Twig;
use Grav\Common\Filesystem\File;
use Grav\Common\Filesystem\Folder;
use Grav\Common\Data;
use Grav\Common\Uri;
use Grav\Common\Grav;
use Grav\Common\Taxonomy;
use Grav\Common\Markdown\Markdown;
use Grav\Common\Markdown\MarkdownExtra;
use Grav\Component\EventDispatcher\Event;
use Grav\Common\Data\Blueprint;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\File\MarkdownFile;
use Symfony\Component\Yaml\Yaml;
/**
@@ -30,6 +30,11 @@ class Page
{
use GravTrait;
const ALL_PAGES = 0; // both standard and modular pages
const STANDARD_PAGES = 1; // visible and invisible pages (e.g. 01.regular/, invisible/)
const MODULAR_PAGES = 2; // modular pages (e.g. _modular/)
/**
* @var string Filename. Leave as null if page is folder.
*/
@@ -54,6 +59,7 @@ class Page
protected $modified;
protected $id;
protected $header;
protected $frontmatter;
protected $content;
protected $raw_content;
protected $pagination;
@@ -137,7 +143,27 @@ class Page
$this->header = null;
$this->content = null;
}
return $file->raw();
return $file ? $file->raw() : '';
}
public function frontmatter($var = null) {
if ($var) {
$this->frontmatter = (string) $var;
// Update also file object.
$file = $this->file();
if ($file) {
$file->frontmatter((string) $var);
}
// Force content re-processing.
$this->id(time().md5($this->filePath()));
}
if (!$this->frontmatter) {
$this->header();
}
return $this->frontmatter;
}
/**
@@ -164,6 +190,7 @@ class Page
$file = $this->file();
if ($file) {
$this->raw_content = $file->markdown();
$this->frontmatter = $file->frontmatter();
$this->header = (object) $file->header();
$var = true;
@@ -254,6 +281,8 @@ class Page
return Utils::truncateHTML($content, $size);
}
/**
* Gets and Sets the content based on content portion of the .md file
*
@@ -376,6 +405,10 @@ class Page
$path = explode('.', $name);
$scope = array_shift($path);
if ($name == 'frontmatter') {
return $this->frontmatter;
}
if ($scope == 'header') {
$current = $this->header();
foreach ($path as $field) {
@@ -397,12 +430,13 @@ class Page
/**
* Get file object to the page.
*
* @return File\Markdown|null
* @return MarkdownFile|null
*/
public function file()
{
if ($this->name) {
return File\Markdown::instance($this->filePath());
// TODO: use CompiledMarkdownFile after fixing issues in it.
return MarkdownFile::instance($this->filePath());
}
return null;
}
@@ -420,7 +454,7 @@ class Page
if ($file) {
$file->filename($this->filePath());
$file->header((array) $this->header());
$file->markdown($this->content());
$file->markdown($this->raw_content);
$file->save();
}
}
@@ -472,7 +506,7 @@ class Page
/**
* Get blueprints for the page.
*
* @return Data\Blueprint
* @return Blueprint
*/
public function blueprints()
{
@@ -610,7 +644,7 @@ class Page
$this->template = $var;
}
if (empty($this->template)) {
$this->template = str_replace(CONTENT_EXT, '', $this->name());
$this->template = ($this->modular() ? 'modular/' : '') . str_replace(CONTENT_EXT, '', $this->name());
}
return $this->template;
}
@@ -716,33 +750,36 @@ class Page
$this->metadata = array();
$page_header = $this->header;
// Set the Generator tag
$this->metadata['generator'] = array('name'=>'generator', 'content'=>'Grav ' . GRAV_VERSION);
// Merge any site.metadata settings in with page metadata
$defaults = (array) self::$grav['config']->get('site.metadata');
if (isset($page_header->metadata)) {
$page_header->metadata = array_merge($defaults, $page_header->metadata);
} else {
$page_header->metadata = $defaults;
}
// Safety check to ensure we have a header
if ($page_header) {
// Merge any site.metadata settings in with page metadata
$defaults = (array) self::$grav['config']->get('site.metadata');
// Build an array of meta objects..
foreach((array)$page_header->metadata as $key => $value) {
// If this is a property type metadata: "og", "twitter", "facebook" etc
if (is_array($value)) {
foreach ($value as $property => $prop_value) {
$prop_key = $key.":".$property;
$this->metadata[$prop_key] = array('property'=>$prop_key, 'content'=>$prop_value);
}
// If it this is a standard meta data type
if (isset($page_header->metadata)) {
$page_header->metadata = array_merge($defaults, $page_header->metadata);
} else {
if (in_array($key, $header_tag_http_equivs)) {
$this->metadata[$key] = array('http_equiv'=>$key, 'content'=>$value);
$page_header->metadata = $defaults;
}
// Build an array of meta objects..
foreach((array)$page_header->metadata as $key => $value) {
// If this is a property type metadata: "og", "twitter", "facebook" etc
if (is_array($value)) {
foreach ($value as $property => $prop_value) {
$prop_key = $key.":".$property;
$this->metadata[$prop_key] = array('property'=>$prop_key, 'content'=>$prop_value);
}
// If it this is a standard meta data type
} else {
$this->metadata[$key] = array('name'=>$key, 'content'=>$value);
if (in_array($key, $header_tag_http_equivs)) {
$this->metadata[$key] = array('http_equiv'=>$key, 'content'=>$value);
} else {
$this->metadata[$key] = array('name'=>$key, 'content'=>$value);
}
}
}
}
@@ -1100,14 +1137,25 @@ class Page
/**
* Returns children of this page.
*
* @param bool $modular|null whether or not to return modular children
* @return Collection
*/
public function children()
public function children($type = Page::STANDARD_PAGES)
{
/** @var Pages $pages */
$pages = self::$grav['pages'];
$children = $pages->children($this->path());
return $pages->children($this->path());
// Filter out modular pages on regular call
// Filter out non-modular pages when all you want is modular
foreach ($children as $child) {
$is_modular_page = $child->modular();
if (($is_modular_page && $type == Page::STANDARD_PAGES) || (!$is_modular_page && $type == Page::MODULAR_PAGES)) {
$children->remove($child->path());
}
}
return $children;
}
/**
@@ -1276,6 +1324,7 @@ class Page
*/
public function activeChild()
{
/** @var Uri $uri */
$uri = self::$grav['uri'];
if (!$this->home() && (strpos($uri->url(), $this->url()) === 0)) {
@@ -1434,12 +1483,10 @@ class Page
if (!empty($parts)) {
switch ($parts[0]) {
case 'modular':
// FIXME: filter by modular
$results = $this->children();
$results = $this->children(Page::MODULAR_PAGES);
break;
case 'children':
// FIXME: filter by non-modular
$results = $this->children();
$results = $this->children(Page::STANDARD_PAGES);
break;
}
}
@@ -1575,7 +1622,7 @@ class Page
} else {
$parsedown = new Markdown($this);
}
$content = $parsedown->parse($content);
$content = $parsedown->text($content);
return $content;
}
@@ -1613,17 +1660,14 @@ class Page
$parent = $this->parent();
// Extract visible children from the parent page.
$visible = array();
$list = array();
/** @var Page $page */
foreach ($parent as $page) {
foreach ($parent->children()->visible() as $page) {
if ($page->order()) {
$visible[$page->slug] = $page->path();
$list[$page->slug] = $page->path();
}
}
// List only visible pages.
$list = array_intersect($visible, $pages->sort($parent));
// If page was moved, take it out of the list.
if ($this->_action == 'move') {
unset($list[$this->slug()]);
@@ -1660,6 +1704,13 @@ class Page
Folder::copy($this->_original->path(), $this->path());
}
if ($this->name() != $this->_original->name()) {
$path = $this->path();
if (is_file($path . '/' . $this->_original->name())) {
rename($path . '/' . $this->_original->name(), $path . '/' . $this->name());
}
}
$this->_action = null;
$this->_original = null;
}

View File

@@ -1,14 +1,15 @@
<?php
namespace Grav\Common\Page;
use Grav\Common\Filesystem\Folder;
use Grav\Common\Grav;
use Grav\Common\Config;
use Grav\Common\Data;
use Grav\Common\Config\Config;
use Grav\Common\Utils;
use Grav\Common\Cache;
use Grav\Common\Taxonomy;
use Grav\Component\EventDispatcher\Event;
use Grav\Common\Data\Blueprint;
use Grav\Common\Data\Blueprints;
use Grav\Common\Filesystem\Folder;
use RocketTheme\Toolbox\Event\Event;
/**
* GravPages is the class that is the entry point into the hierarchy of pages
@@ -36,7 +37,7 @@ class Pages
/**
* @var array|string[]
*/
protected $routes;
protected $routes = array();
/**
* @var array
@@ -44,7 +45,7 @@ class Pages
protected $sort;
/**
* @var Data\Blueprints
* @var Blueprints
*/
protected $blueprints;
@@ -53,6 +54,11 @@ class Pages
*/
protected $last_modified;
/**
* @var Types
*/
static protected $types;
/**
* Constructor
*
@@ -172,6 +178,9 @@ class Pages
public function sortCollection(Collection $collection, $orderBy, $orderDir = 'asc', $orderManual = null)
{
$items = $collection->toArray();
if (!$items) {
return [];
}
$lookup = md5(json_encode($items));
if (!isset($this->sort[$lookup][$orderBy])) {
@@ -193,6 +202,7 @@ class Pages
*
* @param string $path
* @return Page
* @throws \Exception
*/
public function get($path)
{
@@ -252,15 +262,12 @@ class Pages
* Get a blueprint for a page type.
*
* @param string $type
* @return Data\Blueprint
* @return Blueprint
*/
public function blueprints($type)
{
if (!isset($this->blueprints)) {
/** @var Config $config */
$config = $this->grav['config'];
$this->blueprints = new Data\Blueprints(THEMES_DIR . $config->get('system.pages.theme') . '/blueprints/');
$this->blueprints = new Blueprints(self::getTypes());
}
try {
@@ -307,6 +314,26 @@ class Pages
return $list;
}
/**
* Get available page types.
*
* @return Types
*/
static public function getTypes()
{
if (!self::$types) {
self::$types = new Types();
self::$types->scanBlueprints('theme://blueprints/');
self::$types->scanTemplates('theme://templates/');
$event = new Event();
$event->types = self::$types;
Grav::instance()->fireEvent('onGetPageTemplates', $event);
}
return self::$types;
}
/**
* Get available page types.
*
@@ -314,14 +341,21 @@ class Pages
*/
static public function types()
{
$grav = Grav::instance();
$types = self::getTypes();
/** @var Config $config */
$config = $grav['config'];
return $types->pageSelect();
}
$blueprints = new Data\Blueprints(THEMES_DIR . $config->get('system.pages.theme') . '/blueprints/');
/**
* Get available page types.
*
* @return array
*/
static public function modularTypes()
{
$types = self::getTypes();
return $blueprints->types();
return $types->modularSelect();
}
/**
@@ -408,6 +442,8 @@ class Pages
$directory = rtrim($directory, DS);
$iterator = new \DirectoryIterator($directory);
$page = new Page;
/** @var Config $config */
$config = $this->grav['config'];
$page->path($directory);
@@ -429,13 +465,18 @@ class Pages
// set current modified of page
$last_modified = $page->modified();
// flat for content availability
$content_exists = false;
/** @var \DirectoryIterator $file */
foreach ($iterator as $file) {
$name = $file->getFilename();
$modified = $file->getMTime();
if ($file->isFile() && Utils::endsWith($name, CONTENT_EXT)) {
$page->init($file);
$content_exists = true;
if ($config->get('system.pages.events.page')) {
$this->grav->fireEvent('onPageProcessed', new Event(['page' => $page]));
@@ -458,22 +499,23 @@ class Pages
// set the modified time if not already set
if (!$page->date()) {
$page->date($file->getMTime());
$page->date($modified);
}
// set the last modified time on pages
$this->lastModified($file->getMTime());
if ($config->get('system.pages.events.page')) {
$this->grav->fireEvent('onFolderProcessed', new Event(['page' => $page]));
}
} else {
$date = $file->getMTime();
if ($date > $last_modified) {
$last_modified = $date;
}
}
// Update the last modified if it's newer than already found
if ($modified > $last_modified) {
$last_modified = $modified;
}
}
// Set routability to false if no page found
if (!$content_exists) {
$page->routable(false);
}
// Override the modified and ID so that it takes the latest change into account

View File

@@ -0,0 +1,86 @@
<?php
namespace Grav\Common\Page;
use Grav\Common\Filesystem\Folder;
use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
use RocketTheme\Toolbox\ArrayTraits\Constructor;
use RocketTheme\Toolbox\ArrayTraits\Countable;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\Iterator;
class Types implements \ArrayAccess, \Iterator, \Countable
{
use ArrayAccess, Constructor, Iterator, Countable, Export;
protected $items;
public function register($type, $blueprint = null)
{
if ($blueprint || empty($this->items[$type])) {
$this->items[$type] = $blueprint;
}
}
public function scanBlueprints($path)
{
$options = [
'compare' => 'Filename',
'pattern' => '|\.yaml$|',
'filters' => [
'key' => '|\.yaml$|'
],
'key' => 'SubPathName',
'value' => 'PathName',
];
$this->items = Folder::all($path, $options) + $this->items;
}
public function scanTemplates($path)
{
$options = [
'compare' => 'Filename',
'pattern' => '|\.html\.twig$|',
'filters' => [
'value' => '|\.html\.twig$|'
],
'value' => 'Filename',
'recursive' => false
];
foreach (Folder::all($path, $options) as $type) {
$this->register($type);
}
if (file_exists($path . 'modular/')) {
foreach (Folder::all($path . 'modular/', $options) as $type) {
$this->register('modular/' . $type);
}
}
}
public function pageSelect()
{
$list = [];
foreach ($this->items as $name => $file) {
if (strpos($name, '/')) {
continue;
}
$list[$name] = ucfirst(strtr($name, '_', ' '));
}
ksort($list);
return $list;
}
public function modularSelect()
{
$list = [];
foreach ($this->items as $name => $file) {
if (strpos($name, 'modular/') !== 0) {
continue;
}
$list[$name] = trim(ucfirst(strtr(basename($name), '_', ' ')));
}
ksort($list);
return $list;
}
}

View File

@@ -1,8 +1,9 @@
<?php
namespace Grav\Common;
use Grav\Component\EventDispatcher\EventDispatcher;
use Grav\Component\EventDispatcher\EventSubscriberInterface;
use Grav\Common\Config\Config;
use RocketTheme\Toolbox\Event\EventDispatcher;
use RocketTheme\Toolbox\Event\EventSubscriberInterface;
/**
* The Plugin object just holds the id and path to a plugin.

View File

@@ -1,9 +1,12 @@
<?php
namespace Grav\Common;
use Grav\Common\Filesystem\File;
use Grav\Component\EventDispatcher\EventDispatcher;
use Grav\Component\EventDispatcher\EventSubscriberInterface;
use Grav\Common\Config\Config;
use Grav\Common\Data\Blueprints;
use Grav\Common\Data\Data;
use Grav\Common\File\CompiledYamlFile;
use RocketTheme\Toolbox\Event\EventDispatcher;
use RocketTheme\Toolbox\Event\EventSubscriberInterface;
/**
* The Plugins object holds an array of all the plugin objects that
@@ -41,7 +44,7 @@ class Plugins extends Iterator
continue;
}
$filePath = $this->grav['locator']('plugin://' . $plugin . DS . $plugin . PLUGIN_EXT);
$filePath = $this->grav['locator']('plugins://' . $plugin . DS . $plugin . PLUGIN_EXT);
if (!is_file($filePath)) {
throw new \RuntimeException(sprintf("Plugin '%s' enabled but not found!", $filePath, $plugin));
}
@@ -73,12 +76,12 @@ class Plugins extends Iterator
/**
* Return list of all plugin data with their blueprints.
*
* @return array|Data\Data[]
* @return array
*/
static public function all()
{
$list = array();
$iterator = new \DirectoryIterator('plugin:///');
$iterator = new \DirectoryIterator('plugins://');
/** @var \DirectoryIterator $directory */
foreach ($iterator as $directory) {
@@ -95,18 +98,18 @@ class Plugins extends Iterator
return $list;
}
static public function get($type)
static public function get($name)
{
$blueprints = new Data\Blueprints('plugin://' . $type);
$blueprints = new Blueprints("plugins://{$name}");
$blueprint = $blueprints->get('blueprints');
$blueprint->name = $type;
$blueprint->name = $name;
// Load default configuration.
$file = File\Yaml::instance('plugin://' . "{$type}/{$type}" . YAML_EXT);
$obj = new Data\Data($file->content(), $blueprint);
$file = CompiledYamlFile::instance("plugins://{$name}/{$name}.yaml");
$obj = new Data($file->content(), $blueprint);
// Override with user configuration.
$file = File\Yaml::instance('plugin://' . "config/plugins/{$type}" . YAML_EXT);
$file = CompiledYamlFile::instance("user://config/plugins/{$name}.yaml");
$obj->merge($file->content());
// Save configuration always to user/config.

View File

@@ -1,81 +0,0 @@
<?php
namespace Grav\Common;
/**
* The Registry class is an implementation of the Registry Pattern to store and retrieve
* instances of objects used by Grav
*
* @author RocketTheme
* @license MIT
* @deprecated
*/
class Registry
{
/**
* Return global instance.
*
* @return Registry
*/
public static function instance()
{
user_error(__METHOD__ . '()', E_USER_DEPRECATED);
return new Registry;
}
/**
* Get entry from the registry.
*
* @param string $key
* @return mixed
* @throws \Exception
*/
public static function get($key)
{
user_error(__METHOD__ . '()', E_USER_DEPRECATED);
$instance = Grav::instance();
return $instance[strtolower($key)];
}
/**
* @internal
*/
private function __construct()
{
}
/**
* @internal
*/
private function __clone()
{
}
/**
* Store entry to the registry.
*
* @param string $key
* @param mixed $value
* @throws \Exception
*/
public function store($key, $value)
{
user_error(__CLASS__ . '::' . __METHOD__ . '()', E_USER_DEPRECATED);
$instance = Grav::instance();
$instance[strtolower($key)] = $value;
}
/**
* Get entry from the registry.
*
* @param string $key
* @return mixed
* @throws \Exception
*/
public function retrieve($key)
{
user_error(__CLASS__ . '::' . __METHOD__ . '()', E_USER_DEPRECATED);
$instance = Grav::instance();
return $instance[strtolower($key)];
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace Grav\Common\Service;
use Grav\Common\Config\Config;
use Grav\Common\Grav;
use Grav\Common\Filesystem\Folder;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use RocketTheme\Toolbox\Blueprints\Blueprints;
/**
* The Config class contains configuration information.
*
* @author RocketTheme
* @license MIT
*/
class ConfigServiceProvider implements ServiceProviderInterface
{
public function register(Container $container)
{
$self = $this;
$container['blueprints'] = function ($c) use ($self) {
return $self->loadMasterBlueprints($c);
};
$container['config'] = function ($c) use ($self) {
return $self->loadMasterConfig($c);
};
}
public function loadMasterConfig(Container $container)
{
$file = CACHE_DIR . 'compiled/config/master.php';
$data = is_file($file) ? (array) include $file : [];
if ($data) {
try {
$config = new Config($data, $container);
} catch (\Exception $e) {
}
}
if (!isset($config)) {
$file = GRAV_ROOT . '/setup.php';
$data = is_file($file) ? (array) include $file : [];
$config = new Config($data, $container);
}
return $config;
}
public function loadMasterBlueprints(Container $container)
{
$file = CACHE_DIR . 'compiled/blueprints/master.php';
$data = is_file($file) ? (array) include $file : [];
return new Blueprints($data, $container);
}
}

View File

@@ -1,11 +1,12 @@
<?php
namespace Grav\Common\Service;
use Grav\Component\DI\ServiceProviderInterface;
use Grav\Component\Filesystem\ResourceLocator;
use Grav\Component\Filesystem\StreamWrapper\ReadOnlyStream;
use Grav\Component\Filesystem\StreamWrapper\Stream;
use Grav\Common\Config\Config;
use Pimple\Container;
use RocketTheme\Toolbox\DI\ServiceProviderInterface;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use RocketTheme\Toolbox\StreamWrapper\ReadOnlyStream;
use RocketTheme\Toolbox\StreamWrapper\Stream;
class StreamsServiceProvider implements ServiceProviderInterface
{
@@ -14,16 +15,18 @@ class StreamsServiceProvider implements ServiceProviderInterface
$self = $this;
$container['locator'] = function($c) use ($self) {
$locator = new ResourceLocator;
$locator = new UniformResourceLocator(ROOT_DIR);
$self->init($c, $locator);
return $locator;
};
}
protected function init(Container $container, ResourceLocator $locator)
protected function init(Container $container, UniformResourceLocator $locator)
{
$schemes = $container['config']->get('streams.schemes');
/** @var Config $config */
$config = $container['config'];
$schemes = $config->get('streams.schemes');
if (!$schemes) {
return;
@@ -50,7 +53,7 @@ class StreamsServiceProvider implements ServiceProviderInterface
}
$type = !empty($config['type']) ? $config['type'] : 'ReadOnlyStream';
if ($type[0] != '\\') {
$type = '\\Grav\\Component\\Filesystem\\StreamWrapper\\' . $type;
$type = '\\RocketTheme\\Toolbox\\StreamWrapper\\' . $type;
}
if (!stream_wrapper_register($scheme, $type)) {

View File

@@ -1,91 +0,0 @@
<?php
namespace Grav\Common\Session;
use Grav\Common\Getters;
/**
* Session wide messages.
*
* @author RocketTheme
* @license MIT
*/
class Message
{
/**
* @var array|string[]
*/
protected $messages = array();
/**
* Add message to the queue.
*
* @param string $message
* @param string $scope
* @return $this
*/
public function add($message, $scope = 'default')
{
$message = array('message' => $message, 'scope' => $scope);
$this->messages[] = $message;
return $this;
}
/**
* Clear message queue.
*
* @param string $scope
* @return $this
*/
public function clear($scope = null)
{
if ($scope === null) {
$this->messages = array();
} else {
foreach ($this->messages as $key => $message) {
if ($message['scope'] == $scope) {
unset($this->messages[$key]);
}
}
}
return $this;
}
/**
* Fetch all messages.
*
* @param string $scope
* @return array
*/
public function all($scope = null)
{
if ($scope === null) {
return array_values($this->messages);
}
$messages = array();
foreach ($this->messages as $message) {
if ($message['scope'] == $scope) {
$messages[] = $message;
}
}
return $messages;
}
/**
* Fetch and clear message queue.
*
* @param string $scope
* @return array
*/
public function fetch($scope = null)
{
$messages = $this->all($scope);
$this->clear($scope);
return $messages;
}
}

View File

@@ -1,245 +0,0 @@
<?php
namespace Grav\Common\Session;
/**
* Session handling.
*
* @author RocketTheme
* @license MIT
*/
class Session implements \IteratorAggregate
{
/**
* @var bool
*/
protected $started = false;
/**
* @var Session
*/
static $instance;
/**
* @param int $lifetime Defaults to 1800 seconds.
* @param string $path Cookie path.
*/
public function __construct($lifetime, $path)
{
if (isset(self::$instance)) {
throw new \RuntimeException("Session has already been initialized.", 500);
}
// Destroy any existing sessions started with session.auto_start
if (session_id())
{
session_unset();
session_destroy();
}
// Disable transparent sid support
ini_set('session.use_trans_sid', '0');
// Only allow cookies
ini_set('session.use_cookies', 1);
session_set_cookie_params($lifetime, $path);
register_shutdown_function('session_write_close');
session_cache_limiter('none');
self::$instance = $this;
}
/**
* Get current session instance.
*
* @return Session
* @throws \RuntimeException
*/
public function instance()
{
if (!isset(self::$instance)) {
throw new \RuntimeException("Session hasn't been initialized.", 500);
}
return self::$instance;
}
/**
* Starts the session storage
*
* @return $this
* @throws \RuntimeException
*/
public function start()
{
if (!session_start()) {
throw new \RuntimeException('Failed to start session');
}
$this->started = true;
return $this;
}
/**
* Get session ID
*
* @return string Session ID
*/
public function getId()
{
return session_id();
}
/**
* Set session Id
*
* @param string $id Session ID
*
* @return $this
*/
public function setId($id)
{
session_id($id);
return $this;
}
/**
* Get session name
*
* @return string
*/
public function getName()
{
return session_name();
}
/**
* Set session name
*
* @param string $name
*
* @return $this
*/
public function setName($name)
{
session_name($name);
return $this;
}
/**
* Invalidates the current session.
*
* @return $this
*/
public function invalidate()
{
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params['path'], $params['domain'],
$params['secure'], $params['httponly']
);
session_unset();
session_destroy();
$this->started = false;
return $this;
}
/**
* Force the session to be saved and closed
*
* @return $this
*/
public function close()
{
session_write_close();
$this->started = false;
return $this;
}
/**
* Checks if an attribute is defined.
*
* @param string $name The attribute name
*
* @return bool True if the attribute is defined, false otherwise
*/
public function __isset($name)
{
return isset($_SESSION[$name]);
}
/**
* Returns an attribute.
*
* @param string $name The attribute name
*
* @return mixed
*/
public function __get($name)
{
return isset($_SESSION[$name]) ? $_SESSION[$name] : null;
}
/**
* Sets an attribute.
*
* @param string $name
* @param mixed $value
*/
public function __set($name, $value)
{
$_SESSION[$name] = $value;
}
/**
* Removes an attribute.
*
* @param string $name
*
* @return mixed The removed value or null when it does not exist
*/
public function __unset($name)
{
unset($_SESSION[$name]);
}
/**
* Returns attributes.
*
* @return array Attributes
*/
public function all()
{
return $_SESSION;
}
/**
* Retrieve an external iterator
*
* @return \ArrayIterator Return an ArrayIterator of $_SESSION
*/
public function getIterator()
{
return new \ArrayIterator($_SESSION);
}
/**
* Checks if the session was started.
*
* @return Boolean
*/
public function started()
{
return $this->started;
}
}

View File

@@ -1,7 +1,9 @@
<?php
namespace Grav\Common;
use \Grav\Common\Page;
use Grav\Common\Config\Config;
use Grav\Common\Page\Collection;
use Grav\Common\Page\Page;
/**
* The Taxonomy object is a singleton that holds a reference to a 'taxonomy map'. This map is
@@ -41,10 +43,10 @@ class Taxonomy
* Takes an individual page and processes the taxonomies configured in its header. It
* then adds those taxonomies to the map
*
* @param Page\Page $page the page to process
* @param Page $page the page to process
* @param array $page_taxonomy
*/
public function addTaxonomy(Page\Page $page, $page_taxonomy = null)
public function addTaxonomy(Page $page, $page_taxonomy = null)
{
if (!$page_taxonomy) {
$page_taxonomy = $page->taxonomy();
@@ -69,7 +71,7 @@ class Taxonomy
* particular taxonomy.
*
* @param array $taxonomies taxonomies to search, eg ['tag'=>['animal','cat']]
* @return Page\Page page object with sub-pages set to contain matches found in the taxonomy map
* @return Page page object with sub-pages set to contain matches found in the taxonomy map
*/
public function findTaxonomy($taxonomies)
{
@@ -83,7 +85,7 @@ class Taxonomy
}
}
return new Page\Collection($results, ['taxonomies' => $taxonomies]);
return new Collection($results, ['taxonomies' => $taxonomies]);
}
/**

View File

@@ -1,8 +1,7 @@
<?php
namespace Grav\Common;
use Grav\Common\Filesystem\File\Yaml;
use Grav\Component\Filesystem\ResourceLocator;
use Grav\Common\Config\Config;
class Theme extends Plugin
{

View File

@@ -1,12 +1,13 @@
<?php
namespace Grav\Common;
use Grav\Common\Config\Config;
use Grav\Common\File\CompiledYamlFile;
use Grav\Common\Data\Blueprints;
use Grav\Common\Data\Data;
use Grav\Common\Filesystem\File;
use Grav\Component\EventDispatcher\EventDispatcher;
use Grav\Component\EventDispatcher\EventSubscriberInterface;
use Grav\Component\Filesystem\ResourceLocator;
use RocketTheme\Toolbox\Event\EventDispatcher;
use RocketTheme\Toolbox\Event\EventSubscriberInterface;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
/**
* The Themes object holds an array of all the theme objects that Grav knows about.
@@ -73,7 +74,7 @@ class Themes extends Iterator
/**
* Get theme configuration or throw exception if it cannot be found.
*
* @param string $name
* @param string $name
* @return Data
* @throws \RuntimeException
*/
@@ -95,11 +96,11 @@ class Themes extends Iterator
}
// Load default configuration.
$file = File\Yaml::instance("themes://{$name}/{$name}" . YAML_EXT);
$file = CompiledYamlFile::instance("themes://{$name}/{$name}" . YAML_EXT);
$obj = new Data($file->content(), $blueprint);
// Override with user configuration.
$file = File\Yaml::instance("user://config/themes/{$name}" . YAML_EXT);
$file = CompiledYamlFile::instance("user://config/themes/{$name}" . YAML_EXT);
$obj->merge($file->content());
// Save configuration always to user/config.
@@ -130,7 +131,7 @@ class Themes extends Iterator
$config = $this->config;
$name = $this->current();
/** @var ResourceLocator $locator */
/** @var UniformResourceLocator $locator */
$locator = $grav['locator'];
$file = $locator('theme://theme.php') ?: $locator("theme://{$name}.php");
@@ -145,7 +146,7 @@ class Themes extends Iterator
$class = new $className($grav, $config, $name);
}
}
} elseif (!$locator('theme://')) {
} elseif (!$locator('theme://') && !defined('GRAV_CLI')) {
exit("Theme '$name' does not exist, unable to display page.");
}
@@ -161,24 +162,21 @@ class Themes extends Iterator
*
* @throws \InvalidArgumentException
*/
public function configure() {
public function configure()
{
$name = $this->current();
/** @var Config $config */
$config = $this->config;
$themeConfig = File\Yaml::instance("themes://{$name}/{$name}" . YAML_EXT)->content();
$this->loadConfiguration($name, $config);
$config->merge(['themes' => [$name => $themeConfig]]);
/** @var ResourceLocator $locator */
/** @var UniformResourceLocator $locator */
$locator = $this->grav['locator'];
// TODO: move
$registered = stream_get_wrappers();
$schemes = $config->get(
"themes.{$name}.streams.schemes",
['theme' => ['paths' => ["user/themes/{$name}"]]]
['theme' => ['paths' => $locator->findResources("themes://{$name}", false)]]
);
foreach ($schemes as $scheme => $config) {
@@ -196,7 +194,7 @@ class Themes extends Iterator
}
$type = !empty($config['type']) ? $config['type'] : 'ReadOnlyStream';
if ($type[0] != '\\') {
$type = '\\Grav\\Component\\Filesystem\\StreamWrapper\\' . $type;
$type = '\\RocketTheme\\Toolbox\\StreamWrapper\\' . $type;
}
if (!stream_wrapper_register($scheme, $type)) {
@@ -204,4 +202,11 @@ class Themes extends Iterator
}
}
}
protected function loadConfiguration($name, Config $config)
{
$themeConfig = CompiledYamlFile::instance("themes://{$name}/{$name}" . YAML_EXT)->content();
$config->joinDefaults("themes.{$name}", $themeConfig);
}
}

View File

@@ -1,8 +1,9 @@
<?php
namespace Grav\Common;
use \Grav\Common\Page\Page;
use Grav\Component\Filesystem\ResourceLocator;
use Grav\Common\Config\Config;
use Grav\Common\Page\Page;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
/**
* The Twig object handles all the Twig template rendering for Grav. It's a singleton object
@@ -67,7 +68,7 @@ class Twig
if (!isset($this->twig)) {
/** @var Config $config */
$config = $this->grav['config'];
/** @var ResourceLocator $locator */
/** @var UniformResourceLocator $locator */
$locator = $this->grav['locator'];
$this->twig_paths = $locator->findResources('theme://templates');
@@ -79,7 +80,7 @@ class Twig
$params = $config->get('system.twig');
if (!empty($params['cache'])) {
$params['cache'] = $locator->findResource('cache://');
$params['cache'] = $locator->findResource('cache://twig', true, true);
}
$this->twig = new \Twig_Environment($loader_chain, $params);
@@ -172,8 +173,7 @@ class Twig
// Get Twig template layout
if ($item->modularTwig()) {
$twig_vars['content'] = $content;
// FIXME: this is inconsistent with main page.
$template = $this->template('modular/' . $item->template()) . TEMPLATE_EXT;
$template = $item->template() . TEMPLATE_EXT;
$output = $this->twig->render($template, $twig_vars);
} else {
$name = '@Page:' . $item->path();
@@ -184,6 +184,24 @@ class Twig
return $output;
}
/**
* @param string $string string to render.
* @param array $vars Optional variables
* @return string
*/
public function processString($string, array $vars = array())
{
// override the twig header vars for local resolution
$this->grav->fireEvent('onTwigStringVariables');
$vars += $this->twig_vars;
$name = '@Var:' . $string;
$this->setTemplate($name, $string);
$output = $this->twig->render($name, $vars);
return $output;
}
/**
* Twig process that renders the site layout. This is the main twig process that renders the overall
* page and handles all the layout for the site display.

View File

@@ -1,6 +1,8 @@
<?php
namespace Grav\Common;
use Grav\Component\Filesystem\ResourceLocator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
/**
* The Twig extension adds some filters and functions that are useful for Grav
@@ -34,6 +36,7 @@ class TwigExtension extends \Twig_Extension
new \Twig_SimpleFilter('truncate', array($this,'truncateFilter')),
new \Twig_SimpleFilter('*ize', array($this,'inflectorFilter')),
new \Twig_SimpleFilter('md5', array($this,'md5Filter')),
new \Twig_SimpleFilter('sort_by_key', array($this,'sortByKeyFilter')),
);
}
@@ -209,11 +212,38 @@ class TwigExtension extends \Twig_Extension
public function urlFunc($input, $domain = false)
{
$grav = Grav::instance();
/** @var ResourceLocator $locator */
/** @var UniformResourceLocator $locator */
$locator = $grav['locator'];
/** @var Uri $uri */
$uri = $grav['uri'];
return $uri->rootUrl($domain) .'/'. $locator->findResource($input, false);
}
/**
* Sorts a collection by key
*
* @param string $input
* @param string $filter
* @param string $direction
* @return string
*/
public function sortByKeyFilter($input, $filter, $direction = SORT_ASC)
{
$output = [];
if (!$input) {
return $output;
}
foreach ($input as $key => $row) {
$output[$key] = $row[$filter];
}
array_multisort($output, $direction, $input);
return $input;
}
}

View File

@@ -28,7 +28,10 @@ class Uri
{
$base = 'http://';
$uri = $_SERVER['REQUEST_URI'];
$name = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'localhost';
$port = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : 80;
$uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
$root_path = rtrim(substr($_SERVER['PHP_SELF'], 0, strpos($_SERVER['PHP_SELF'], 'index.php')), '/');
@@ -36,15 +39,15 @@ class Uri
$base = (@$_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://';
}
$base .= $_SERVER['SERVER_NAME'];
$base .= $name;
if ($_SERVER['SERVER_PORT'] != '80' && $_SERVER['SERVER_PORT'] != '443') {
$base .= ":".$_SERVER['SERVER_PORT'];
if ($port != '80' && $port != '443') {
$base .= ":".$port;
}
// check if userdir in the path and workaround PHP bug with PHP_SELF
if (strpos($_SERVER['REQUEST_URI'], '/~') !== false && strpos($_SERVER['PHP_SELF'], '/~') === false) {
$root_path = substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], '/', 1)) . $root_path;
if (strpos($uri, '/~') !== false && strpos($_SERVER['PHP_SELF'], '/~') === false) {
$root_path = substr($uri, 0, strpos($uri, '/', 1)) . $root_path;
}
$this->base = $base;
@@ -130,7 +133,7 @@ class Uri
*/
public function route($absolute = false, $domain = false)
{
return ($absolute ? $this->rootUrl($domain) : '') . '/' . implode('/', $this->paths);
return urldecode(($absolute ? $this->rootUrl($domain) : '') . '/' . implode('/', $this->paths));
}
/**
@@ -139,12 +142,16 @@ class Uri
* @param string $id Optional attribute.
* @return string
*/
public function query($id = null)
public function query($id = null, $raw = false)
{
if (isset($id)) {
return filter_var($this->query[$id], FILTER_SANITIZE_STRING) ;
return isset($this->query[$id]) ? filter_var($this->query[$id], FILTER_SANITIZE_STRING) : null;
} else {
return http_build_query($this->query);
if ($raw) {
return $this->query;
} else {
return http_build_query($this->query);
}
}
}
@@ -215,7 +222,10 @@ class Uri
*
* @return String The extension of the URI
*/
public function extension() {
public function extension($default = null) {
if (!$this->extension) {
$this->extension = $default;
}
return $this->extension;
}
@@ -306,7 +316,6 @@ class Uri
*/
public function ip()
{
$ipaddress = '';
if (getenv('HTTP_CLIENT_IP'))
$ipaddress = getenv('HTTP_CLIENT_IP');
else if(getenv('HTTP_X_FORWARDED_FOR'))
@@ -324,4 +333,23 @@ class Uri
return $ipaddress;
}
/**
* The opposite of built-in PHP method parse_url()
*
* @param $parsed_url
* @return string
*/
public static function build_url($parsed_url) {
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
$host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
$port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
$user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
$pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
$pass = ($user || $pass) ? "$pass@" : '';
$path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
$query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
$fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
return "$scheme$user$pass$host$port$path$query$fragment";
}
}

View File

@@ -1,7 +1,9 @@
<?php
namespace Grav\Common\User;
use Grav\Common\Data\Blueprints;
use Grav\Common\Data\Data;
use Grav\Common\File\CompiledYamlFile;
/**
* User object
@@ -11,6 +13,30 @@ use Grav\Common\Data\Data;
*/
class User extends Data
{
/**
* Load user account.
*
* Always creates user object. To check if user exists, use $this->exists().
*
* @param string $username
* @return User
*/
public static function load($username)
{
// FIXME: validate directory name
$blueprints = new Blueprints('blueprints://user');
$blueprint = $blueprints->get('account');
$file = CompiledYamlFile::instance(ACCOUNTS_DIR . $username . YAML_EXT);
$content = $file->content();
if (!isset($content['username'])) {
$content['username'] = $username;
}
$user = new User($content, $blueprint);
$user->file($file);
return $user;
}
/**
* Authenticate user.
*

View File

@@ -40,6 +40,30 @@ abstract class Utils
return (object) array_merge((array) $obj1, (array) $obj2);
}
/**
* Recurseive remove a directory - DANGEROUS! USE WITH CARE!!!!
*
* @param $dir
* @return bool
*/
public static function rrmdir($dir) {
$files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::CHILD_FIRST
);
/** @var \DirectoryIterator $fileinfo */
foreach ($files as $fileinfo) {
if ($fileinfo->isDir()) {
if (false === rmdir($fileinfo->getRealPath())) return false;
} else {
if (false === unlink($fileinfo->getRealPath())) return false;
}
}
return rmdir($dir);
}
/**
* Truncate HTML by text length.
*

View File

@@ -1,46 +0,0 @@
<?php
namespace Grav\Component\ArrayTraits;
/**
* Class ArrayAccess
* @package Grav\Component\ArrayTraits
*
* @property array $items
*/
trait ArrayAccess
{
/**
* @param mixed $offset
* @return bool
*/
public function offsetExists($offset)
{
return isset($this->items[$offset]);
}
/**
* @param mixed $offset
* @return mixed
*/
public function offsetGet($offset)
{
return isset($this->items[$offset]) ? $this->items[$offset] : null;
}
/**
* @param mixed $offset
* @param mixed $value
*/
public function offsetSet($offset, $value)
{
$this->items[$offset] = $value;
}
/**
* @param mixed $offset
*/
public function offsetUnset($offset)
{
unset($this->items[$offset]);
}
}

View File

@@ -1,21 +0,0 @@
<?php
namespace Grav\Component\ArrayTraits;
/**
* Class Constructor
* @package Grav\Component\ArrayTraits
*
* @property array $items
*/
trait Constructor
{
/**
* Constructor.
*
* @param array $items Initial items inside the iterator.
*/
public function __construct(array $items = array())
{
$this->items = $items;
}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace Grav\Component\ArrayTraits;
/**
* Class Countable
* @package Grav\Component\ArrayTraits
*
* @property array $items;
*/
trait Countable
{
/**
* @return int
*/
public function count()
{
count($this->items);
}
}

View File

@@ -1,50 +0,0 @@
<?php
namespace Grav\Component\ArrayTraits;
trait Getters
{
use ArrayAccess;
/**
* Magic setter method
*
* @param mixed $offset Asset name value
* @param mixed $value Asset value
*/
public function __set($offset, $value)
{
$this->offsetSet($offset, $value);
}
/**
* Magic getter method
*
* @param mixed $offset Asset name value
* @return mixed Asset value
*/
public function __get($offset)
{
return $this->offsetGet($offset);
}
/**
* Magic method to determine if the attribute is set
*
* @param mixed $offset Asset name value
* @return boolean True if the value is set
*/
public function __isset($offset)
{
return $this->offsetExists($offset);
}
/**
* Magic method to unset the attribute
*
* @param mixed $offset The name value to unset
*/
public function __unset($offset)
{
$this->offsetUnset($offset);
}
}

View File

@@ -1,6 +0,0 @@
<?php
namespace Grav\Component\DI;
class Container extends \Pimple\Container
{
}

View File

@@ -1,6 +0,0 @@
<?php
namespace Grav\Component\DI;
interface ServiceProviderInterface extends \Pimple\ServiceProviderInterface
{
}

View File

@@ -1,15 +0,0 @@
<?php
namespace Grav\Component\EventDispatcher;
use Grav\Component\ArrayTraits\ArrayAccess;
use Grav\Component\ArrayTraits\Constructor;
class Event extends \Symfony\Component\EventDispatcher\Event implements \ArrayAccess
{
use ArrayAccess, Constructor;
/**
* @var array
*/
protected $items = array();
}

View File

@@ -1,16 +0,0 @@
<?php
namespace Grav\Component\EventDispatcher;
use \Symfony\Component\EventDispatcher\Event as BaseEvent;
class EventDispatcher extends \Symfony\Component\EventDispatcher\EventDispatcher
{
public function dispatch($eventName, BaseEvent $event = null)
{
if (null === $event) {
$event = new Event();
}
return parent::dispatch($eventName, $event);
}
}

View File

@@ -1,6 +0,0 @@
<?php
namespace Grav\Component\EventDispatcher;
interface EventSubscriberInterface extends \Symfony\Component\EventDispatcher\EventSubscriberInterface
{
}

View File

@@ -1,119 +0,0 @@
<?php
namespace Grav\Component\Filesystem;
/**
* Implements Uniform Resource Location
*
* @link http://webmozarts.com/2013/06/19/the-power-of-uniform-resource-location-in-php/
*/
class ResourceLocator
{
/**
* @var array
*/
protected $schemes = [];
/**
* @param string $scheme
* @param string $prefix
* @param string|array $paths
*/
public function addPath($scheme, $prefix, $paths)
{
$list = [];
foreach((array) $paths as $path) {
$path = trim($path, '/');
if (strstr($path, '://')) {
$list = array_merge($list, $this->find($path, true, false));
} else {
$list[] = $path;
}
}
if (isset($this->schemes[$scheme][$prefix])) {
$list = array_merge($list, $this->schemes[$scheme][$prefix]);
}
$this->schemes[$scheme][$prefix] = $list;
// Sort in reverse order to get longer prefixes to be matched first.
krsort($this->schemes[$scheme]);
}
/**
* @param $uri
* @return string|bool
*/
public function __invoke($uri)
{
return $this->find($uri, false, true);
}
/**
* @param string $uri
* @param bool $absolute
* @return string|bool
*/
public function findResource($uri, $absolute = true)
{
return $this->find($uri, false, $absolute);
}
/**
* @param string $uri
* @param bool $absolute
* @return array
*/
public function findResources($uri, $absolute = true)
{
return $this->find($uri, true, $absolute);
}
/**
* @param string $uri
* @param bool $absolute
* @param bool $array
*
* @throws \InvalidArgumentException
* @return array|string|bool
*/
protected function find($uri, $array, $absolute)
{
$segments = explode('://', $uri, 2);
$file = array_pop($segments);
$scheme = array_pop($segments);
if (!$scheme) {
$scheme = 'file';
}
if (!isset($this->schemes[$scheme])) {
throw new \InvalidArgumentException("Invalid resource {$scheme}://");
}
if (!$file && $scheme == 'file') {
$file = getcwd();
}
$results = $array ? [] : false;
foreach ($this->schemes[$scheme] as $prefix => $paths) {
if ($prefix && strpos($file, $prefix) !== 0) {
continue;
}
foreach ($paths as $path) {
$filename = $path . '/' . ltrim(substr($file, strlen($prefix)), '\/');
$lookup = ROOT_DIR . '/' . $filename;
if (file_exists($lookup)) {
if (!$array) {
return $absolute ? $lookup : $filename;
}
$results[] = $absolute ? $lookup : $filename;
}
}
}
return $results;
}
}

View File

@@ -1,71 +0,0 @@
<?php
namespace Grav\Component\Filesystem\StreamWrapper;
use Grav\Component\Filesystem\ResourceLocator;
class ReadOnlyStream extends Stream implements StreamInterface
{
/**
* @var ResourceLocator
*/
protected static $locator;
public function stream_open($uri, $mode, $options, &$opened_url)
{
if (!in_array($mode, ['r', 'rb', 'rt'])) {
if ($options & STREAM_REPORT_ERRORS) {
trigger_error('stream_open() write modes not supported for read-only stream wrappers', E_USER_WARNING);
}
return false;
}
$path = $this->getPath($uri);
if (!$path) {
return false;
}
$this->handle = ($options & STREAM_REPORT_ERRORS) ? fopen($path, $mode) : @fopen($path, $mode);
return (bool) $this->handle;
}
public function stream_lock($operation)
{
// Disallow exclusive lock or non-blocking lock requests
if (!in_array($operation, [LOCK_SH, LOCK_UN, LOCK_SH | LOCK_NB])) {
trigger_error(
'stream_lock() exclusive lock operations not supported for read-only stream wrappers',
E_USER_WARNING
);
return false;
}
return flock($this->handle, $operation);
}
public function stream_write($data)
{
throw new \BadMethodCallException('stream_write() not supported for read-only stream wrappers');
}
public function unlink($uri)
{
throw new \BadMethodCallException('unlink() not supported for read-only stream wrappers');
}
public function rename($from_uri, $to_uri)
{
throw new \BadMethodCallException('rename() not supported for read-only stream wrappers');
}
public function mkdir($uri, $mode, $options)
{
throw new \BadMethodCallException('mkdir() not supported for read-only stream wrappers');
}
public function rmdir($uri, $options)
{
throw new \BadMethodCallException('rmdir() not supported for read-only stream wrappers');
}
}

View File

@@ -1,232 +0,0 @@
<?php
namespace Grav\Component\Filesystem\StreamWrapper;
use Grav\Component\Filesystem\ResourceLocator;
class Stream implements StreamInterface
{
/**
* A generic resource handle.
*
* @var Resource
*/
protected $handle = null;
/**
* @var ResourceLocator
*/
protected static $locator;
/**
* @param ResourceLocator $locator
*/
public static function setLocator(ResourceLocator $locator)
{
static::$locator = $locator;
}
public function stream_open($uri, $mode, $options, &$opened_url)
{
$path = $this->getPath($uri, $mode);
if (!$path) {
return false;
}
$this->handle = ($options & STREAM_REPORT_ERRORS) ? fopen($path, $mode) : @fopen($path, $mode);
return (bool) $this->handle;
}
public function stream_close()
{
return fclose($this->handle);
}
public function stream_lock($operation)
{
if (in_array($operation, [LOCK_SH, LOCK_EX, LOCK_UN, LOCK_NB])) {
return flock($this->handle, $operation);
}
return false;
}
public function stream_metadata($uri, $option, $value)
{
switch ($option) {
case STREAM_META_TOUCH:
list ($time, $atime) = $value;
return touch($uri, $time, $atime);
case STREAM_META_OWNER_NAME:
case STREAM_META_OWNER:
return chown($uri, $value);
case STREAM_META_GROUP_NAME:
case STREAM_META_GROUP:
return chgrp($uri, $value);
case STREAM_META_ACCESS:
return chmod($uri, $value);
}
return false;
}
public function stream_read($count)
{
return fread($this->handle, $count);
}
public function stream_write($data)
{
return fwrite($this->handle, $data);
}
public function stream_eof()
{
return feof($this->handle);
}
public function stream_seek($offset, $whence)
{
// fseek returns 0 on success and -1 on a failure.
return !fseek($this->handle, $offset, $whence);
}
public function stream_flush()
{
return fflush($this->handle);
}
public function stream_tell()
{
return ftell($this->handle);
}
public function stream_stat()
{
return fstat($this->handle);
}
public function unlink($uri)
{
$path = $this->getPath($uri);
if (!$path) {
return false;
}
return unlink($path);
}
public function rename($fromUri, $toUri)
{
$fromPath = $this->getPath($fromUri);
$toPath = $this->getPath($toUri);
if (!($fromPath && $toPath)) {
return false;
}
return rename($fromPath, $toPath);
}
public function mkdir($uri, $mode, $options)
{
$recursive = (bool) ($options & STREAM_MKDIR_RECURSIVE);
$path = $this->getPath($uri, $recursive ? $mode : null);
if (!$path) {
return false;
}
return ($options & STREAM_REPORT_ERRORS) ? mkdir($path, $mode, $recursive) : @mkdir($path, $mode, $recursive);
}
public function rmdir($uri, $options)
{
$path = $this->getPath($uri);
if (!$path) {
return false;
}
return ($options & STREAM_REPORT_ERRORS) ? rmdir($path) : @rmdir($path);
}
public function url_stat($uri, $flags)
{
$path = $this->getPath($uri);
if (!$path) {
return false;
}
// Suppress warnings if requested or if the file or directory does not
// exist. This is consistent with PHP's plain filesystem stream wrapper.
return ($flags & STREAM_URL_STAT_QUIET || !file_exists($path)) ? @stat($path) : stat($path);
}
public function dir_opendir($uri, $options)
{
$path = $this->getPath($uri);
if (!$path) {
return false;
}
$this->handle = opendir($path);
return (bool) $this->handle;
}
public function dir_readdir()
{
return readdir($this->handle);
}
public function dir_rewinddir()
{
rewinddir($this->handle);
return true;
}
public function dir_closedir()
{
closedir($this->handle);
return true;
}
protected function getPath($uri, $mode = null)
{
$path = $this->findPath($uri);
if ($mode == null || !$path || file_exists($path)) {
return $path;
}
if ($mode[0] == 'r') {
return false;
}
// We are either opening a file or creating directory.
list($scheme, $target) = explode('://', $uri, 2);
$path = $this->findPath($scheme . '://' . dirname($target));
if (!$path) {
return false;
}
return $path . '/' . basename($uri);
}
protected function findPath($uri)
{
return static::$locator ? static::$locator->findResource($uri) : false;
}
}

View File

@@ -1,251 +0,0 @@
<?php
namespace Grav\Component\Filesystem\StreamWrapper;
/**
* Generic PHP stream wrapper interface.
*
* @see http://www.php.net/manual/class.streamwrapper.php
*/
interface StreamInterface
{
/**
* Support for fopen(), file_get_contents(), file_put_contents() etc.
*
* @param string $uri A string containing the URI to the file to open.
* @param string $mode The file mode ("r", "wb" etc.).
* @param int $options A bit mask of STREAM_USE_PATH and STREAM_REPORT_ERRORS.
* @param string $opened_url A string containing the path actually opened.
*
* @return bool Returns TRUE if file was opened successfully.
* @see http://php.net/manual/streamwrapper.stream-open.php
*/
public function stream_open($uri, $mode, $options, &$opened_url);
/**
* Support for fclose().
*
* @return bool TRUE if stream was successfully closed.
* @see http://php.net/manual/streamwrapper.stream-close.php
*/
public function stream_close();
/**
* Support for flock().
*
* @param $operation
* One of the following:
* - LOCK_SH to acquire a shared lock (reader).
* - LOCK_EX to acquire an exclusive lock (writer).
* - LOCK_UN to release a lock (shared or exclusive).
* - LOCK_NB if you don't want flock() to block while locking (not
* supported on Windows).
*
* @return bool Always returns TRUE at the present time.
* @see http://php.net/manual/streamwrapper.stream-lock.php
*/
public function stream_lock($operation);
/**
* Support for touch(), chmod(), chown(), chgrp().
*
* @param $path
* The file path or URL to set metadata. Note that in the case of a URL, it must be a :// delimited URL.
* Other URL forms are not supported.
*
* @param $option
* One of:
* - STREAM_META_TOUCH The method was called in response to touch()
* - STREAM_META_OWNER_NAME The method was called in response to chown() with string parameter
* - STREAM_META_OWNER The method was called in response to chown()
* - STREAM_META_GROUP_NAME The method was called in response to chgrp()
* - STREAM_META_GROUP The method was called in response to chgrp()
* - STREAM_META_ACCESS The method was called in response to chmod()
*
* @param $value
* If option is
* - STREAM_META_TOUCH: Array consisting of two arguments of the touch() function.
* - STREAM_META_OWNER_NAME or
* STREAM_META_GROUP_NAME: The name of the owner user/group as string.
* - STREAM_META_OWNER or
* STREAM_META_GROUP: The value owner user/group argument as integer.
* - STREAM_META_ACCESS: The argument of the chmod() as integer.
*
* @return bool
* @see http://php.net/manual/en/streamwrapper.stream-metadata.php
*/
public function stream_metadata($path, $option, $value);
/**
* Support for fread(), file_get_contents() etc.
*
* @param $count
* Maximum number of bytes to be read.
*
* @return string|bool The string that was read, or FALSE in case of an error.
* @see http://php.net/manual/streamwrapper.stream-read.php
*/
public function stream_read($count);
/**
* Support for fwrite(), file_put_contents() etc.
*
* @param $data
* The string to be written.
*
* @return int The number of bytes written (integer).
* @see http://php.net/manual/streamwrapper.stream-write.php
*/
public function stream_write($data);
/**
* Support for feof().
*
* @return bool TRUE if end-of-file has been reached.
* @see http://php.net/manual/streamwrapper.stream-eof.php
*/
public function stream_eof();
/**
* Support for fseek().
*
* @param $offset
* The byte offset to got to.
* @param $whence
* SEEK_SET, SEEK_CUR, or SEEK_END.
*
* @return bool TRUE on success.
* @see http://php.net/manual/streamwrapper.stream-seek.php
*/
public function stream_seek($offset, $whence);
/**
* Support for fflush().
*
* @return bool TRUE if data was successfully stored (or there was no data to store).
* @see http://php.net/manual/streamwrapper.stream-flush.php
*/
public function stream_flush();
/**
* Support for ftell().
*
* @return int The current offset in bytes from the beginning of file.
* @see http://php.net/manual/streamwrapper.stream-tell.php
*/
public function stream_tell();
/**
* Support for fstat().
*
* @return array An array with file status, or FALSE in case of an error - see fstat()
* @see http://php.net/manual/streamwrapper.stream-stat.php
*/
public function stream_stat();
/**
* Support for unlink().
*
* @param $uri
* A string containing the URI to the resource to delete.
*
* @return
* TRUE if resource was successfully deleted.
* @see http://php.net/manual/streamwrapper.unlink.php
*/
public function unlink($uri);
/**
* Support for rename().
*
* @param $from_uri ,
* The URI to the file to rename.
* @param $to_uri
* The new URI for file.
*
* @return bool TRUE if file was successfully renamed.
* @see http://php.net/manual/streamwrapper.rename.php
*/
public function rename($from_uri, $to_uri);
/**
* Support for mkdir().
*
* @param $uri
* A string containing the URI to the directory to create.
* @param $mode
* Permission flags - see mkdir().
* @param $options
* A bit mask of STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE.
*
* @return bool TRUE if directory was successfully created.
* @see http://php.net/manual/streamwrapper.mkdir.php
*/
public function mkdir($uri, $mode, $options);
/**
* Support for rmdir().
*
* @param $uri
* A string containing the URI to the directory to delete.
* @param $options
* A bit mask of STREAM_REPORT_ERRORS.
*
* @return
* TRUE if directory was successfully removed.
*
* @see http://php.net/manual/streamwrapper.rmdir.php
*/
public function rmdir($uri, $options);
/**
* Support for stat().
*
* @param $uri
* A string containing the URI to get information about.
* @param $flags
* A bit mask of STREAM_URL_STAT_LINK and STREAM_URL_STAT_QUIET.
*
* @return array An array with file status, or FALSE in case of an error - see fstat()
* @see http://php.net/manual/streamwrapper.url-stat.php
*/
public function url_stat($uri, $flags);
/**
* Support for opendir().
*
* @param $uri
* A string containing the URI to the directory to open.
* @param $options
* Unknown (parameter is not documented in PHP Manual).
*
* @return bool TRUE on success.
* @see http://php.net/manual/streamwrapper.dir-opendir.php
*/
public function dir_opendir($uri, $options);
/**
* Support for readdir().
*
* @return string The next filename, or FALSE if there are no more files in the directory.
* @see http://php.net/manual/streamwrapper.dir-readdir.php
*/
public function dir_readdir();
/**
* Support for rewinddir().
*
* @return bool TRUE on success.
* @see http://php.net/manual/streamwrapper.dir-rewinddir.php
*/
public function dir_rewinddir();
/**
* Support for closedir().
*
* @return bool TRUE on success.
* @see http://php.net/manual/streamwrapper.dir-closedir.php
*/
public function dir_closedir();
}

View File

@@ -1,5 +1,5 @@
<?php
namespace Grav\Console;
namespace Grav\Console\Cli;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
@@ -8,7 +8,7 @@ use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Helper\ProgressBar;
use \Symfony\Component\Yaml\Yaml;
use Symfony\Component\Yaml\Yaml;
class BackupCommand extends Command {
@@ -25,7 +25,7 @@ class BackupCommand extends Command {
)
->setDescription("Creates a backup of the Grav instance")
->setHelp('The <info>backup</info> creates a zipped backup');
->setHelp('The <info>backup</info> creates a zipped backup. Optionally can be saved in a different destination.');
$this->source = getcwd();

View File

@@ -1,6 +1,7 @@
<?php
namespace Grav\Console;
namespace Grav\Console\Cli;
use Grav\Common\Filesystem\Folder;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@@ -10,8 +11,6 @@ use Symfony\Component\Console\Formatter\OutputFormatterStyle;
class CleanCommand extends Command {
protected $destination_dir = 'distribution';
protected $paths_to_remove = [
'user/plugins/email/vendor/swiftmailer/swiftmailer/.travis.yml',
'user/plugins/email/vendor/swiftmailer/swiftmailer/build.xml',
@@ -48,7 +47,10 @@ class CleanCommand extends Command {
'vendor/erusev/parsedown/.git',
'vendor/erusev/parsedown/test',
'vendor/erusev/parsedown-extra/composer.json',
'vendor/erusev/parsedown-extra/phpunit.xml.dist',
'vendor/erusev/parsedown-extra/.travis.yml',
'vendor/erusev/parsedown-extra/.git',
'vendor/erusev/parsedown-extra/test',
'vendor/gregwar/image/Gregwar/Image/composer.json',
'vendor/gregwar/image/Gregwar/Image/phpunit.xml',
'vendor/gregwar/image/Gregwar/Image/.gitignore',
@@ -87,6 +89,12 @@ class CleanCommand extends Command {
'vendor/pimple/pimple/ext',
'vendor/pimple/pimple/phpunit.xml.dist',
'vendor/pimple/pimple/src/Pimple/Tests',
'vendor/rockettheme/toolbox/.git',
'vendor/rockettheme/toolbox/.gitignore',
'vendor/rockettheme/toolbox/.scrutinizer.yml',
'vendor/rockettheme/toolbox/.travis.yml',
'vendor/rockettheme/toolbox/composer.json',
'vendor/rockettheme/toolbox/phpunit.xml',
'vendor/symfony/console/Symfony/Component/Console/composer.json',
'vendor/symfony/console/Symfony/Component/Console/phpunit.xml.dist',
'vendor/symfony/console/Symfony/Component/Console/.gitignore',
@@ -153,7 +161,7 @@ class CleanCommand extends Command {
foreach($this->paths_to_remove as $path) {
$path = ROOT_DIR . $path;
if (is_dir($path) && @$this->rrmdir($path)) {
if (is_dir($path) && @Folder::delete($path)) {
$anything = true;
$output->writeln('<red>dir: </red>' . $path);
} elseif (is_file($path) && @unlink($path)) {
@@ -169,18 +177,4 @@ class CleanCommand extends Command {
}
// Recursively Delete folder - DANGEROUS! USE WITH CARE!!!!
private function rrmdir($dir) {
if (is_dir($dir)) {
$objects = scandir($dir);
foreach ($objects as $object) {
if ($object != "." && $object != "..") {
if (filetype($dir."/".$object) == "dir") $this->rrmdir($dir."/".$object); else unlink($dir."/".$object);
}
}
reset($objects);
rmdir($dir);
return true;
}
}
}

View File

@@ -1,6 +1,7 @@
<?php
namespace Grav\Console;
namespace Grav\Console\Cli;
use Grav\Common\Filesystem\Folder;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@@ -11,36 +12,42 @@ use \Symfony\Component\Yaml\Yaml;
class ClearCacheCommand extends Command {
protected $paths_to_remove = [
'cache',
'images',
'assets'
protected $standard_remove = [
'cache/twig/',
'cache/doctrine/',
'cache/compiled/',
'cache/validated-',
'images/',
'assets/',
];
protected $all_remove = [
'cache/',
'images/',
'assets/'
];
protected function configure() {
$this
->setName("clear-cache")
->setDescription("Clears Grav cache")
->addOption('all', null, InputOption::VALUE_NONE, 'If set will remove all')
->setHelp('The <info>clear-cache</info> deletes all cache files');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
// Create a red output option
$output->getFormatter()->setStyle('red', new OutputFormatterStyle('red'));
$output->getFormatter()->setStyle('cyan', new OutputFormatterStyle('cyan'));
$output->getFormatter()->setStyle('green', new OutputFormatterStyle('green'));
$output->getFormatter()->setStyle('magenta', new OutputFormatterStyle('magenta'));
$this->cleanPaths($output);
$this->cleanPaths($input, $output);
}
// loops over the array of paths and deletes the files/folders
private function cleanPaths($output)
private function cleanPaths($input, $output)
{
$output->writeln('');
$output->writeln('<magenta>Clearing cache</magenta>');
@@ -50,12 +57,23 @@ class ClearCacheCommand extends Command {
$anything = false;
foreach($this->paths_to_remove as $path) {
$files = glob(ROOT_DIR . rtrim($path, DS) . DS .'*');
if ($input->getOption('all')) {
$remove_paths = $this->all_remove;
} else {
$remove_paths = $this->standard_remove;
}
foreach($remove_paths as $path) {
$files = glob(ROOT_DIR . $path . '*');
foreach ($files as $file) {
if (is_file($file) && @unlink($file)) $anything = true;
elseif (is_dir($file) && @$this->rrmdir($file)) $anything = true;
if (is_file($file)) {
if (@unlink($file)) $anything = true;
}
elseif (is_dir($file)) {
if (@Folder::delete($file)) $anything = true;
}
}
if ($anything) $output->writeln('<red>Cleared: </red>' . $path . '*');
@@ -74,20 +92,5 @@ class ClearCacheCommand extends Command {
}
}
// Recursively Delete folder - DANGEROUS! USE WITH CARE!!!!
private function rrmdir($dir) {
if (is_dir($dir)) {
$objects = scandir($dir);
foreach ($objects as $object) {
if ($object != "." && $object != "..") {
if (filetype($dir."/".$object) == "dir") $this->rrmdir($dir."/".$object); else unlink($dir."/".$object);
}
}
reset($objects);
rmdir($dir);
return true;
}
}
}

View File

@@ -1,5 +1,5 @@
<?php
namespace Grav\Console;
namespace Grav\Console\Cli;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
@@ -31,8 +31,8 @@ class InstallCommand extends Command {
'Where to install the required bits (default to current project)'
)
->setDescription("Handles cloning and symlinking for Grav")
->setHelp('The <info>install</info> provides clone and symlink installation chores');
->setDescription("Installs the dependencies needed by Grav. Optionally can create symbolic links")
->setHelp('The <info>install</info> command installs the dependencies needed by Grav. Optionally can create symbolic links');
}
protected function execute(InputInterface $input, OutputInterface $output)
@@ -54,8 +54,7 @@ class InstallCommand extends Command {
if (file_exists($local_config_file)) {
$this->local_config = Yaml::parse($local_config_file);
$output->writeln('');
$output->writeln('read local config from <cyan>' . $local_config_file . '</cyan>');
$output->writeln('Read local config from <cyan>' . $local_config_file . '</cyan>');
}
// Look for dependencies file in ROOT and USER dir
@@ -67,6 +66,10 @@ class InstallCommand extends Command {
$output->writeln('<red>ERROR</red> Missing .dependencies file in <cyan>user/</cyan> folder');
}
// Updates composer first
$output->writeln("\nInstalling vendor dependencies");
$output->writeln(system('php bin/composer.phar --working-dir="'.$this->destination.'" --no-interaction update'));
// If yaml config, process
if ($this->config) {
if (!$input->getOption('symlink')) {
@@ -114,7 +117,7 @@ class InstallCommand extends Command {
if (!$this->local_config) {
$output->writeln('<red>No local configuration available, aborting...</red>');
$output->writeln('');
exit;
return;
}
exec('cd ' . $this->destination);

Some files were not shown because too many files have changed in this diff Show More