Author SHA1 Message Date
Michal Kváček f3b1951d25 FEATURE: cache most used SQL queries 2017-02-22 01:40:26 +01:00
Michal Kváček cad205960d Merge branch 'dobrotyzpece.cz' into dev 2017-02-22 01:17:29 +01:00
Michal Kváček 73e049e47d Merge branch 'binarity.cz' into refactoring 2017-02-22 01:16:42 +01:00
Michal Kváček 0ae3e4fe08 FEATURE: improve little bit update script 2017-02-11 01:24:28 +01:00
Michal Kváček 28addf5e33 FIX: fix typos 2017-02-10 14:20:49 +01:00
Michal Kváček d5a33b382e Merge branch 'refactoring' into binarity.cz 2017-02-10 14:01:49 +01:00
Michal Kváček 2fdec0a65f FIX: add dependencies into bower.json and package.json 2017-02-10 14:01:37 +01:00
Michal Kváček 6fb6aef199 Merge branch 'refactoring' into binarity.cz 2017-02-10 13:49:36 +01:00
Michal Kváček f69b8f39ad FEATURE: rename alias in database 2017-02-10 13:49:17 +01:00
Michal Kváček 35bda2828c FEATURE: merge template namespace and module alias 2017-02-10 13:45:41 +01:00
Michal Kváček bda659b910 FIX: fix article detail 2017-02-10 13:45:23 +01:00
Michal Kváček c8ec9c4eeb Merge branch 'refactoring' into binarity.cz 2017-02-10 11:41:40 +01:00
Michal Kváček 9451357e02 FIX: fix registering template paths 2017-02-10 11:41:17 +01:00
Michal Kváček 727e449ebc FIX: fix path to blog templates in binarity template 2017-02-10 11:41:03 +01:00
Michal Kváček b5425423d6 Merge branch 'refactoring' into binarity.cz 2017-02-10 02:49:20 +01:00
Michal Kváček 66e78aff6a FIX: fix content edit 2017-02-10 02:49:14 +01:00
Michal Kváček 497ff1b96d FIX: fix few minor bugs 2017-02-10 02:48:13 +01:00
Michal Kváček c429024a54 FIX: fix typo in template 2017-02-10 02:27:08 +01:00
Michal Kváček 0778a1d30c FIX: fix template handler according to refactoring 2017-02-10 02:24:38 +01:00
Michal Kváček 3748d00c2f FEATURE: hide user name when empty 2017-02-10 02:19:44 +01:00
Michal Kváček a525d9c085 REFACTORING: delete old admin assets 2017-02-10 02:16:35 +01:00
Michal Kváček dff8dcf565 FIX: fix typo in content search handler 2017-02-10 02:15:50 +01:00
Michal Kváček 0d22a14604 FEATURE: add scripts and styles into gulpfile 2017-02-10 02:13:03 +01:00
Michal Kváček 724478d0c9 Merge branch 'refactoring' into elixir 2017-02-09 23:42:23 +01:00
Michal Kváček 0ba0d1c15d FIX: fix few minor bugs 2017-02-09 23:42:07 +01:00
Michal Kváček 9593b6502e FIX: fix file manager 2017-02-09 23:25:21 +01:00
Michal Kváček e53283da65 FIX: fix logging 2017-02-09 23:25:08 +01:00
Michal Kváček f0d1cb8020 FIX: add middleware for queuing cookies 2017-02-09 23:24:50 +01:00
Michal Kváček 9076f81185 FEATURE: add widget for social buttons 2017-02-09 23:24:30 +01:00
Michal Kváček b7d4ab9515 FIX: lots of bugfixes, sorry for ugly commit 2017-02-09 23:10:11 +01:00
Michal Kváček 2b98c60d2a FIX #2228: change font from Lato to Open Sans 2017-02-03 01:32:57 +01:00
Michal Kváček 99ed8806da REFACTORING #2039 @1h: clean a little bit file manager 2016-11-28 01:46:05 +01:00
Michal Kváček d0fbce13fb REFACTORING #1880 @15m: remove custom fields 2016-11-23 03:21:23 +01:00
Michal Kváček 675aec669b REFACTORING #1880 @40m: move creating new directory into interaction 2016-11-23 01:30:28 +01:00
Michal Kváček ca718a5efe Merge branch 'dev' into refactoring 2016-11-23 01:05:22 +01:00
Michal Kváček 3aabb8fa7b REFACTORING #1880 @20m: move action log logic into repository 2016-11-23 01:03:37 +01:00
Michal Kváček 2fffea39fa FIX: fix content observer - ignore viewed 2016-11-23 00:49:55 +01:00
Michal Kvacek 7584491ff5 FIX: fix html widget 2016-11-21 11:16:44 +01:00
Michal Kvacek 84b2902613 FIX: fix one page menu widget 2016-11-21 11:13:39 +01:00
Michal Kvacek 2d6be0cb2e Merge branch 'dobrotyzpece.cz' of binarity.cz:/opt/git/internal/cms/minicms into dobrotyzpece.cz 2016-11-21 11:09:38 +01:00
Michal Kváček e6c02617e6 Merge branch 'dev' into dobrotyzpece.cz 2016-11-21 11:02:34 +01:00
root 0b56b41c3c Merge remote-tracking branch 'origin/dev' into dobrotyzpece.cz 2016-11-21 09:38:12 +01:00
Peter Hrdlica f89e11b5fb FEATURE: dobrotyzpece stylings 2016-11-03 22:06:55 +01:00
Michal Kváček 785168ecc2 FEATURE: somehow glued Elixir and SASS. Still not even nearly finished, still really experimental 2016-10-27 11:26:15 +02:00
Michal Kváček 9f430cee11 FEATURE #1933 @1h: add basic SASS structure and configure gulp/elixir 2016-10-26 03:31:20 +02:00
Michal Kváček 785387b796 FIX: fix gallery handler 2016-10-25 20:13:46 +02:00
Michal Kváček f940930c66 FEATURE: improve logging 2016-10-25 20:13:34 +02:00
Michal Kváček 8cb06bf641 REFACTORING #1880 @1h: move content actions into interactions 2016-10-25 20:13:12 +02:00
Michal Kváček 000352052d FEATURE: make nicer URL uniqueness check 2016-10-25 20:11:58 +02:00
Michal Kváček 988f153d9f FEATURE: add line breaks into diff 2016-10-25 19:55:07 +02:00
Michal Kváček 6dd452317b Merge branch 'dev' into refactoring 2016-10-25 19:16:18 +02:00
Michal Kváček 34feaac2ac FIX: fix editing menu widget 2016-10-25 18:04:07 +02:00
Michal Kváček e25f47a511 TESTING #1931 @1h: add few tests 2016-10-25 16:51:11 +02:00
Michal Kváček cc999dd24e REFACTORING #1880 @4h30m: another ugly refactoring commit 2016-10-25 16:50:04 +02:00
Michal Kváček 28f67ffe3c REFACTORING: another ugly commit full of refactored code 2016-10-19 00:28:44 +02:00
Michal Kváček 4f6a006ad9 FEATURE: add another repositories 2016-10-18 22:35:06 +02:00
Michal Kváček 662700b4f2 REFACTORING: move part of content business logic into repository 2016-10-18 15:36:26 +02:00
Michal Kváček eabbf48ee0 REFACTORING: move content update and create logic into separate interactions 2016-10-17 02:52:30 +02:00
Michal Kváček 6ec692bd1c FIX: fix uploading file without directory 2016-10-17 01:10:04 +02:00
Michal Kváček aa0ef7ccc6 REFACTORING: remove gravatar from user model 2016-10-17 01:07:25 +02:00
Michal Kváček e26e6fd460 FEATURE: move bussiness logic out of models 2016-10-17 01:05:20 +02:00
Michal Kváček 2ded2924a1 FEATURE: change directory structure 2016-10-17 00:07:21 +02:00
Michal Kváček 87f1920373 FEATURE: temporarily display large image as thumbnail in reference detail 2016-10-13 01:14:24 +02:00
Michal Kváček b7f911c9b8 FIX: fix encoding in translations 2016-10-13 01:06:08 +02:00
Michal Kváček 84aea5c847 FEATURE: add missing czech translations 2016-10-13 00:58:04 +02:00
Michal Kváček 98fc287f10 FIX: remove comments overview from submenu 2016-10-12 21:24:27 +02:00
Michal Kváček 686edf1c6f FIX: fix error on comments overview when article was deleted 2016-10-12 21:22:28 +02:00
Michal Kváček deb094b5c9 FEATURE: add uploader into CKEditor 2016-10-12 18:22:12 +02:00
Michal Kváček d425133cae REFACTORING: refactor module service providers to reduce amoount of duplicated code 2016-10-12 17:38:06 +02:00
Michal Kváček 9b6ae9f9f8 FIX: fix saving published_from and published_to fields 2016-10-12 17:15:58 +02:00
Michal Kváček 4a95a9692d FIX: fix saving new widget without container ID 2016-10-12 16:33:07 +02:00
Michal Kváček 06439bc9a0 Merge branch 'dev' into dobrotyzpece.cz 2016-10-12 16:09:44 +02:00
Michal Kváček 12683df0ab FIX: fix error when rendering not-existing widget area 2016-10-12 16:09:39 +02:00
Michal Kváček 0cb53b2d80 FEATURE: add template for widget with contact form 2016-10-12 16:01:05 +02:00
Michal Kváček 5fd9b1871f FIX: fix editing widget with container ID 2016-10-12 16:00:39 +02:00
Michal Kváček fb51ae80ac FEATURE: add widget for displaying contact form 2016-10-12 15:36:24 +02:00
Michal Kváček b9d141c4f1 FEATURE: add widget with address block 2016-10-12 15:12:42 +02:00
Michal Kváček af4edac469 FEATURE: json_decode widget settings before calling handler 2016-10-12 15:12:18 +02:00
Michal Kváček 0451f9942c FIX: fix error when trying to render widget preview with json_decoded settings 2016-10-12 15:11:55 +02:00
Michal Kváček 657c030033 DOC: add phpdoc 2016-10-12 15:11:29 +02:00
Michal Kváček d761914786 Merge branch 'dev' into binarity.cz 2016-10-11 19:12:18 +02:00
Michal Kváček 625bdbb2e2 FEATURE: add functionality for rendering widgets via shortcode [widget] 2016-10-11 19:10:03 +02:00
Michal Kváček d5f9e1fc2b Merge branch 'dobrotyzpece.cz' into dev 2016-10-11 16:38:05 +02:00
Michal Kváček ce058ae824 FEATURE: include commont parts of widget forms 2016-10-11 16:24:16 +02:00
Michal Kváček dee5060a24 FEATURE: add unique code to widgets 2016-10-11 16:16:16 +02:00
Michal Kváček c70e8a6b0d FEATURE: minor changes in template for dobrotyzpece.cz 2016-10-11 15:57:29 +02:00
Michal Kváček cb9b96c861 FIX: fix one page menu 2016-10-11 15:40:42 +02:00
Michal Kváček 4bd5a61fde FEATURE: add artisan shortcut for publishing assets and views 2016-10-11 15:32:06 +02:00
Michal Kváček 46ff310b01 FIX: fix meta information in header 2016-10-10 23:42:23 +02:00
Michal Kváček 96b05a7b25 FEATURE: add meta robots into head 2016-10-10 23:40:21 +02:00
Michal Kváček 563528cdc6 FEATURE: add meta author into head 2016-10-10 23:37:10 +02:00
Michal Kváček efa3289491 FEATURE: make list of directories nicer 2016-10-10 23:19:13 +02:00
Michal Kváček 3460f607b5 FEATURE: make history overview nicer 2016-10-10 23:09:38 +02:00
Michal Kváček 0384fab421 FIX: fix published_from and published_to 2016-10-10 21:45:31 +02:00
Michal Kváček b3ac9b9eec Merge branch 'binarity.cz' of binarity.cz:/opt/git/internal/cms/minicms into binarity.cz 2016-10-10 21:42:03 +02:00
Michal Kváček bfa0685f2e FIX: display only published pages - respect published_* settings 2016-10-10 21:41:32 +02:00
Peter Hrdlica 59226dbdc4 FEATURE: more changes to article and article list 2016-10-10 21:36:55 +02:00
Peter Hrdlica 446e8352b5 FEATURE: changed graphics of articles 2016-10-10 21:04:25 +02:00
Michal Kváček 190ca25b2a FIX: fix published_from and published_to fields 2016-10-10 20:31:59 +02:00
Michal Kváček f121ee356b Merge branch 'dobrotyzpece.cz' into binarity.cz 2016-10-10 20:25:17 +02:00
Michal Kváček b3d59fc182 FIX: fix date in articles 2016-10-10 20:19:00 +02:00
Michal Kváček 20f6295f84 "FEATURE: add categories into article detail and article list"
This reverts commit 08e97fe2cb.
2016-10-10 20:15:10 +02:00
Michal Kváček 2c36203690 FEATURE: check translation for lockUrl 2016-10-10 20:13:03 +02:00
Michal Kváček 458d8ec61c FIX: fix translations for template paths in blog 2016-10-10 19:08:59 +02:00
Michal Kváček 284dfa3fe8 Merge branch 'dev' into binarity.cz 2016-10-10 19:03:55 +02:00
Michal Kváček 08e97fe2cb Revert "FEATURE: add categories into article detail and article list"
This reverts commit bceef69a72.
2016-10-10 19:03:32 +02:00
Michal Kváček 7e63047313 FEATURE: swap created_at and published_from in template for blog 2016-10-10 19:00:42 +02:00
Michal Kváček d9834fe068 REFACTORING: split ArticleList::render into few smaller methods 2016-10-10 18:29:37 +02:00
Michal Kváček dc43a9503d FEATURE: add basic template for dobrotyzpece.cz 2016-10-10 01:25:36 +02:00
Michal Kváček a75425ca12 Merge branch 'dev' into dobrotyzpece.cz 2016-10-09 23:20:13 +02:00
Michal Kváček 8c20654280 Merge branch 'dev' into dobrotyzpece.cz 2016-10-09 23:18:11 +02:00
Michal Kváček bceef69a72 FEATURE: add categories into article detail and article list 2016-10-07 10:32:46 +02:00
Michal Kváček 1e310f0da5 FIX: add with methods to selecting comments and content pages 2016-10-04 13:14:21 +02:00
Michal Kváček ab90b907f8 FIX: hide protected pages from menu widget 2016-10-04 13:09:32 +02:00
Michal Kváček f1aacf8008 FEATURE: style little bit comments 2016-10-04 13:04:24 +02:00
Michal Kváček ce82b67c47 FEATURE: hide comments when article does not allow them 2016-10-04 12:58:42 +02:00
Michal Kváček 3024ae8b23 FEATURE: turn website off and on during update 2016-10-04 12:40:31 +02:00
Michal Kváček 7dcb8e280c FIX: fix exception when accessing protected page 2016-10-04 11:33:38 +02:00
Michal Kváček f9dfe076a0 Merge branch 'dev' into binarity.cz 2016-10-04 11:27:55 +02:00
Michal Kváček db88079f26 FIX: fix template loading for article list 2016-10-04 11:27:49 +02:00
Michal Kváček 3f1ae0f5a1 Merge branch 'dev' into binarity.cz 2016-10-04 11:16:59 +02:00
Michal Kváček f699d2c569 FIX: fix minor bugs in blog module 2016-10-04 11:16:04 +02:00
Michal Kváček fdb63fcd8a FEATURE: minor updates in binarity template 2016-10-04 11:14:17 +02:00
Michal Kváček 52fb27b4fe FEATURE: style comments and comments form 2016-10-04 06:59:46 +02:00
Michal Kváček 7234fa0810 Merge branch 'dev' into binarity.cz 2016-10-04 00:44:40 +02:00
Michal Kváček 581e32e98b FEATURE: add new helper Date 2016-10-04 00:44:36 +02:00
Michal Kváček 06780015d4 FEATURE: create model for articles 2016-10-04 00:44:24 +02:00
Michal Kváček 35bdd38f4c FEATURE: add new macros for date manipulation 2016-10-04 00:43:59 +02:00
Michal Kváček eacba1bec3 FIX: fix logging creating comments 2016-10-04 00:43:28 +02:00
Michal Kváček 975cc50272 FIX: fix routes namespaces 2016-10-04 00:43:04 +02:00
Michal Kváček 52b0ec7c72 FEATURE: add template for blog 2016-10-04 00:42:20 +02:00
Michal Kváček de02944d0d FIX: check if method is callable before calling it 2016-10-03 03:09:04 +02:00
Michal Kváček da1c8652e6 FEATURE: prepare support for blog 2016-10-03 03:04:03 +02:00
Michal Kváček 012ba1fd36 Merge branch 'dev' into binarity.cz 2016-10-03 02:41:57 +02:00
Michal Kváček cbfc5fcee5 DELETE: do some cleanup of repository 2016-10-03 02:37:19 +02:00
Michal Kváček 232e1d5ec9 Merge branch 'dev' into binarity.cz 2016-10-03 01:40:09 +02:00
Michal Kváček aca3c778e0 FEATURE: add same height to all templates 2016-10-03 01:40:04 +02:00
Michal Kváček 88a2463d13 FIX: render menu link to index when page is set as homepage 2016-10-03 01:36:52 +02:00
Michal Kváček f3e797a59a FIX: minor fixes in styles 2016-10-03 01:25:11 +02:00
Michal Kváček 0869ac7c24 Merge branch 'dev' into binarity.cz 2016-10-03 01:09:53 +02:00
Michal Kváček 304518fbe7 FEATURE: make bigger line-height in .off-canvas-content div 2016-10-03 01:09:51 +02:00
Michal Kváček 49577a7e1a FEATURE: add warning before leaving form with unsaved changes 2016-10-03 01:04:31 +02:00
Michal Kváček 606ee2016d FEATURE: add translations for emails dir in contact form templates 2016-10-03 00:34:07 +02:00
Michal Kváček 8608a2d67b Merge branch 'dev' into binarity.cz 2016-10-03 00:07:50 +02:00
Michal Kváček 3bac8dc09b FEATURE: add missing translations 2016-10-03 00:07:46 +02:00
Michal Kváček f61b8a7bcc Merge branch 'dev' into binarity.cz 2016-10-02 23:58:55 +02:00
Michal Kváček b6d3daaa35 FEATURE: add settings for linkedIn profile 2016-10-02 23:58:50 +02:00
Michal Kváček 33a928febb Merge branch 'dev' into binarity.cz 2016-10-02 23:33:53 +02:00
Michal Kváček 06c84867ec FEATURE: add error pages 2016-10-02 23:33:43 +02:00
Michal Kváček 8a9543cd2d FIX: fix gravatar http/https path 2016-10-02 23:11:30 +02:00
Michal Kváček 4f67b69984 FIX: fix creating new menu 2016-10-02 22:41:20 +02:00
Michal Kváček 28df8681e4 FEATURE: change default image for og:image 2016-10-02 22:21:49 +02:00
Michal Kváček 4222dbc243 Merge branch 'dev' into doucovanineratovice.cz 2016-10-02 22:16:33 +02:00
Michal Kváček 2f27e7f5c9 Merge branch 'dev' into binarity.cz 2016-10-02 22:16:21 +02:00
Michal Kváček c5fb951546 FIX: fix warning in update script 2016-10-02 22:16:16 +02:00
Michal Kváček ecd7aa27f0 Merge branch 'dev' into binarity.cz 2016-10-02 22:14:48 +02:00
Michal Kváček ecd01e1396 FIX: fix saving menu widget 2016-10-02 22:14:41 +02:00
Michal Kváček 76b226629f FEATURE: display references in descending order 2016-10-02 22:14:28 +02:00
Michal Kváček 97fae9bf9d FIX: fix selecting pages - exclude pages from disabled modules 2016-10-02 21:58:56 +02:00
Michal Kváček 5621b5acba FEATURE: add error message and meta information into contact form template 2016-10-02 21:24:51 +02:00
Michal Kváček ff237599a8 FEATURE: add meta information to all static pages 2016-10-02 21:22:47 +02:00
Michal Kváček 679ca91fc9 FEATURE: make homepage and default static page templates HTML5 valid 2016-10-02 21:16:41 +02:00
Michal Kváček 1dea234cb7 Merge branch 'dev' into binarity.cz 2016-10-02 20:47:27 +02:00
Michal Kváček e42d06904e FEATURE: add current URL into all views 2016-10-02 20:47:22 +02:00
Michal Kváček cb2fa84ade FEATURE: few mostly minor fixes in layout 2016-10-02 20:47:00 +02:00
Michal Kváček 0a0b0e7518 Merge branch 'dev' into binarity.cz 2016-10-02 20:16:06 +02:00
Michal Kváček 65dc4fd37e FIX: allow using same url after page was deleted 2016-10-02 20:15:02 +02:00
Michal Kváček b13f6bbea9 Merge branch 'dev' into binarity.cz 2016-10-02 20:03:23 +02:00
Michal Kváček 10e463b8b4 Merge branch 'dev' into doucovanineratovice.cz 2016-10-02 20:03:16 +02:00
Michal Kváček 316f7a9a16 FIX: fix sending status after sending e-mail from contact form 2016-10-02 20:03:09 +02:00
Michal Kváček 5e79d0208a FEATURE: add error message to contact form 2016-10-02 20:02:49 +02:00
Michal Kváček 5cdca60cce FIX: fix success message after sending e-mail 2016-10-02 20:00:05 +02:00
Michal Kváček 86a2d277f6 FEATURE: change colors 2016-10-02 19:51:00 +02:00
Michal Kváček ad59bc0adc FEATURE: change color of title page and navbar 2016-10-02 19:01:20 +02:00
Michal Kváček 556d863b45 FEATURE: add page title into tab header 2016-10-02 18:46:33 +02:00
Michal Kváček 673531ae22 FEATURE: add missing templates 2016-10-02 16:28:37 +02:00
Michal Kváček a9d5c0de28 Merge branch 'dev' into binarity.cz 2016-10-02 16:28:24 +02:00
Michal Kváček b43e37fa83 FEATURE: add support for shortcodes 2016-10-02 16:28:12 +02:00
Michal Kváček 6c4b30f1f7 FETURE: add default template for static pages 2016-10-02 15:14:18 +02:00
Michal Kváček c4a4354aff Merge branch 'dev' into binarity.cz 2016-10-02 14:49:43 +02:00
Michal Kváček ad77de0424 Merge branch 'dev' into binarity.cz 2016-10-02 14:49:13 +02:00
Michal Kváček c4d4ac2bfe FEATURE: add notification after sending mail from contact form 2016-10-02 14:46:54 +02:00
Michal Kváček 0718772740 FIX: delete mainMenu widget area when uninstalling template 2016-10-02 14:31:57 +02:00
Michal Kváček 0ace8c9cb6 FIX: fix template installation 2016-10-02 14:31:24 +02:00
Michal Kváček 0482121441 Merge branch 'dev' of binarity.cz:/opt/git/internal/cms/minicms into dev 2016-10-02 13:50:33 +02:00
Michal Kvacek 0df8eaa2e3 Merge branch 'master' into dev 2016-10-01 22:06:13 +02:00
Michal Kvacek 66a570bbf7 FIX fix redirect after sending message 2016-10-01 22:05:17 +02:00
Michal Kváček 0badc52f17 Merge branch 'dev' into binarity.cz 2016-10-01 14:18:10 +02:00
Peter Hrdlica 4cf1fedc61 FEATURE: more translations 2016-09-29 23:11:42 +02:00
Peter Hrdlica 341b5dc9ec FEATURE: translations 2016-09-29 23:04:26 +02:00
Michal Kváček f5e2aec818 REFORMATTING: reformatted whitespaces in contactform template 2016-09-29 00:13:02 +02:00
Michal Kváček 77a65b71a2 FIX: remove duplicated CSS rules 2016-09-29 00:11:40 +02:00
Michal Kváček 5f1cf06e36 FEATURE: add template for contact 2016-09-28 23:53:43 +02:00
Michal Kváček b734c3de18 FEATURE: add 'load more references' button into reference list 2016-09-28 23:27:59 +02:00
Michal Kváček 808bb0eb86 FEATURE: add template for reference detail 2016-09-28 23:07:34 +02:00
Michal Kváček 682f3b75bf FEATURE: add binarity template - currently only homepage 2016-09-28 22:12:58 +02:00
Michal Kváček 5b9b5c0d38 FIX: fix template path registrator 2016-09-28 00:14:18 +02:00
Michal Kváček 3a0a132701 FEATURE: add tag to homepage in overview 2016-09-27 23:39:44 +02:00
Michal Kváček 60c3582947 FEATURE: add filter to action log 2016-09-27 23:24:03 +02:00
Michal Kváček 9039334138 FEATURE: add widgets for assigning into area into edit form 2016-09-27 23:13:21 +02:00
Michal Kváček 5ee403ab28 FEATURE: hide language selection when only one language is installed 2016-09-27 23:05:09 +02:00
Michal Kváček fdf44e8b4a Merge branch 'dev' into doucovanineratovice.cz 2016-09-27 22:52:57 +02:00
Michal Kváček 91cdbec3af FIX: fix template install and other minor bugs 2016-09-27 22:52:55 +02:00
Michal Kváček 45354a3192 FEATURE: move installing widget into base template 2016-09-27 22:52:40 +02:00
Michal Kváček 846cfef1b8 FEATURE: add reset() method to CurrentTemplate helper 2016-09-27 22:27:53 +02:00
Michal Kváček 80dba6d9b7 FEATURE: add missing czech translations 2016-09-27 22:10:33 +02:00
Michal Kváček bf615f8494 FIX: fix contact e-mail saving in contact form 2016-09-27 22:08:24 +02:00
Michal Kváček 4c67d442d6 FIX: fix searching in name and overflowin of no-data message 2016-09-27 22:04:09 +02:00
Michal Kváček 36615aac21 FEATURE: add missing translation for actionLog.createUser 2016-09-27 22:00:42 +02:00
Michal Kváček 4e6fc7c074 FEATURE: add translatable titles to static pages and contact form modules 2016-09-27 21:57:25 +02:00
Michal Kváček e0357d0003 FEATURE: fix redirecting after edit to be more logical 2016-09-27 21:48:06 +02:00
Michal Kváček 8533c8cdc6 FEATURE: make template not mandatory in content editor 2016-09-27 21:34:39 +02:00
Michal Kváček bb3d000e5e FEATURE: display page status as tag in overview 2016-09-27 21:31:32 +02:00
Michal Kváček 842b54d879 FEATURE: add missing czech translations 2016-09-26 21:29:51 +02:00
Michal Kváček 256e24c5a4 FIX: fix typo in Blade macro 2016-09-26 21:01:05 +02:00
Michal Kváček 07df49c481 FEATURE: use thumbnails in content overview and Appearance tab 2016-09-26 20:58:59 +02:00
Michal Kváček a11a29de19 Merge branch 'doucovanineratovice.cz' into dev 2016-09-26 16:54:34 +02:00
Michal Kváček 044eb7e532 FIX: fix order of pages on homepage 2016-09-26 16:54:27 +02:00
Michal Kváček 7f93a5035c FIX: register also /resources/views/visitor 2016-09-26 16:33:43 +02:00
Michal Kváček 358027a8e0 FIX: fix duplicate :: in template names 2016-09-26 16:31:49 +02:00
Michal Kváček 98fe43e12c FIX: hopefully fix broken foreign keys management in migrations 2016-09-26 16:20:22 +02:00
Michal Kváček c096a30b81 Merge branch 'dev' into doucovanineratovice.cz 2016-09-26 15:36:07 +02:00
Michal Kváček 02157f332e FIX: remove on update current_timestamp from content column updated_at 2016-09-26 15:29:49 +02:00
Michal Kváček cd28625309 FEATURE: make thumbnails from uploaded image 2016-09-26 15:21:30 +02:00
Michal Kváček 6868af609f FIX: fix removing image after resaving page 2016-09-26 14:20:06 +02:00
Michal Kváček 445f369ff2 FEATURE: add ViewerJS to preview pdf and odf files in file manager 2016-09-26 12:17:53 +02:00
Michal Kváček 2a1ffb4259 FIX: fix page colors 2016-09-26 11:38:29 +02:00
Michal Kváček 4876cb631e FEATURE: make file overview nicer 2016-09-25 17:33:58 +02:00
Michal Kváček 4c35d95a73 FEATURE: simplify routes 2016-09-25 13:48:12 +02:00
Michal Kváček 8f4e6268a4 FEATURE: move widget and template dir installation into middleware, optimize middleware groups 2016-09-25 12:42:26 +02:00
Michal Kváček d7b60690b2 FEATURE: finish template 2016-09-25 02:52:20 +02:00
Michal Kváček 4de93f9fd9 FEATURE: add basics of template 2016-09-25 02:02:59 +02:00
Michal Kváček f26c384257 Merge branch 'template_support' into doucovanineratovice.cz 2016-09-25 02:01:43 +02:00
Michal Kváček 78213e6f41 Merge branch 'dev' into template_support 2016-09-25 02:01:31 +02:00
Michal Kváček a188c707d8 FEATURE: ignore errors on pages during rendering, display error message for logged user 2016-09-25 02:01:24 +02:00
Michal Kváček ff4585327e FEATURE: add helper function asset_path() 2016-09-25 02:00:50 +02:00
Michal Kváček fa1d302ea6 Merge branch 'dev' into doucovanineratovice.cz 2016-09-25 02:00:18 +02:00
Michal Kváček 52394c6289 FEATURE: add widget association in new admin layout - hopefully last try of doing this 2016-09-25 01:57:45 +02:00
Michal Kváček 76fa76a5af Merge branch 'template_support' into dev 2016-09-25 01:37:24 +02:00
Michal Kváček d6a441cb96 FEATURE: add template_id and module_id into in WidgetType model 2016-09-25 01:36:18 +02:00
Michal Kváček 85bbc384a7 FEATURE: add getJson method to Settings helper 2016-09-25 01:35:52 +02:00
Michal Kváček 80105c857e FEATURE: extend registering widgets by setting template and module ID 2016-09-25 01:35:39 +02:00
Michal Kváček 09151e97d1 FEATURE: prepare widget for displaying menu on onepage templates 2016-09-25 01:35:03 +02:00
Michal Kváček 9802161af1 FIX: add deletable fields into in widgets and widget area models 2016-09-25 00:21:18 +02:00
Michal Kváček 00d7bd562c FEATURE: make user id not mandatory in widgets table 2016-09-25 00:19:20 +02:00
Michal Kváček 124ad2f061 Merge branch 'template_support' into doucovanineratovice.cz 2016-09-24 23:00:57 +02:00
Michal Kváček a37767c2e8 Merge branch 'dev' into template_support 2016-09-24 23:00:44 +02:00
Michal Kváček d5950d9cd7 FEATURE: add removing settings into uninstall method for template 2016-09-24 20:29:07 +02:00
Michal Kváček 82283e65be FEATURE: add translations for comment logged events 2016-09-24 20:19:21 +02:00
Michal Kváček 2f760fd56d FEATURE: minor UI tweaks in template settings + add events into OnePage base template 2016-09-24 19:48:39 +02:00
Michal Kváček 939610cf9d FIX: fix homepage settings for one-page template when no template is set 2016-09-24 03:23:35 +02:00
Michal Kváček 6d107d0881 FEATURE: add customizable settings for create/edit/overview of content pages 2016-09-24 02:55:51 +02:00
Michal Kváček b7434d0a0b FIX: fix translating submodules 2016-09-24 02:55:01 +02:00
Michal Kváček 55ef5e4af1 FEATURE: add testing template files into testing template Jednostrankovka 2016-09-24 02:10:10 +02:00
Michal Kváček dc289723a9 FIX: fix contact form edit 2016-09-24 02:09:07 +02:00
Michal Kváček b15bbae037 FEATURE: add base support for displaying multiple pages on one-page template 2016-09-24 01:53:43 +02:00
Michal Kváček 7cd084e621 FIX: fix setting homepage pages - omitted file for one of previous commits 2016-09-24 00:00:30 +02:00
Michal Kváček 0857b4efd9 FEATURE: add template parameters into rendering page in visitor area 2016-09-23 23:59:39 +02:00
Michal Kváček 0c01b35188 FEATURE: make snippet for bootstrap cards 2016-09-23 23:58:59 +02:00
Michal Kváček 9d33ae6d49 FEATURE: move default Handler::render() into parent class 2016-09-23 23:57:23 +02:00
Michal Kváček a9aaf797cd FEATURE: add few czech translations 2016-09-23 23:41:22 +02:00
Michal Kváček 4b6f0b14ad FEATURE: make shortcut for getting currently installed template 2016-09-23 23:28:04 +02:00
Michal Kváček a111b67c53 DOC: add link to improvement 2016-09-23 22:55:18 +02:00
Michal Kváček 91ef424bd0 FEATURE: extend translation command for translating also templates 2016-09-23 22:54:46 +02:00
Michal Kváček 7589051216 FEATURE: add homepage settings for base of one page pages 2016-09-23 22:27:58 +02:00
Michal Kváček fbb59277b9 FEATURE: add events to comments 2016-09-23 00:41:38 +02:00
Michal Kváček 0d8e927b08 FIX: fix localization in categories 2016-09-23 00:08:53 +02:00
Michal Kváček 72f7b9a047 FIX: fix localization of comments 2016-09-23 00:02:25 +02:00
Michal Kváček 139373145a FEATURE: split controllers/admin into more smaller directories 2016-09-22 23:35:53 +02:00
Michal Kváček 0357417163 FEATURE: add soft deletes to users 2016-09-22 22:13:15 +02:00
Michal Kváček d5d4216a80 FEATURE: add soft deletes to content pages 2016-09-22 21:57:45 +02:00
Michal Kváček b982831fbf FEATURE: make settings accessible only using helper class/function 2016-09-22 21:09:48 +02:00
Peter Hrdlica 75c4a4a649 #1573: added full slovakian localization, some fixes in czech one. 2016-09-21 15:59:35 +02:00
Michal Kváček fb977ce1b9 FEATURE: few mostly minor UI tweaks in admin 2016-09-21 01:15:32 +02:00
Michal Kváček 7d051d1f6a FEATURE: make templates not-editable 2016-09-21 00:58:03 +02:00
Michal Kváček f5e1e24df8 FIX: minor fixed in chmod 2016-09-20 23:44:11 +02:00
Michal Kváček 5ddb31b57b FIX: do not prevent running ModuleManager in CLI mode 2016-09-20 23:37:32 +02:00
Michal Kváček 01a28fc65a FIX: minor fixes in installation 2016-09-20 23:25:45 +02:00
Michal Kváček 67cd7fb36f FEATURE: make installation working again 2016-09-20 23:06:42 +02:00
Michal Kváček 5f8dab0068 FIX: remove Article module from template 2016-09-20 20:59:47 +02:00
Michal Kváček 40949eaa90 FIX: fix update from migrations 2016-09-20 20:58:52 +02:00
Michal Kváček f25ef4ab1b Merge branch 'template_support' into dev 2016-09-20 20:17:06 +02:00
Michal Kváček f3873ed61c FEATURE: add functionality for modifying content page on template level 2016-09-20 20:14:02 +02:00
Michal Kváček bbffbd5616 Merge branch 'module_blog' into dev 2016-09-20 15:34:44 +02:00
Michal Kváček d9feb92612 FEATURE: add template into page rendering 2016-09-20 15:25:18 +02:00
Michal Kváček 5662dc7cf7 FIX: fix determination of invalid template 2016-09-20 15:23:07 +02:00
Michal Kváček 39279a1633 FEATURE: delete all template paths from template_path table on template uninstall 2016-09-20 14:54:43 +02:00
Michal Kváček e8b91db6f2 FEATURE: add template check on content overview 2016-09-20 14:47:38 +02:00
Michal Kváček df9ff7b259 REFACTORING: reformatt code 2016-09-20 14:30:11 +02:00
Michal Kváček a0f3409f82 FEATURE: make widgets not deletable 2016-09-20 14:24:40 +02:00
Michal Kváček 694724945d FEATURE: make widget areas not deletable 2016-09-20 14:16:33 +02:00
Michal Kváček 5cbe19064b FEATURE: delete commented-out sections with template registration 2016-09-20 12:09:37 +02:00
Michal Kváček 4ac084139c DELETE: delete old admin templates 2016-09-20 11:57:17 +02:00
Michal Kváček f92c1d0089 FEATURE: simplify template handler - move un/install and registerAdminMenu into base class 2016-09-20 11:54:23 +02:00
Michal Kváček bcd19058ea DELETE: remove views/visitor/cwbs -- will be converted into template 2016-09-20 11:51:16 +02:00
Michal Kváček d43073a011 FEATURE: add blade template registration into templates 2016-09-20 11:50:41 +02:00
Michal Kváček 8bd30d0301 FEATURE: add number of views in page 2016-09-20 01:04:23 +02:00
Michal Kváček f087da1b43 FEATURE: prepare for installing templates 2016-09-20 00:46:32 +02:00
Michal Kváček 7a681fd7f7 FEATURE: remove modularAdmin logo from login and layout 2016-09-19 20:43:23 +02:00
Michal Kváček 2e483c7598 FEATURE: add html template 2016-09-19 20:35:59 +02:00
Michal Kváček f98065e849 FEATURE: delete unnecessary templates 2016-09-17 19:47:46 +02:00
Michal Kváček 48294842bf FEATURE: rewrite widgets management into new layout 2016-09-15 23:52:01 +02:00
Michal Kváček 65e9ca1c32 FEATURE: minor change in icon (remove->trash) 2016-09-14 20:29:25 +02:00
Peter Hrdlica 233f2007b0 Initial Dobroty z pece 2016-09-14 09:34:57 +02:00
Michal Kváček 5b29082ca6 Merge branch 'dev' into module_blog 2016-09-13 03:53:29 +02:00
Michal Kváček 48c0abbe89 FEATURE: add few czech translations 2016-09-13 03:52:44 +02:00
Michal Kváček 8476d5fbd7 FEATURE: add working confirm dialog 2016-09-13 03:51:50 +02:00
Michal Kváček ea5aa8d873 FEATURE: make content pages list responsive 2016-09-13 03:51:34 +02:00
Michal Kváček 3229a84ee5 FEATURE: make user list vertical 2016-09-13 02:57:42 +02:00
Michal Kváček f8127f3ed6 FEATURE: add article list -- advanced options 2016-09-07 02:21:30 +02:00
Michal Kváček 44677aea78 FEATURE: add basic article list 2016-09-07 00:47:57 +02:00
Michal Kváček 5b7b81ff4d FIX: remove echo from TemplateRegistrator 2016-09-07 00:28:03 +02:00
Michal Kváček 68d37e87f6 FIX: fix module installator to skip Database directory in subdmodules 2016-09-07 00:06:44 +02:00
Michal Kváček 51d6850cd6 FEATURE: add selecting comments into template in visitors area 2016-09-06 12:47:54 +02:00
Michal Kváček 8f66a96ec0 FEATURE: add comments management 2016-09-05 16:05:14 +02:00
Michal Kváček 9ce9f62a45 FEATURE: add creating comments 2016-09-05 12:50:04 +02:00
Michal Kváček a33023a4aa FEATURE: remove not used templates 2016-09-05 12:49:44 +02:00
Michal Kváček d2d0a48e9c FEATURE: add czech translation to article submodule 2016-09-05 00:10:46 +02:00
Michal Kváček e55672bdcd FEATURE: support submodules in translation helper command 2016-09-05 00:10:31 +02:00
Michal Kváček 5ef2630810 FEATURE: make title in contents overview translatable and modifiable by module 2016-09-05 00:00:52 +02:00
Michal Kváček 8c4b1ad69b FIX: fix saving title photo in content pages 2016-09-04 23:33:56 +02:00
Michal Kváček 59dede6049 FEATURE: add comments into articles 2016-09-04 23:20:07 +02:00
Michal Kváček f304ef0141 FEATURE: add nice checkbox into settings 2016-09-04 21:47:22 +02:00
Michal Kváček 3cfadc380d FIX: fix default settings for language 2016-09-04 21:41:22 +02:00
Michal Kváček b73b575b71 FEATURE: make user management nicer 2016-09-04 21:31:56 +02:00
Michal Kváček 9b64b54d7b FEATURE: update project dependencies 2016-09-04 05:04:26 +02:00
Michal Kváček e826b474b8 FEATURE: tweak a little bit footer 2016-09-04 04:23:49 +02:00
Michal Kváček 9d63c8d8b0 FEATURE: add link to index page and move template management into system submenu 2016-09-04 04:16:46 +02:00
Michal Kváček aa4161e451 FEATURE: add search 2016-09-04 03:56:35 +02:00
Michal Kváček 9029f14523 FEATURE: add file detail/edit form in file manager 2016-09-04 03:40:45 +02:00
Michal Kváček 98c8313475 FIX: fix active state in sidebar menu 2016-09-04 03:40:32 +02:00
Michal Kváček 8351137206 FEATURE: add settings 2016-09-04 03:31:07 +02:00
Michal Kváček 79ec8e1d2e FEATURE: add action log in bootstrap 2016-09-04 03:08:58 +02:00
Michal Kváček 418d9ac4ca FEATURE: style templates management 2016-09-04 02:42:55 +02:00
Michal Kváček d0be4d1e49 FEATURE: style categories 2016-09-04 02:28:06 +02:00
Michal Kváček 4cfd93161b FIX: fix rendering dividers in submenus 2016-09-04 02:21:58 +02:00
Michal Kváček 05efb6b9f2 FEATURE: add divider before list of all content pages 2016-09-04 02:21:44 +02:00
Michal Kváček 66caf7ce05 FEATURE: ugly commit - rewritten widgets, menu made more compact, added profile menu, few bugfixes etc. Sorry.. 2016-09-04 01:52:17 +02:00
Michal Kváček 9c02c63704 FEATURE: style user management 2016-08-28 00:24:38 +02:00
Michal Kváček 8458622e32 FEATURE: redirect to last page when session has expired 2016-08-25 01:03:35 +02:00
Michal Kváček f1121107c5 FEATURE: add users overview 2016-08-23 23:36:54 +02:00
Michal Kváček d954743e8b FIX: fix route to user edit 2016-08-23 23:36:36 +02:00
Michal Kváček 0a6528a845 FEATURE: style file manager 2016-08-23 23:07:44 +02:00
Michal Kváček f744ca81ca FEATURE: add login form title 2016-08-23 23:06:52 +02:00
Michal Kváček 3a82696d62 FEATURE: rewrite contact form admin into new theme 2016-08-23 00:34:01 +02:00
Michal Kváček cd6f1b4cfa FEATURE: style buttons in content edit 2016-08-23 00:14:17 +02:00
Michal Kváček 28478ba165 FEATURE: add templates for content history 2016-08-23 00:14:06 +02:00
Michal Kváček d746d5b52b FEATURE: add content page overview 2016-08-22 23:29:04 +02:00
Michal Kváček f6fd924cbd FEATURE #1435: few smaller UI tweaks in creating new content page 2016-08-19 13:20:16 +02:00
Michal Kváček 0d1d692c45 Merge branch 'module_blog' into admin_theme 2016-08-19 13:19:14 +02:00
Michal Kváček 6f6497d540 FEATURE #1435: add base of content overview and creating new content 2016-08-18 13:21:35 +02:00
Michal Kváček 7889548a7e FEATURE #1435: small changes in layout 2016-08-18 13:21:05 +02:00
Michal Kváček fc1141184c FEATURE #1435: add login and layout of new admin theme 2016-08-16 16:07:14 +02:00
Michal Kváček 0993d812cb FIX: fix routes to writing new blog and blog post overview 2016-08-16 13:20:30 +02:00
Michal Kváček be81834aad FEATURE: add stub of module for comments 2016-08-16 13:20:07 +02:00
Michal Kváček e389760204 Merge branch 'dev' into module_blog 2016-08-16 00:53:35 +02:00
Michal Kváček 6f799f00bb FEATURE: add support for writing blog posts 2016-08-16 00:52:09 +02:00
Michal Kváček debe80a314 FEATURE: implement loadExtraData as empty function in Handler 2016-08-16 00:51:17 +02:00
Michal Kváček af3db57b29 FEATURE: add translation for history.columnCategory 2016-08-16 00:50:37 +02:00
Michal Kváček 8edde44c0d FIX: fix saving new content without category 2016-08-16 00:50:00 +02:00
Michal Kváček cd369c327f FEATURE: add templates to blog/article 2016-08-15 23:16:06 +02:00
Michal Kváček 206e5201b8 FIX: reset namespace and module ID after each registered path 2016-08-15 22:24:23 +02:00
Michal Kváček aa462c2454 FIX: make artisan install-module really work with submodules 2016-08-15 21:39:18 +02:00
Michal Kváček ae1123ba39 FEATURE: add blog module stub 2016-08-15 20:49:12 +02:00
Michal Kváček bd06797c24 FEATURE: do not use Module::enabled() in registering admin menu 2016-08-15 20:48:17 +02:00
Michal Kváček 5b79fd4323 FIX: ignore enumeration duplicities with null as key 2016-08-15 20:47:52 +02:00
Michal Kváček 53d532da82 FEATURE: make artisan command for installing work with submodules 2016-08-15 20:47:34 +02:00
Michal Kváček fa21c407b4 FEATURE: add support for submodules 2016-08-15 20:46:54 +02:00
Michal Kváček d54f160d6a FIX: fix showing/hiding categories when categories module is not enabled 2016-08-14 17:13:20 +02:00
Michal Kváček 5df5fa8480 FIX: fix module install - manages_content field 2016-08-13 20:35:57 +02:00
Michal Kváček ddd3530993 Merge branch 'module_categories' into dev 2016-08-13 19:03:16 +02:00
Michal Kváček befb95a4ac FEATURE: add relationship methods to categories and content 2016-08-13 19:02:58 +02:00
Michal Kváček dc185bdc2e FEATURE: log not writable module.json in modules 2016-08-13 18:59:14 +02:00
Michal Kváček 42967e72cd Merge branch 'module_categories' into dev 2016-08-13 17:55:23 +02:00
Michal Kváček 85e10b5631 Merge branch 'module_static_pages' into dev 2016-08-13 17:55:16 +02:00
Michal Kváček 54e5927bb1 FEATURE: add support for categories into content save/edit 2016-08-13 17:48:44 +02:00
Michal Kváček 9517d07692 REFACTORING: delete not used files 2016-08-13 16:58:37 +02:00
Michal Kváček 93abfd2485 REFATORING: delete not used files 2016-08-13 16:57:16 +02:00
Michal Kváček 5aec27478a FEATURE: add translations and more friendly look to categories 2016-08-13 16:51:53 +02:00
Michal Kváček 9f955d4544 FEATURE: add category edit and overview 2016-08-11 23:47:24 +02:00
Michal Kváček 597d161a38 FEATURE: add tree view into creating new category 2016-08-11 00:49:05 +02:00
Michal Kváček 48d301330f FEATURE: add basic form for adding new category 2016-08-11 00:27:22 +02:00
Michal Kváček 03f0d71dbb FEATURE: add structure for categories 2016-08-11 00:26:52 +02:00
Michal Kváček 778c87ffa3 FEATURE: install franzose/closure-tables 2016-08-11 00:26:06 +02:00
Michal Kváček ed0b35f9d7 FEATURE: initial commit of module for categories 2016-08-09 01:14:02 +02:00
Michal Kváček cfc3a148e7 FEATURE: prepare handlers for categories 2016-08-09 01:13:44 +02:00
Michal Kváček a698d7a7db FEATURE: add marks for modules 2016-08-09 01:04:03 +02:00
Michal Kváček ba107184e1 FIX: fix missing FK in contents->templates 2016-08-08 23:00:24 +02:00
Michal Kváček 7abd60e785 FEATURE: change collation of all non-utf8_unicode_ci tables 2016-08-01 23:19:13 +02:00
Michal Kváček 0d5e6d4133 FEATURE: add possibilityy to upload and assign files to content pages 2016-07-29 01:22:49 +02:00
Michal Kváček 790cc772a9 FIX: fix missing settings in login form 2016-07-28 15:14:43 +02:00
Michal Kváček 0d8148f22d FEATURE: allow multiple uploads 2016-07-26 15:31:01 +02:00
Michal Kváček bef37a5cce FIX: fix saving content pages - typo in save method 2016-07-26 14:39:48 +02:00
Michal Kváček be4a136476 FEATURE: add lock to URL 2016-07-26 14:39:27 +02:00
Michal Kváček 5fc4393729 FEATURE: add rest of missing czech translations 2016-07-26 12:55:06 +02:00
Michal Kváček 097d76de93 FEATURE: first few czech translations 2016-07-24 00:12:17 +02:00
Michal Kváček 0076cfba2e FIX: use LaravelLocalizationServiceProvider in CLI, but do not throw exceptions when table languages is missing 2016-07-23 23:54:57 +02:00
Michal Kváček 6b24c8a461 FIX: do not use localization service provider in CLI 2016-07-23 22:28:54 +02:00
Michal Kváček 3733d24da8 Merge branch 'multilanguage' into dev 2016-07-23 21:56:30 +02:00
Michal Kváček b2b8084cfe DOC: add few comments 2016-07-23 21:51:59 +02:00
Michal Kváček ab3d8eab41 FEATURE: make settings translatable 2016-07-23 21:47:20 +02:00
Michal Kváček b89941b909 FIX: fix saving and displaying menu with non-existing pages 2016-07-22 16:30:20 +02:00
Michal Kváček bce98e6eaf FEATURE: Add language into widgets 2016-07-22 15:30:03 +02:00
Michal Kváček f90aee4108 FEATURE: add language to content pages 2016-07-22 14:55:51 +02:00
Michal Kváček 2189bbed5e FEATURE: language switch works - changes translations 2016-07-22 11:57:13 +02:00
Michal Kváček ebc7330717 FEATURE: show diff as escaped HTML 2016-07-22 01:01:38 +02:00
Michal Kváček bab897f01e FEATURE: add language selector in admin 2016-07-22 00:44:40 +02:00
Michal Kváček fb2c705ac4 FEATURE: add service provider for registering languages 2016-07-22 00:44:27 +02:00
Michal Kváček 6021f1ea9a FEATURE: add fields for languages 2016-07-22 00:43:52 +02:00
Michal Kváček 181e1fb721 FEATURE: create directory for translations if not exist 2016-07-22 00:42:45 +02:00
Michal Kváček 5b54f6d45f FEATURE: install laravel-localization 2016-07-21 23:47:19 +02:00
Michal Kváček 15a29de894 UPDATE: update dependencies 2016-07-21 23:22:26 +02:00
Michal Kváček db912c4c91 FEATURE: add language table 2016-07-21 23:11:05 +02:00
Michal Kváček 949b1f7c61 FEATURE: add translation command into Artisan 2016-07-21 22:46:48 +02:00
983 changed files with 119688 additions and 26499 deletions
+4 -5
View File
@@ -1,8 +1,8 @@
APP_ENV=testing APP_KEY=
APP_DEBUG=true APP_ENV=
APP_KEY=ZKZAzM23wQ6BPji6zffBjaTzZ3yW7zKQ APP_DEBUG=
DB_HOST=localhost DB_HOST=
DB_DATABASE= DB_DATABASE=
DB_USERNAME= DB_USERNAME=
DB_PASSWORD= DB_PASSWORD=
@@ -17,4 +17,3 @@ MAIL_PASSWORD=
SITE_URL=/ SITE_URL=/
UPLOAD_URL=/public/uploads UPLOAD_URL=/public/uploads
THUMBNAILS_URL=/uploads/thumbnails THUMBNAILS_URL=/uploads/thumbnails
THEME=
+9 -2
View File
@@ -1,8 +1,9 @@
# published files from modules # published files from modules
/resources/views/modules
/public/modules /public/modules
/resources/views/vendor
/public/assets /public/assets
/resources/views/modules
/resources/views/visitor
/resources/views/vendor
# directories with custom templates # directories with custom templates
**/views/**/custom/* **/views/**/custom/*
@@ -26,3 +27,9 @@
# meta information created by installer # meta information created by installer
.install .install
/node_modules/
/public/css/
/public/js
/public/fonts
/bower_components
.sass-cache
@@ -0,0 +1,39 @@
<?php
namespace App\CMS\Contracts\Content;
use App\Http\Requests\Request;
use App\Models\Content\Content;
interface AdvancedContentModifier {
/**
* Save module-specific values into database
*
* @param Content $content
* @param array $inputData
*
* @return mixed
*/
public static function createNew(Content &$content, array $inputData);
/**
* Method used for editing existing content page.
*
* Reason why it is separated from createNew() method is simple - we need to save history of changes.
*
* @param Content $content
* @param $request
*
* @return mixed
*/
public static function edit(Content $content, $request);
/**
* Method called right after content page is marked as deleted
*
* This method is supposed to clean after "deleted" page.
*
* @param Content $content
*
* @return mixed
*/
public static function delete(Content $content);
}
@@ -0,0 +1,40 @@
<?php
namespace App\CMS\Contracts\Content;
use App\Models\Content\Content;
/**
* Interface ContentTitles
*
* Classes which implement this interface get the ability to rewrite default text on edit/create/overview of content
* page.
*
* @package App\CMS\Handlers\Contracts
*/
interface ContentTitles
{
/**
* Get translated title for pages overview of given type.
*
* This value is used for rendering list of all content pages of given type in administration.
*
* @return string
*/
public static function getOverviewTitle();
/**
* Title displayed above for for creating new content page
*
* @return string
*/
public static function getCreateNewTitle();
/**
* Title for form displayed for editin content page
*
* @param Content $content
*
* @return mixed
*/
public static function getEditTitle(Content $content);
}
@@ -1,5 +1,5 @@
<?php <?php
namespace App\Handlers\Contracts; namespace App\CMS\Handlers\Contracts;
interface HistoryHandler interface HistoryHandler
{ {
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace App\CMS\Contracts;
interface Interaction {
/**
* Perform given action
*
* @param $data array input data
* @param $parameters array parameters
*
* @return mixed
*/
public function handle($data, $parameters);
/**
* @param $data array input data
*
* @return array
*/
public function getValidationRules($data = []);
}
+52
View File
@@ -0,0 +1,52 @@
<?php
namespace App\CMS\Contracts\Modules;
use App\Http\Requests\Request;
use App\Models\Content\Content;
interface AppHandler extends BaseModuleHandler
{
/**
* Render given module into visitor area.
*
* @param Content $content DB model from table contents
* @param array $parameters parameters used for modifications/custom settings of given model
*
* @return mixed
*/
public function render(Content $content, $parameters);
public static function getHistoryHandler();
/**
* Getter for section names in creating/editing content resource
*
* @return mixed
*/
public static function getExtendedFormSections();
/**
* Getter for views used in extended part of form for creating new content
*
* This method should return pairs <section, view-name>. View is returned only as name in plain text
*
* @return array of views/templates/html codes divided into section
*/
public static function getExtendedFormInputsCreate();
/**
* Getter for extended part of form for editing existing content
*
* Basically, this method may be
*
* @return array of views/templates/html codes divided into section
*/
public static function getExtendedFormInputsEdit();
/**
* Does this module support categories?
*
* @return boolean
*/
public static function supportCategories();
}
@@ -0,0 +1,18 @@
<?php
namespace App\CMS\Contracts\Modules;
use App\Models\User;
interface BaseModuleHandler
{
/**
* Register admin menu (or multiple ones)
*
* This cannot be in ServiceProvider because Session is started at middleware. And ServiceProvider is called before
* starting any middleware..
*
* @param User $user model of logged user
*/
public static function registerAdminMenu(User $user);
}
@@ -0,0 +1,23 @@
<?php
namespace App\CMS\Contracts\Modules;
use App\Models\Module;
interface InstallableModule {
/**
* Install module.
*
* This method is called after inserting info about module into database. It can be called only using Artisan in
* CLI.
*/
public static function install();
/**
* Uninstalls module
*
* @param Module $module
*
* @return mixed
*/
public static function uninstall(Module $module);
}
@@ -0,0 +1,54 @@
<?php
namespace App\CMS\Contracts\Modules;
use App\Http\Requests\Request;
use App\Models\Content\Content;
/**
* Interface TemplateHandler
*
* Inerface used for registering template into CMS core.
*
* @package App\CMS\Handlers\Contracts
*/
interface TemplateHandler extends BaseModuleHandler
{
/**
* Get list of registered templates and supported modules.
*
* Returned array is expected to be array of arrays with following items: module, namespace and templateName.
* Example:
* return [
* ['module' => 'Article', 'templateName' => 'article::template.name']
* ];
*
* When no "namespace" is specified, value strtolower(module) is used instaed.
*
* @return array
*/
public static function getPublicTemplatePaths();
/**
* Prepare Content object to be rendered using Blade template
*
* @param Content $content
*/
public static function beforeRender(Content & $content);
/**
* Modify input data into format which can be understood by edit template
*
* @param Content $content
*/
public static function beforePopulatingEditForm(Content & $content);
/**
* Slightly modify Content object before saving into database.
*
* This way we can do almost whatever we want - even in template
*
* @param Content $content
* @param $request
*/
public static function beforeSave(Content & $content, $request);
}
@@ -0,0 +1,7 @@
<?php
namespace App\CMS\Contracts\Repositories;
interface BaseRepository {
public function getOne($id = null);
public function all();
}
@@ -0,0 +1,15 @@
<?php
namespace App\CMS\Contracts\Repositories;
interface Content extends BaseRepository
{
const STATUS_DRAFT = 'draft';
const STATUS_PROTECTED = 'protected';
const STATUS_PUBLIC = 'public';
public function getOverview($handler = null, $statuses = null);
public function getHomepage();
public function getByURL($url);
}
+20
View File
@@ -0,0 +1,20 @@
<?php
namespace App\CMS\Contracts\Repositories;
use App\Models\Content\Content as ContentModel;
use App\Models\Module as ModuleModel;
interface Module extends BaseRepository
{
public function getCreateContentTitle(ModuleModel $module);
public function getUpdateContentTitle(ModuleModel $module, ContentModel $content = null);
public function getOverviewTitle(ModuleModel $module);
public function getByHandler($handler);
public function call(ModuleModel $module, $method, $parameters = []);
public function callOnString($moduleHandler, $method, $parameters = []);
}
@@ -0,0 +1,21 @@
<?php
namespace App\CMS\Contracts\Repositories;
use App\Models\Module as ModuleModel;
use App\Models\Templates\Template as TemplateModel;
interface Template
{
const EDITED_TEMPLATES_DIRECTORY = 'custom';
const ASSETS_DIRECTORY = 'assets';
const MODULES_DIRECTORY = 'modules';
const TEMPLATE_SUFFIX = '.blade.php';
const CUSTOM_EDIT_DIRECTORY = 1;
const ORIGINAL_DIRECTORY = 2;
public function setModel(TemplateModel $model);
public function getPathToTemplate();
public function getForModule(ModuleModel $module);
}
@@ -1,5 +1,5 @@
<?php <?php
namespace App\Handlers\Contracts; namespace App\CMS\Handlers\Contracts;
interface SearchHandler { interface SearchHandler {
public function getId(); public function getId();
@@ -1,5 +1,5 @@
<?php <?php
namespace App\Handlers\Contracts; namespace App\CMS\Handlers\Contracts;
interface SearchPresenterHandler interface SearchPresenterHandler
{ {
@@ -1,5 +1,5 @@
<?php <?php
namespace App\Widgets\Contracts; namespace App\CMS\Contracts\Widgets;
interface AdminDashboardWidget extends PublicWidget interface AdminDashboardWidget extends PublicWidget
{ {
@@ -0,0 +1,17 @@
<?php
namespace App\CMS\Contracts\Widgets;
interface InstallableWidget extends PublicWidget {
/**
* Function for installing given widget
*
* @return mixed
*/
public static function install();
/**
* Function for uninstalling widget
*/
public static function uninstall();
}
@@ -1,5 +1,5 @@
<?php <?php
namespace App\Widgets\Contracts; namespace App\CMS\Widgets\Contracts;
interface MenuExtensionHandler { interface MenuExtensionHandler {
@@ -1,8 +1,8 @@
<?php <?php
namespace App\Widgets\Contracts; namespace App\CMS\Contracts\Widgets;
use App\Http\Requests\Request; use App\Http\Requests\Request;
use App\Models\Widget; use App\Models\Widgets\Widget;
interface PublicWidget interface PublicWidget
{ {
@@ -28,22 +28,11 @@ interface PublicWidget
public static function getPresenter(); public static function getPresenter();
/** /**
* @param Widget $widget * Get widget settings for saving into database
* @param \stdClass $data
* *
* @return mixed * @param Request $request
*/
public static function save(Widget $widget, \stdClass $data);
/**
* Function for installing given widget
* *
* @return mixed * @return array
*/ */
public static function install(); public static function save(Request $request);
/**
* Function for uninstalling widget
*/
public static function uninstall();
} }
@@ -1,8 +1,8 @@
<?php <?php
namespace App\Widgets\Contracts; namespace App\CMS\Contracts\Widgets;
use App\Models\Widget; use App\Models\Widgets\Widget;
use App\Models\WidgetType; use App\Models\Widgets\WidgetType;
interface WidgetRenderer interface WidgetRenderer
{ {
@@ -11,11 +11,11 @@ interface WidgetRenderer
* *
* This method renders some HTML code for displaying. * This method renders some HTML code for displaying.
* *
* @param object $settings * @param Widget $settings
* *
* @return mixed * @return mixed
*/ */
public static function render($settings); public static function render(Widget $settings);
/** /**
* Display form for creating new instance of widget * Display form for creating new instance of widget
@@ -1,5 +1,5 @@
<?php <?php
namespace App\Enumerations; namespace App\CMS\Enumerations;
use App\Models\ActionLogType; use App\Models\ActionLogType;
@@ -1,7 +1,7 @@
<?php <?php
namespace App\Enumerations; namespace App\CMS\Enumerations;
use App\Models\ActionLogType; use App\Models\ActionLog\ActionLogType;
class ActionLogTypesForLogger extends BaseEnumeration class ActionLogTypesForLogger extends BaseEnumeration
{ {
@@ -1,7 +1,7 @@
<?php <?php
namespace App\Enumerations; namespace App\CMS\Enumerations;
use App\Enumerations\Exceptions\EnumerationExistsException; use App\CMS\Exceptions\Enumerations\EnumerationExistsException;
abstract class BaseEnumeration abstract class BaseEnumeration
{ {
@@ -47,7 +47,7 @@ abstract class BaseEnumeration
public static function push($key, $translation, $overwrite = false) public static function push($key, $translation, $overwrite = false)
{ {
// item exists and we do not want to overwrite items // item exists and we do not want to overwrite items
if (!$overwrite && isset(self::$items[get_called_class()][$key])) { if (!$overwrite && !is_null($key) && isset(self::$items[get_called_class()][$key])) {
throw new EnumerationExistsException(get_called_class() . ': ' . $key . ' already in ' . $translation); throw new EnumerationExistsException(get_called_class() . ': ' . $key . ' already in ' . $translation);
} }
@@ -1,7 +1,8 @@
<?php <?php
namespace App\Enumerations; namespace App\CMS\Enumerations;
use App\Models\Content;
use App\CMS\Contracts\Repositories\Content;
class ContentStatuses extends BaseEnumeration class ContentStatuses extends BaseEnumeration
{ {
@@ -9,7 +10,6 @@ class ContentStatuses extends BaseEnumeration
{ {
$roles = [ $roles = [
Content::STATUS_DRAFT => 'admin/contents.status_' . Content::STATUS_DRAFT, Content::STATUS_DRAFT => 'admin/contents.status_' . Content::STATUS_DRAFT,
Content::STATUS_DELETED => 'admin/contents.status_' . Content::STATUS_DELETED,
Content::STATUS_PROTECTED => 'admin/contents.status_' . Content::STATUS_PROTECTED, Content::STATUS_PROTECTED => 'admin/contents.status_' . Content::STATUS_PROTECTED,
Content::STATUS_PUBLIC => 'admin/contents.status_' . Content::STATUS_PUBLIC Content::STATUS_PUBLIC => 'admin/contents.status_' . Content::STATUS_PUBLIC
]; ];
@@ -1,5 +1,5 @@
<?php <?php
namespace App\Enumerations; namespace App\CMS\Enumerations;
use App\Models\Module; use App\Models\Module;
@@ -1,5 +1,5 @@
<?php <?php
namespace App\Enumerations; namespace App\CMS\Enumerations;
use App\Models\Module; use App\Models\Module;
@@ -1,7 +1,8 @@
<?php <?php
namespace App\Enumerations; namespace App\CMS\Enumerations;
use App\Models\Module; use App\Models\Module;
use Illuminate\Support\Facades\Cache;
class ModulesHandlerToId extends BaseEnumeration class ModulesHandlerToId extends BaseEnumeration
{ {
@@ -12,7 +13,9 @@ class ModulesHandlerToId extends BaseEnumeration
*/ */
protected static function init() protected static function init()
{ {
$modules = Module::all(['handler', 'id']); $modules = Cache::rememberForever('module-handler-to-id-enumeration', function () {
return Module::all(['handler', 'id']);
});
foreach ($modules as $module) { foreach ($modules as $module) {
self::push($module->handler, $module->id); self::push($module->handler, $module->id);
@@ -6,9 +6,9 @@
* Time: 10:33 * Time: 10:33
*/ */
namespace App\Enumerations; namespace App\CMS\Enumerations;
use App\Models\TemplatePath; use App\Models\Templates\TemplatePath;
class TemplatePaths extends BaseEnumeration class TemplatePaths extends BaseEnumeration
{ {
@@ -6,9 +6,9 @@
* Time: 10:33 * Time: 10:33
*/ */
namespace App\Enumerations; namespace App\CMS\Enumerations;
use App\Models\TemplatePath; use App\Models\Templates\TemplatePath;
class TemplatePathsHumanReadable extends BaseEnumeration class TemplatePathsHumanReadable extends BaseEnumeration
{ {
@@ -6,9 +6,9 @@
* Time: 10:33 * Time: 10:33
*/ */
namespace App\Enumerations; namespace App\CMS\Enumerations;
use App\Models\TemplatePath; use App\Models\Templates\TemplatePath;
class TemplatePathsHumanReadableRootOnly extends BaseEnumeration class TemplatePathsHumanReadableRootOnly extends BaseEnumeration
{ {
@@ -6,9 +6,9 @@
* Time: 15:50 * Time: 15:50
*/ */
namespace App\Enumerations; namespace App\CMS\Enumerations;
use App\Models\Template; use App\Models\Templates\Template;
class Templates extends BaseEnumeration class Templates extends BaseEnumeration
{ {
@@ -1,5 +1,5 @@
<?php <?php
namespace App\Enumerations; namespace App\CMS\Enumerations;
use App\Models\User; use App\Models\User;
@@ -6,7 +6,7 @@
* Time: 23:20 * Time: 23:20
*/ */
namespace App\Enumerations; namespace App\CMS\Enumerations;
use App\Models\User; use App\Models\User;
@@ -15,7 +15,6 @@ class UserStates extends BaseEnumeration
public static function init() public static function init()
{ {
$items = [ $items = [
User::STATE_DELETED => 'admin/userManagement.status_' . User::STATE_DELETED,
User::STATE_BLOCKED => 'admin/userManagement.status_' . User::STATE_BLOCKED, User::STATE_BLOCKED => 'admin/userManagement.status_' . User::STATE_BLOCKED,
User::STATE_ACTIVE => 'admin/userManagement.status_' . User::STATE_ACTIVE User::STATE_ACTIVE => 'admin/userManagement.status_' . User::STATE_ACTIVE
]; ];
@@ -1,7 +1,7 @@
<?php <?php
namespace App\Enumerations; namespace App\CMS\Enumerations;
use App\Models\WidgetType; use App\Models\Widgets\WidgetType;
class WidgetTypes extends BaseEnumeration class WidgetTypes extends BaseEnumeration
{ {
@@ -1,7 +1,7 @@
<?php <?php
namespace App\Enumerations; namespace App\CMS\Enumerations;
use App\Models\WidgetType; use App\Models\Widgets\WidgetType;
class WidgetTypesWithHandlers extends BaseEnumeration class WidgetTypesWithHandlers extends BaseEnumeration
{ {
@@ -1,5 +1,5 @@
<?php <?php
namespace App\Enumerations\Exceptions; namespace App\CMS\Exceptions\Enumerations;
class EnumerationExistsException extends \Exception { class EnumerationExistsException extends \Exception {
@@ -0,0 +1,6 @@
<?php
namespace App\CMS\Exceptions\FileManager;
class SubdirectoryExistsInDirectory extends \Exception
{
}
@@ -0,0 +1,19 @@
<?php
namespace App\CMS\Exceptions\Validation;
use Exception;
class ValidationFailedException extends \Exception {
protected $validator;
public function __construct($validator, $message = null, $code = null, Exception $previous = null)
{
parent::__construct($message, $code, $previous);
$this->validator = $validator;
}
public function getValidator(){
return $this->validator;
}
}
@@ -1,5 +1,5 @@
<?php <?php
namespace App\Widgets\Exceptions; namespace App\CMS\Widgets\Exceptions;
use Exception; use Exception;
@@ -1,5 +1,5 @@
<?php <?php
namespace App\Widgets\Exceptions; namespace App\CMS\Widgets\Exceptions;
use Exception; use Exception;
+110
View File
@@ -0,0 +1,110 @@
<?php
namespace App\CMS\Handlers;
use App\CMS\Contracts\Modules\BaseModuleHandler;
use App\CMS\Enumerations\TemplatePathsHumanReadableRootOnly;
use App\CMS\Enumerations\WidgetTypes;
use App\Models\User;
abstract class BaseHandler implements BaseModuleHandler
{
/**
* Register admin menu (or multiple ones)
*
* This cannot be in ServiceProvider because Session is started at middleware. And ServiceProvider is called before
* starting any middleware..
*
* @param User $user model of logged user
*/
public static function registerAdminMenu(User $user)
{
$menu = \Menu::instance('mainMenu');
if (!is_null($menu)) {
// menus were already defined
return;
}
// create instance of main menu
\Menu::create('mainMenu', function ($menu) {
// $menu->route('admin.dashboard', trans('admin/mainMenu.dashboard'), [], 10, ['icon' => 'fa fa-fw fa-dashboard']);
/**
* Content pages
*
* Submenus can be registered later
*/
$menu->dropdown(trans('admin/mainMenu.contentPages'), function ($sub) {
$sub->route('content.overview', trans('admin/mainMenu.contentPageOverview'), [], 10000, ['icon' => 'fa fa-fw fa-list']);
}, 20, ['icon' => 'fa fa-fw fa-database']);
/**
* File manager
*/
$menu->route('upload.overview', trans('admin/mainMenu.fileManager'), [], 40, ['icon' => 'fa fa-fw fa-folder']);
/**
* Widgets and widget areas
*/
$menu->dropdown(trans('admin/mainMenu.widgets'), function ($sub) {
// create list of all widget types
$widgets = WidgetTypes::getWithTranslations();
if (!empty($widgets)) {
$sub->dropdown(trans('admin/mainMenu.newWidget'), function ($sub) use ($widgets) {
$i = 1;
foreach ($widgets as $type => $name) {
$sub->route('widgets.new', $name, ['type' => $type], $i++);
}
}, 10, ['icon' => 'fa fa-fw fa-plus']);
}
$sub->route('widgets.overview', trans('admin/mainMenu.widgetsOverview'), [], 20, ['icon' => 'fa fa-fw fa-list']);
// menu for widget areas
$sub->dropdown(trans('admin/mainMenu.widgetAreas'), function ($sub) {
$sub->route('widgetareas.new', trans('admin/mainMenu.newWidgetArea'), [], 1, ['icon' => 'fa fa-fw fa-plus']);
$sub->route('widgetareas.overview', trans('admin/mainMenu.widgetAreaOverview'), [], 2, ['icon' => 'fa fa-fw fa-list']);
}, 50, ['icon' => 'fa fa-fw fa-object-group']);
}, 50, ['icon' => 'fa fa-fw fa-puzzle-piece']);
/**
* Divide system entries and other links in menu
*/
$menu->addDivider(9999);
$menu->dropdown(trans('admin/mainMenu.system'), function ($sub) {
/**
* User management
*/
$sub->dropdown(trans('admin/mainMenu.userManagement'), function ($sub) {
$sub->route('users.new', trans('admin/mainMenu.newUser'), [], 10, ['icon' => 'fa fa-fw fa-user-plus']);
$sub->route('users.overview', trans('admin/mainMenu.usersOverview'), [], 30, ['icon' => 'fa fa-fw fa-users']);
}, 20, ['icon' => 'fa fa-fw fa-users']);
/**
* Templates
*/
$templatePaths = TemplatePathsHumanReadableRootOnly::getWithTranslations();
if (!empty($templatePaths)) {
$sub->dropdown(trans('admin/mainMenu.templates'), function ($sub) use ($templatePaths) {
$i = 1;
foreach ($templatePaths as $id => $name) {
$sub->route('templates.overview', $name, ['id' => $id], $i++);
}
}, 60, ['icon' => 'fa fa-fw fa-file-code-o']);
}
$sub->route('log.overview', trans('admin/mainMenu.actionLog'), [], 70, ['icon' => 'fa fa-fw fa-history']);
$sub->route('settings.overview', trans('admin/mainMenu.settings'), [], 80, ['icon' => 'fa fa-fw fa-gear']);
}, 10000, ['icon' => 'fa fa-fw fa-gears']);
});
// create menu with user profile
\Menu::create('profileMenu', function ($menu) {
$menu->route('users.detail', trans('admin/userManagement.profile'), [], 10, ['icon' => 'fa fa-fw fa-user icon']);
$menu->addDivider(999);
$menu->route('logout', trans('admin/common.logout'), [], 1000, ['icon' => 'fa fa-fw fa-power-off icon']);
});
}
}
+62
View File
@@ -0,0 +1,62 @@
<?php
namespace App\CMS\Handlers;
use App\CMS\Contracts\Modules\AppHandler;
use App\Http\Controllers\Controller;
use App\Models\Content\Content;
/**
* Class Handler
*
* Handler is a class which handles module-specific behaviour which cannot be done by module itself, resp. cannot be
* done by some abstract magic.
*
* @package App\CMS\Handlers\
*/
abstract class Handler extends BaseHandler implements AppHandler
{
public static function getHistoryHandler()
{
return History::class;
}
/**
* Render given module into visitor area.
*
* @param Content $content DB model from table contents
* @param array $parameters parameters used for modifications/custom settings of given model
*
* @return mixed
*/
public function render(Content $content, $parameters)
{
return Controller::VisitorsView($content->template, ['page' => $content, 'extra' => $parameters]);
}
public static function getExtendedFormSections()
{
return [
'basicInfo' => trans('admin/contents.basicInfo'),
'meta' => trans('admin/contents.metaOptions'),
'publication' => trans('admin/contents.publicationOptions'),
'appearance' => trans('admin/contents.appearance'),
'files' => trans('admin/contents.files')
];
}
public static function getExtendedFormInputsCreate()
{
return [
'basicInfo' => 'admin.content.formParts.basicInformation',
'meta' => 'admin.content.formParts.metaInformation',
'publication' => 'admin.content.formParts.publicationSetup',
'appearance' => 'admin.content.formParts.appearance',
'files' => 'admin.content.formParts.files'
];
}
public static function getExtendedFormInputsEdit()
{
return self::getExtendedFormInputsCreate();
}
}
@@ -1,9 +1,9 @@
<?php <?php
namespace App\Handlers; namespace App\CMS\Handlers;
use App\Enumerations\ContentStatuses; use App\CMS\Enumerations\ContentStatuses;
use App\Handlers\Contracts\HistoryHandler; use App\CMS\Handlers\Contracts\HistoryHandler;
use App\Helpers\Differ; use App\CMS\Helpers\Differ;
class History implements HistoryHandler class History implements HistoryHandler
{ {
@@ -25,6 +25,15 @@ class History implements HistoryHandler
break; break;
case 'metaDescription': case 'metaDescription':
return Differ::htmlDiff($changeset->old_value, $changeset->new_value); return Differ::htmlDiff($changeset->old_value, $changeset->new_value);
break;
case 'file':
if (!$changeset->old_value) {
// old value is null -> new file was uploaded
return trans('admin/history.uploadedFile', ['file' => $changeset->new_value]);
} else {
return trans('admin/history.deletedFile', ['file' => $changeset->old_value]);
}
break; break;
case 'status': case 'status':
$statuses = ContentStatuses::getWithTranslations(); $statuses = ContentStatuses::getWithTranslations();
@@ -34,7 +43,7 @@ class History implements HistoryHandler
'to' => $statuses[$changeset->new_value] 'to' => $statuses[$changeset->new_value]
]); ]);
break; break;
case 'is_homepage': case 'isHomepage':
return trans('admin/history.setupAsHomepage'); return trans('admin/history.setupAsHomepage');
break; break;
default: default:
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace App\Cms\Handlers\Interactions;
use App\CMS\Contracts\Interaction as InteractionContract;
use App\CMS\Exceptions\Validation\ValidationFailedException;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Validator;
class Interaction
{
/**
* Handle interaction with application
*
* @param string $interactionClass
* @param array $data
* @param array $extraParameters
*
* @return mixed
* @throws ValidationFailedException
*/
public static function handle($interactionClass, $data, ...$extraParameters)
{
/**
* @var $interaction InteractionContract
*/
$interaction = app($interactionClass);
// validate interaction
$validator = Validator::make($data, $interaction->getValidationRules($extraParameters));
if ($validator->fails()) {
throw new ValidationFailedException($validator);
}
// handle it
return $interaction->handle($data, $extraParameters);
}
}
@@ -1,5 +1,5 @@
<?php <?php
namespace App\Handlers\Registrators\Items; namespace App\CMS\Handlers\Registrators\Items;
class SettingExtension class SettingExtension
{ {
@@ -1,7 +1,7 @@
<?php <?php
namespace App\Handlers\Registrators; namespace App\CMS\Handlers\Registrators;
use App\Widgets\Contracts\MenuExtensionHandler; use App\CMS\Widgets\Contracts\MenuExtensionHandler;
class MenuExtensionRegistrator { class MenuExtensionRegistrator {
@@ -1,10 +1,10 @@
<?php <?php
namespace App\Handlers\Registrators; namespace App\CMS\Handlers\Registrators;
use App\Enumerations\Exceptions\EnumerationExistsException; use App\CMS\Enumerations\Exceptions\EnumerationExistsException;
use App\Handlers\Contracts\SearchHandler; use App\CMS\Handlers\Contracts\SearchHandler;
use App\Handlers\Contracts\SearchPresenterHandler; use App\CMS\Handlers\Contracts\SearchPresenterHandler;
use App\Handlers\Registrators\Items\SearchEngine; use App\CMS\Handlers\Registrators\Items\SearchEngine;
use Symfony\Component\Process\Exception\InvalidArgumentException; use Symfony\Component\Process\Exception\InvalidArgumentException;
class SearchEngineRegistrator class SearchEngineRegistrator
@@ -1,8 +1,8 @@
<?php <?php
namespace App\Handlers\Registrators; namespace App\CMS\Handlers\Registrators;
use App\Enumerations\Exceptions\EnumerationExistsException; use App\CMS\Enumerations\Exceptions\EnumerationExistsException;
use App\Handlers\Registrators\Items\SettingExtension; use App\CMS\Handlers\Registrators\Items\SettingExtension;
class SettingsExtensionRegistrator class SettingsExtensionRegistrator
{ {
@@ -1,10 +1,11 @@
<?php <?php
namespace App\Handlers\Registrators; namespace App\CMS\Handlers\Registrators;
use App\Enumerations\TemplatePaths; use App\CMS\Contracts\Repositories\Template;
use App\Enumerations\Templates; use App\CMS\Enumerations\TemplatePaths;
use App\Models\Template; use App\CMS\Enumerations\Templates;
use App\Models\TemplatePath; use App\Models\Templates\Template as TemplateModel;
use App\Models\Templates\TemplatePath;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Symfony\Component\Process\Exception\InvalidArgumentException; use Symfony\Component\Process\Exception\InvalidArgumentException;
@@ -28,6 +29,10 @@ class TemplateRegistrator
* @var int ID of module * @var int ID of module
*/ */
protected $moduleId; protected $moduleId;
/**
* @var array list of all registered template paths
*/
protected $paths = [];
/** /**
* Set base path * Set base path
@@ -64,7 +69,7 @@ class TemplateRegistrator
*/ */
public function setNamespace($namespace) public function setNamespace($namespace)
{ {
$this->namespace = $namespace . '::'; $this->namespace = $namespace;
} }
/** /**
@@ -73,28 +78,52 @@ class TemplateRegistrator
public function unsetNamespace() public function unsetNamespace()
{ {
$this->namespace = null; $this->namespace = null;
$this->setModuleId(null);
} }
/** /**
* Registers new template path. * Registers new template path.
* This method registeres given path itself with all subdirectories * This method registeres given path itself with all subdirectories
* *
* @param $baseDirectory
* @param string $path path relatively to root of the project * @param string $path path relatively to root of the project
* @param string $name human readable identification of given template path * @param string $name human readable identification of given template path
* *
* @throws \Exception * @throws \Exception
*/ */
public function register($path, $name) public function register($baseDirectory, $path, $name)
{ {
// check if base directory was set $this->paths[] = [
if (!$this->baseDirectory || !is_dir($this->baseDirectory)) { 'namespace' => $this->namespace,
throw new \Exception('Set $baseDirectory before registering template path.'); 'moduleId' => $this->moduleId,
'baseDirectory' => $baseDirectory,
'path' => $this->baseDirectory . DIRECTORY_SEPARATOR . $path,
'name' => $name
];
$this->unsetNamespace();
}
/**
* Save templates into database
*/
public function saveTemplates()
{
if (!is_array($this->paths) || empty($this->paths)) {
return;
} }
$this->registeredPath = $this->baseDirectory . DIRECTORY_SEPARATOR . $path; foreach ($this->paths as $path) {
if (!empty($path['namespace'])) {
$this->setNamespace($path['namespace'] . '::');
}
$this->setModuleId($path['moduleId']);
$this->setBaseDirectory($path['baseDirectory']);
// register path $this->registeredPath = $this->baseDirectory . DIRECTORY_SEPARATOR . $path['path'];
$this->registerPath($this->registeredPath, $name); // register path
$this->registerPath($this->registeredPath, $path['name']);
$this->unsetNamespace();
}
} }
/** /**
@@ -106,7 +135,10 @@ class TemplateRegistrator
*/ */
protected function relativePath($directory) protected function relativePath($directory)
{ {
return trim(str_replace(base_path(), '', $directory), '/'); $directory = str_replace('/', DIRECTORY_SEPARATOR, $directory);
$directory = str_replace('\\', DIRECTORY_SEPARATOR, $directory);
return trim(str_replace(base_path(), '', $directory), '\\/');
} }
/** /**
@@ -117,6 +149,10 @@ class TemplateRegistrator
*/ */
protected function scanForSubdirectories($directory, $name) protected function scanForSubdirectories($directory, $name)
{ {
if (!is_dir($directory)) {
return;
}
$directories = File::directories($directory); $directories = File::directories($directory);
foreach ($directories as $dir) { foreach ($directories as $dir) {
@@ -166,7 +202,7 @@ class TemplateRegistrator
* @param $path * @param $path
* @param $templatePathId * @param $templatePathId
* *
* @throws \App\Enumerations\Exceptions\EnumerationExistsException * @throws \App\CMS\Enumerations\Exceptions\EnumerationExistsException
*/ */
protected function registerTemplates($path, $templatePathId) protected function registerTemplates($path, $templatePathId)
{ {
@@ -184,7 +220,6 @@ class TemplateRegistrator
$registeredTemplates = Templates::getWithTranslations(); $registeredTemplates = Templates::getWithTranslations();
if (!in_array($templateName, $registeredTemplates)) { if (!in_array($templateName, $registeredTemplates)) {
$params = [ $params = [
'template_name' => $templateName, 'template_name' => $templateName,
'name' => $templateName, 'name' => $templateName,
@@ -195,7 +230,7 @@ class TemplateRegistrator
]; ];
// save template into database // save template into database
$newTemplate = Template::create($params); $newTemplate = TemplateModel::create($params);
// push template into enumeration - so it will not be registered again // push template into enumeration - so it will not be registered again
Templates::push($newTemplate->id, $templateName); Templates::push($newTemplate->id, $templateName);
@@ -211,7 +246,7 @@ class TemplateRegistrator
* @param null $parent * @param null $parent
* *
* @return mixed|null * @return mixed|null
* @throws \App\Enumerations\Exceptions\EnumerationExistsException * @throws \App\CMS\Enumerations\Exceptions\EnumerationExistsException
*/ */
protected function saveTemplatePath($path, $name, $parent = null) protected function saveTemplatePath($path, $name, $parent = null)
{ {
@@ -268,12 +303,17 @@ class TemplateRegistrator
* *
* @return string * @return string
*/ */
private function getTemplateName($template) protected function getTemplateName($template)
{ {
$templateName = str_replace($this->registeredPath . DIRECTORY_SEPARATOR, '', $template); $templateName = $template;
$templateName = str_replace($this->registeredPath, '', $templateName);
$templateName = str_replace('\\', DIRECTORY_SEPARATOR, $templateName);
$templateName = str_replace('/', DIRECTORY_SEPARATOR, $templateName);
$templateName = str_replace(DIRECTORY_SEPARATOR, '.', $templateName); $templateName = str_replace(DIRECTORY_SEPARATOR, '.', $templateName);
$templateName = str_replace(Template::TEMPLATE_SUFFIX, '', $templateName); $templateName = str_replace(Template::TEMPLATE_SUFFIX, '', $templateName);
$templateName = preg_replace('/\.{2,}/', '.', $templateName);
return $this->namespace . $templateName; return $this->namespace . trim($templateName, '.');
} }
} }
@@ -0,0 +1,59 @@
<?php
namespace App\CMS\Handlers\Registrators;
use App\CMS\Enumerations\WidgetTypesWithHandlers;
use App\Models\Widgets\WidgetType;
class WidgetRegistrator
{
protected $registeredWidgets = [];
/**
* Method used for registering new widgets
*
* @param $classname
* @param null $templateId
* @param null $moduleId
*/
public function register($classname, $templateId = null, $moduleId = null)
{
$this->registeredWidgets[] = [
'class' => $classname,
'templateId' => $templateId,
'moduleId' => $moduleId
];
}
/**
* Install all non-installed widgets
*/
public function installWidgets()
{
$installed = WidgetTypesWithHandlers::getSimple();
foreach ($this->registeredWidgets as $registeredWidget) {
$classname = $registeredWidget['class'];
$templateId = $registeredWidget['templateId'];
$moduleId = $registeredWidget['moduleId'];
// register widget as new widget type
if (!in_array($classname, $installed)) {
$widgetName = call_user_func($classname . '::getName');
$widgetDescription = call_user_func($classname . '::getDescription');
// create row in widget types
WidgetType::create([
'handler' => $classname,
'name' => $widgetName,
'module_id' => $moduleId,
'template_id' => $templateId,
'description' => $widgetDescription
]);
// install widget (if neccessary)
if (is_callable([$classname, 'install'])) {
call_user_func($classname . '::install');
}
}
}
}
}
@@ -1,7 +1,7 @@
<?php <?php
namespace App\Handlers\Search; namespace App\CMS\Handlers\Search;
use App\Models\Content as ContentModel; use App\Models\Content\Content as ContentModel;
class Content extends Search class Content extends Search
{ {
@@ -16,9 +16,9 @@ class Content extends Search
{ {
$token = $this->tokenize($term); $token = $this->tokenize($term);
return ContentModel::where('content', 'LIKE', $token) return ContentModel::where('pageContent', 'LIKE', $token)
->orWhere('meta_description', 'LIKE', $token) ->orWhere('metaDescription', 'LIKE', $token)
->orWhere('meta_keywords', 'LIKE', $token) ->orWhere('metaKeywords', 'LIKE', $token)
->orWhere('title', 'LIKE', $token) ->orWhere('title', 'LIKE', $token)
->orWhere('url', 'LIKE', $token) ->orWhere('url', 'LIKE', $token)
->get(); ->get();
@@ -1,7 +1,7 @@
<?php <?php
namespace App\Handlers\Search; namespace App\CMS\Handlers\Search;
use App\Models\File; use App\Models\FileManager\File;
class Files extends Search class Files extends Search
{ {
@@ -27,6 +27,7 @@ class Files extends Search
$token = $this->tokenize($term); $token = $this->tokenize($term);
return File::where('original_filename', 'LIKE', $token) return File::where('original_filename', 'LIKE', $token)
->orWhere('name', 'LIKE', $token)
->orWhere('keywords', 'LIKE', $token) ->orWhere('keywords', 'LIKE', $token)
->orWhere('description', 'LIKE', $token) ->orWhere('description', 'LIKE', $token)
->orWhere('mime_type', 'LIKE', $token) ->orWhere('mime_type', 'LIKE', $token)
@@ -0,0 +1,16 @@
<?php
namespace App\CMS\Handlers\Search\Presenters;
use App\CMS\Enumerations\ContentStatuses;
use App\CMS\Handlers\Contracts\SearchPresenterHandler;
class AdminContent implements SearchPresenterHandler
{
public function render($foundItems)
{
return view('admin.search.results.content', [
'pages' => $foundItems,
'statuses' => ContentStatuses::getWithTranslations(),
]);
}
}
@@ -1,7 +1,7 @@
<?php <?php
namespace App\Handlers\Search\Presenters; namespace App\CMS\Handlers\Search\Presenters;
use App\Handlers\Contracts\SearchPresenterHandler; use App\CMS\Handlers\Contracts\SearchPresenterHandler;
class AdminFiles implements SearchPresenterHandler class AdminFiles implements SearchPresenterHandler
{ {
@@ -14,6 +14,6 @@ class AdminFiles implements SearchPresenterHandler
*/ */
public function render($foundItems) public function render($foundItems)
{ {
return view('admin.fileManager.filesTiles', ['files' => $foundItems]); return view('admin.fileManager.filesTiles', ['files' => $foundItems, 'hideDelete' => true]);
} }
} }
@@ -1,7 +1,7 @@
<?php <?php
namespace App\Handlers\Search; namespace App\CMS\Handlers\Search;
use App\Handlers\Contracts\SearchHandler; use App\CMS\Handlers\Contracts\SearchHandler;
use Symfony\Component\Process\Exception\InvalidArgumentException; use Symfony\Component\Process\Exception\InvalidArgumentException;
abstract class Search implements SearchHandler abstract class Search implements SearchHandler
+132
View File
@@ -0,0 +1,132 @@
<?php
namespace App\CMS\Helpers;
use App\CMS\Contracts\Modules\TemplateHandler;
use App\Models\Content\Content;
use App\Models\Module;
use App\Models\User;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\Cache;
class CurrentTemplate implements TemplateHandler
{
protected static $template;
protected static $initialized = false;
/**
* Getter for currently set template
* @return Module
* @throws \ErrorException
*/
public static function get()
{
if (!self::$initialized) {
self::$template = Cache::rememberForever('current-template', function () {
$template = Module::where('is_template', true)
->where('enabled', true)
->first();
// todo remove after adding $casts into Module model
if ($template) {
$template->module_settings = json_decode($template->module_settings);
}
return $template;
});
self::$initialized = true;
}
return self::$template;
}
/**
* Shortcut for getting template handler
*
* @return string
* @throws \ErrorException
*/
public static function handler()
{
$template = self::get();
if (!$template) {
throw new \ErrorException('No template selected, no handler available!');
}
return $template->handler;
}
/**
* Reset currently set template and load another one on next call of get() method.
*/
public static function reset()
{
self::$initialized = false;
}
/**
* Register admin menu (or multiple ones)
*
* This cannot be in ServiceProvider because Session is started at middleware. And ServiceProvider is called before
* starting any middleware..
*
* @param User $user model of logged user
*/
public static function registerAdminMenu(User $user)
{
call_user_func_array([self::handler(), 'registerAdminMenu'], [$user]);
}
/**
* Get list of registered templates and supported modules.
*
* Returned array is expected to be array of arrays with following items: module, namespace and templateName.
* Example:
* return [
* ['module' => 'Article', 'templateName' => 'article::template.name']
* ];
*
* When no "namespace" is specified, value strtolower(module) is used instaed.
*
* @return array
*/
public static function getPublicTemplatePaths()
{
return call_user_func([self::handler(), 'getPublicTemplatePaths']);
}
/**
* Prepare Content object to be rendered using Blade template
*
* @param Content $content
*/
public static function beforeRender(Content & $content)
{
call_user_func_array([self::handler(), 'beforeRender'], [&$content]);
}
/**
* Modify input data into format which can be understood by edit template
*
* @param Content $content
*/
public static function beforePopulatingEditForm(Content & $content)
{
call_user_func_array([self::handler(), 'beforePopulatingEditForm'], [&$content]);
}
/**
* Slightly modify Content object before saving into database.
*
* This way we can do almost whatever we want - even in template
*
* @param Content $content
* @param $request
*/
public static function beforeSave(Content & $content, $request)
{
call_user_func_array([self::handler(), 'beforeSave'], [&$content, $request]);
}
}
+64
View File
@@ -0,0 +1,64 @@
<?php
namespace App\CMS\Helpers;
class Date
{
/**
* Print date in configured format
*
* @param $datestamp
* @param bool $withTime
*
* @return false|string
*/
public static function date($datestamp, $withTime = true)
{
$timestamp = strtotime($datestamp);
if ($timestamp <= 0) {
return '';
}
$format = $withTime ? config('app.datetime_format') : config('app.date_format');
return date($format, $timestamp);
}
public static function dateFormatted($format, $datestamp)
{
$timestamp = strtotime($datestamp);
if ($timestamp <= 0) {
return '';
}
return date($format, $timestamp);
}
public static function monthName($datestamp) {
$timestamp = strtotime($datestamp);
if ($timestamp <= 0) {
return '';
}
$months = [
trans('common.months.january'),
trans('common.months.february'),
trans('common.months.march'),
trans('common.months.april'),
trans('common.months.may'),
trans('common.months.june'),
trans('common.months.july'),
trans('common.months.august'),
trans('common.months.semptember'),
trans('common.months.october'),
trans('common.months.november'),
trans('common.months.december'),
];
$monthNumber = date('n', $timestamp);
return $months[$monthNumber - 1];
}
}
@@ -1,5 +1,5 @@
<?php <?php
namespace App\Helpers; namespace App\CMS\Helpers;
/* /*
Paul's Simple Differ Algorithm v 0.1 Paul's Simple Differ Algorithm v 0.1
@@ -1,7 +1,7 @@
<?php <?php
namespace App\Helpers; namespace App\CMS\Helpers;
use App\Models\File; use App\Models\FileManager\File;
class FileManager class FileManager
{ {
@@ -17,34 +17,42 @@ class FileManager
switch ($type) { switch ($type) {
case 'image': case 'image':
// try to check if file have thumbnail $url = Path::thumbnail($file->filename);
if (file_exists(config('app.thumbnail_url') . '/' . $file->filename)) {
$url = config('app.thumbnail_url') . '/' . $file->filename;
} else {
$url = config('app.upload_url') . '/' . $file->filename;
}
return '<img src="' . $url . '" alt="' . $file->description . '" />'; return '<img src="' . $url . '" alt="' . $file->description . '" />';
break; break;
case 'text': case 'text':
return '<i class="fa fa-file-text-o"></i>'; return self::getIcon($file, 'fa-file-text-o');
break; break;
case 'application': case 'application':
switch ($subtype) { switch ($subtype) {
case 'pdf': case 'pdf':
return '<i class="fa fa-file-pdf-o"></i>'; return self::getIcon($file, 'fa-file-pdf-o');
break; break;
default: default:
return '<i class="fa fa-newspaper-o"></i>'; return self::getIcon($file, 'fa-newspaper-o');
break; break;
} }
break; break;
default: default:
return '<i class="fa fa-question"></i>'; return self::getIcon($file, 'fa-question');
break; break;
} }
} }
/**
* Shortcut for displaying icons
*
* @param $file
* @param $icon
*
* @return string
*/
protected static function getIcon($file, $icon)
{
return '<p><i class="fa fa-2x ' . $icon . '"></i><br /><span>' . e(str_limit($file->original_filename, 15)) . '</span></p>';
}
/** /**
* *
* *
@@ -83,7 +91,6 @@ class FileManager
return '<img src="' . $url . '" alt="' . $file->description . '" />'; return '<img src="' . $url . '" alt="' . $file->description . '" />';
break; break;
case 'text': case 'text':
$string = '<pre><code>'; $string = '<pre><code>';
$string .= htmlspecialchars(file_get_contents($path)); $string .= htmlspecialchars(file_get_contents($path));
$string .= '</pre></code>'; $string .= '</pre></code>';
@@ -91,15 +98,8 @@ class FileManager
return $string; return $string;
break; break;
case 'application': case 'application':
switch ($subtype) { return '<iframe src = "'.asset('resources/assets/admin/js/viewerJs/').'/#'.Path::upload($file->filename).'" style="width: 100%; height: 50em" allowfullscreen webkitallowfullscreen></iframe>';
case 'pdf': break;
return '<i class="fa fa-file-pdf-o"></i>';
break;
default:
return '<i class="fa fa-newspaper-o"></i>';
break;
}
break;
default: default:
return self::getPreview($file); return self::getPreview($file);
break; break;
@@ -119,19 +119,12 @@ class FileManager
switch ($type) { switch ($type) {
case 'text': case 'text':
return [trans('admin/fileManager.encoding') => mb_detect_encoding(file_get_contents($path))];
return [
trans('admin/fileManager.encoding') => mb_detect_encoding(file_get_contents($path))
];
break; break;
case 'image': case 'image':
list ($width, $height) = getimagesize($path); list ($width, $height) = getimagesize($path);
return [trans('admin/fileManager.resolution') => $height . ' x ' . $width,];
return [
trans('admin/fileManager.resolution') => $height . ' x ' . $width,
];
break; break;
} }
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\CMS\Helpers;
class Gravatar
{
public static function img($email, $size = 50)
{
return "//www.gravatar.com/avatar/" . md5(strtolower(trim($email))) . "?s=" . $size;
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace App\CMS\Helpers;
use App\CMS\Handlers\Contracts\AppHandler;
use App\Models\Content\Content;
use Webwizo\Shortcodes\Facades\Shortcode;
/**
* Class PageRenderer
*
* Simple class for rendering page for visitor
*
* @package App\CMS\Helpers
*/
class PageRenderer
{
/**
* Render page
*
* @param Content $page
*
* @return mixed
*/
public static function render(Content $page)
{
// do something template-specific
$parameters = call_user_func_array([CurrentTemplate::handler(), 'beforeRender'], [&$page]);
/**
* @var $handler AppHandler
*/
// get handler
$handler = new $page->moduleHandler;
$page->content = Shortcode::compile($page->content);
// render page
return $handler->render($page, $parameters);
}
}
@@ -1,46 +1,11 @@
<?php <?php
namespace App\Helpers; namespace App\CMS\Helpers;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Process\Exception\InvalidArgumentException; use Symfony\Component\Process\Exception\InvalidArgumentException;
class Template class Path
{ {
/**
* Getter for real template name
*
* @param $template
*
* @return null|string
*/
public static function name($template)
{
$namespace = null;
if (strpos($template, '::')) {
list($namespace, $template) = explode('::', $template);
$namespace .= '::';
}
$theme = config('app.theme');
// list of prefixes
$prefixes = [$theme . '.custom.', $theme . '.', ''];
$view = null;
foreach ($prefixes as $prefix) {
if (view()->exists($namespace . 'visitor.' . $prefix . $template)) {
$view = $namespace . 'visitor.' . $prefix . $template;
break;
}
}
if (is_null($view)) {
throw new InvalidArgumentException('Cannot find template for ' . $namespace . $template);
}
return $view;
}
/** /**
* Get URL to uploaded file * Get URL to uploaded file
* *
@@ -59,6 +24,25 @@ class Template
return config('app.upload_url') . '/' . $filename; return config('app.upload_url') . '/' . $filename;
} }
public static function thumbnail($filename)
{
// try to check if file have thumbnail
if (file_exists(config('app.thumbnail_path') . '/' . $filename)) {
$url = config('app.thumbnail_url') . '/' . $filename;
} else {
$url = self::upload($filename);
}
return $url;
}
/**
* Get path to given asset
*
* @param $path
*
* @return string
*/
public static function asset($path) public static function asset($path)
{ {
if (env('APP_DEBUG')) { if (env('APP_DEBUG')) {
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace App\CMS\Helpers;
use App\Models\FileManager\Directory;
use App\Models\FileManager\File;
class RootDirectory
{
protected static $directory;
/**
* Create root directory
*
* @return Directory
*/
protected static function create()
{
$directory = new Directory();
$directory->name = 'Root';
$directory->is_root = true;
$directory->save();
Directory::whereNull('parent_id')
->where('is_root', false)
->update(['parent_id' => $directory->id]);
File::where('directory_id', null)
->update(['directory_id' => $directory->id]);
return $directory;
}
/**
* Get root directory
*
* @return Directory
*/
public static function get()
{
self::$directory = Directory::where('is_root', true)
->first();
if (is_null(self::$directory)) {
self::$directory = self::create();
}
return self::$directory;
}
}
+86
View File
@@ -0,0 +1,86 @@
<?php
namespace App\CMS\Helpers;
use App\Models\Settings as SettingsModel;
use Illuminate\Support\Facades\Cache;
/**
* Class Settings
*
* Helper class for easier obtaining system settings
*
* @package App\CMS\Helpers
*/
class Settings
{
/**
* @var array List of all used settings
*/
protected static $settings;
/**
* @var bool Are data already initialized?
*/
protected static $initialized = false;
/**
* Getter for settings determined with key.
*
* @param string $key settings key
* @param string|null $default default value, which will be returned when given key does not exist
*
* @param null $exists
*
* @return null|string
*/
public static function get($key, $default = null, & $exists = null)
{
self::init();
if (isset(self::$settings->$key)) {
$exists = true;
self::$initialized = true;
return self::$settings->$key;
}
$exists = false;
return $default;
}
/**
* Shortcut for obtaining settings saved as JSON
*
* @param $key
*
* @return mixed
*/
public static function getJson($key)
{
return json_decode(self::get($key, '{}'));
}
/**
* Get all settings
*
* @return array
*/
public static function all()
{
self::init();
return self::$settings;
}
/**
* Initialize settings array
*/
private static function init()
{
if (!self::$initialized) {
self::$settings = Cache::remember('settings', config('cache.settingCacheLifetime'), function () {
$model = new SettingsModel();
return $model->getAll();
});
}
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace App\CMS\Helpers;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Process\Exception\InvalidArgumentException;
class Template
{
/**
* Getter for real template name
*
* @param $template
*
* @param bool $throwExceptions
*
* @return null|string
*/
public static function name($template, $throwExceptions = true)
{
$namespace = null;
if (strpos($template, '::')) {
list($namespace, $template) = explode('::', $template);
$namespace .= '::';
}
// list of prefixes
$prefixes = ['custom.', ''];
$view = null;
foreach ($prefixes as $prefix) {
if (view()->exists($namespace . 'visitor.' . $prefix . $template)) {
$view = $namespace . 'visitor.' . $prefix . $template;
break;
}
}
if (is_null($view)) {
if ($throwExceptions) {
throw new InvalidArgumentException('Cannot find template for ' . $namespace . $template);
} else {
return false;
}
}
return $view;
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace App\CMS\Helpers\Traits;
use Carbon\Carbon;
/**
* Class Nullable
*
* @package App\CMS\Helpers\Traits
*/
trait Nullable {
/**
* Get null or parsed date
*
* @param $date
*
* @return null|string
*/
public function nullableDate($date) {
return empty($date) ? null : Carbon::parse($date)->toDateTimeString();
}
/**
* Get null or input value
*
* @param $string
*
* @return null
*/
public function nullable($string) {
return empty($string) ? null : $string;
}
}
+18
View File
@@ -0,0 +1,18 @@
<?php
namespace App\CMS\Helpers;
class Widget
{
public static function render(\App\Models\Widgets\Widget $widget)
{
$handler = $widget->widget_type_handler;
if (is_string($widget->settings)) {
$widget->settings = json_decode($widget->settings);
}
$presenter = call_user_func([$handler, 'getPresenter']);
return call_user_func([$presenter, 'render'], $widget);
}
}
+75
View File
@@ -0,0 +1,75 @@
<?php
namespace App\CMS\Helpers;
use App\Models\Widgets\WidgetArea;
use Illuminate\Support\Facades\Cache;
class WidgetAreas
{
/**
* @var array list of inicialized widget areas
*/
protected static $widgetAreas = [];
protected static $initialized = false;
protected static function saveRenderedWidgetArea($widgets)
{
foreach ($widgets as $widget) {
if (!isset(self::$widgetAreas[$widget->widget_area_code])) {
self::$widgetAreas[$widget->widget_area_code] = '';
}
self::$widgetAreas[$widget->widget_area_code] .= Widget::render($widget);
}
}
protected static function loadWidgetArea($uniqueCode)
{
$widgets = Cache::remember('widget-' . $uniqueCode, setting('widgetCacheLifetime', 5), function () use ($uniqueCode) {
$widgetArea = new WidgetArea();
return $widgetArea->getWidgets($uniqueCode)
->get();
});
WidgetArea::where('code', $uniqueCode)
->increment('viewed');
self::saveRenderedWidgetArea($widgets);
}
protected static function initWidgetAreas()
{
if (self::$initialized) {
return;
}
$widgets = Cache::remember('widgets', setting('widgetCacheLifetime', 5), function () {
$widgetArea = new WidgetArea();
return $widgetArea->getWidgets()
->get();
});
self::saveRenderedWidgetArea($widgets);
self::$initialized = true;
}
/**
* Renderer for whole widget area.
*
* @param $uniqueCode
*
* @return mixed
*/
public static function render($uniqueCode)
{
if (!self::$initialized) {
self::initWidgetAreas();
}
if (!isset(self::$widgetAreas[$uniqueCode])) {
self::loadWidgetArea($uniqueCode);
}
echo isset(self::$widgetAreas[$uniqueCode]) ? self::$widgetAreas[$uniqueCode] : null;
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
/**
* Define function for escaping characters.
*
* This funcion prevents of creating build-in function, which escapes also accents...
*
* @param $value
*
* @return string
*/
function e($value)
{
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
}
/**
* Shortcut for \App\CMS\Helpers\Settings::get()
*
* @param $key
* @param null $default
*
* @return null|string
*/
function setting($key, $default = null) {
return \App\CMS\Helpers\Settings::get($key, $default);
}
function asset_path($path) {
return env('SITE_URL').'/assets/'.$path;
}
function template_name($name, $throwExceptions = true) {
return \App\CMS\Helpers\Template::name($name, $throwExceptions);
}
@@ -0,0 +1,38 @@
<?php
namespace App\CMS\Interactions\Admin\Content;
use App\CMS\Contracts\Interaction;
use App\CMS\Enumerations\ContentStatuses;
class ChangeStatus implements Interaction
{
/**
* Change status of content page
*
* @param array $data
* @param array $parameters
*
* @return mixed|void
*/
public function handle($data, $parameters)
{
list ($page) = $parameters;
$page->status = $data['status'];
$page->save();
}
/**
* Check if status is valid
*
* @param array $parameters
*
* @return array
*/
public function getValidationRules($parameters = [])
{
$statuses = ContentStatuses::getWithTranslations();
return ['status' => 'in:' . implode(',', array_keys($statuses))];
}
}
@@ -0,0 +1,45 @@
<?php
namespace App\CMS\Interactions\Admin\Content;
use App\CMS\Contracts\Interaction;
use App\CMS\Contracts\Repositories\Module;
use App\Models\Content\Content;
use Illuminate\Support\Facades\App;
class DeletePage implements Interaction
{
/**
* Perform given action
*
* @param $data array input data
* @param $parameters array parameters
*
* @return void
*/
public function handle($data, $parameters)
{
/**
* @var $page Content
* @var $module Module
*/
$module = App::make(Module::class);
list ($page) = $parameters;
// delete page
$page->delete();
// let module to delete some extra content created during page's life
$module->callOnString($page->moduleHandler, 'delete', [$page]);
}
/**
* @param $data array input data
*
* @return array
*/
public function getValidationRules($data = [])
{
return [];
}
}
@@ -0,0 +1,92 @@
<?php
namespace App\Cms\Interactions\Admin\Content;
use App\CMS\Contracts\Interaction;
use App\CMS\Contracts\Repositories\Module;
use App\CMS\Enumerations\ContentStatuses;
use App\CMS\Helpers\CurrentTemplate;
use App\Events\EditContent as EditContentEvent;
use App\Http\Requests\Request;
use App\Models\Content\Content;
use App\Models\FileManager\File;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Str;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
class EditContent implements Interaction
{
/**
* @var Module
*/
protected $module;
/**
* @var File
*/
protected $file;
public function __construct()
{
$this->module = App::make(Module::class);
$this->file = App::make(File::class);
}
/**
* Edit page
*
* @param array $data
* @param array $parameters
*
* @return mixed|void
*/
public function handle($data, $parameters)
{
/**
* @var $page Content
*/
list($page, $titlePhoto, $relatedFiles) = $parameters;
// let template to modify request and content page
CurrentTemplate::beforeSave($page, $data);
// upload file - if provided
$data['titlePhoto'] = isset($data['existingTitlePhoto']) ? $data['existingTitlePhoto'] : null;
if (!is_null($titlePhoto)) {
$file = $this->file->upload($titlePhoto);
$data['titlePhoto'] = $file ? $file['filename'] : null;
}
$page->update($data);
$this->module->callOnString($page->moduleHandler, 'edit', [$page, $data]);
// move to interaction
$keepAssigned = isset($data->keepAssigned) ? $data->keepAssigned : [];
$page->uploadRelatedFiles($relatedFiles, $keepAssigned);
}
/**
* Validate this interaction
*
* @param array $parameters
*
* @return array
*/
public function getValidationRules($parameters = [])
{
$id = $parameters[0]->id;
// get all available statuses
$statuses = ContentStatuses::getSimple();
$statuses = implode(',', $statuses);
return [
'title' => 'required',
'url' => 'required|unique:contents,url,' . $id.',id,deleted_at,NULL,language,' . LaravelLocalization::getCurrentLocale(),
'publishedFrom' => 'date',
'publishedTo' => 'date',
'status' => 'required|in:' . $statuses,
'parentPage' => 'exists:contents,id'
];
}
}
@@ -0,0 +1,89 @@
<?php
namespace App\Cms\Interactions\Admin\Content;
use App\CMS\Contracts\Interaction;
use App\CMS\Contracts\Repositories\Content as ContentRepository;
use App\CMS\Contracts\Repositories\Module;
use App\CMS\Enumerations\ContentStatuses;
use App\CMS\Helpers\CurrentTemplate;
use App\Models\Content\Content;
use App\Models\FileManager\File;
use Illuminate\Support\Facades\App;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
class NewContent implements Interaction
{
/**
* @var ContentRepository
*/
protected $content;
/**
* @var File
*/
protected $file;
/**
* @var Module
*/
protected $module;
public function __construct()
{
$this->content = App::make(ContentRepository::class);
$this->module = App::make(Module::class);
$this->file = App::make(File::class);
}
/**
* Create new content page
*
* @param array $data
* @param $parameters
*
* @return Content
*/
public function handle($data, $parameters)
{
list($moduleHandler, $titlePhoto, $relatedFiles) = $parameters;
// upload file - if provided
$file = !is_null($titlePhoto) ? $this->file->upload($titlePhoto) : null;
$data['userId'] = \Auth::user()->id;
$data['language'] = LaravelLocalization::getCurrentLocale();
$data['titlePhoto'] = $file ? $file['filename'] : null;
$data['moduleHandler'] = $moduleHandler;
$page = Content::create($data);
// save module-specific stuff
$this->module->callOnString($moduleHandler, 'createNew', [&$page, $data]);
// let template to modify data and content page
CurrentTemplate::beforeSave($page, $data);
// save content of page before (possible) uploading related files
$page->save();
// upload files assigned to content page
$page->uploadRelatedFiles($relatedFiles);
return $page;
}
public function getValidationRules($parameters = [])
{
// get all available statuses
$statuses = ContentStatuses::getSimple();
$statuses = implode(',', $statuses);
return [
'titlePhoto' => 'image',
'title' => 'required',
'url' => 'required|unique:contents,url,NULL,id,deleted_at,NULL,language,' . LaravelLocalization::getCurrentLocale(),
'status' => 'required|in:'.$statuses,
'parentPage' => 'exists:contents,id',
'publishedFrom' => 'date',
'publishedTo' => 'date',
];
}
}
@@ -0,0 +1,43 @@
<?php
namespace App\CMS\Interactions\Admin\Content;
use App\CMS\Contracts\Interaction;
use App\CMS\Enumerations\ContentStatuses;
use App\Models\Content\Content;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
class SetHomepage implements Interaction
{
/**
* Set homepage of given page
*
* @param array $data
* @param array $parameters
*
* @return void
*/
public function handle($data, $parameters)
{
list ($page) = $parameters;
// disable any other existing homepage
Content::where('isHomepage', true)
->where('language', LaravelLocalization::getCurrentLocale())
->update(['isHomepage' => false]);
// set new homepage
$page->isHomepage = true;
$page->save();
}
/**
* Check if status is valid
*
* @param array $parameters
*
* @return array
*/
public function getValidationRules($parameters = [])
{
return [];
}
}
@@ -0,0 +1,65 @@
<?php
namespace App\CMS\Interactions\Admin\FileManager;
use App\CMS\Contracts\Interaction;
use App\CMS\Exceptions\FileManager\SubdirectoryExistsInDirectory;
use App\CMS\Repositories\FileManager\Directory as DirectoryRepository;
use App\Models\FileManager\Directory;
class CreateDirectory implements Interaction
{
/**
* @var DirectoryRepository
*/
private $repository;
public function __construct(DirectoryRepository $repository)
{
$this->repository = $repository;
}
/**
* Perform given action
*
* @param $data array input data
* @param $parameters array parameters
*
* @return Directory
* @throws SubdirectoryExistsInDirectory
*/
public function handle($data, $parameters)
{
$data['parentDirectory'] = isset($data['parentDirectory']) ? $data['parentDirectory'] : null;
// todo this should probably go into validation, but we are not able to get $data in getValidationRules
// check if created folder does not exist in current directory
if ($this->repository->checkIfNameExists($data['name'], $data['parentDirectory'])) {
throw new SubdirectoryExistsInDirectory();
}
// todo use Directory::create() and rename columns/fields in request
// create new database entry
$directory = new Directory();
$directory->name = $data['name'];
$directory->parent_id = $data['parentDirectory'];
$directory->user_id = \Auth::user()->id;
$directory->description = $data['description'];
$directory->save();
return $directory;
}
/**
* @param $data array input data
*
* @return array
* @throws SubdirectoryExistsInDirectory
*/
public function getValidationRules($data = [])
{
return [
'name' => 'required|max:255',
'parentDirectory' => 'exists:directories,id'
];
}
}
@@ -1,5 +1,5 @@
<?php <?php
namespace App\MenuPresenters; namespace App\CMS\MenuPresenters;
use Pingpong\Menus\MenuItem; use Pingpong\Menus\MenuItem;
use Pingpong\Menus\Presenters\Presenter; use Pingpong\Menus\Presenters\Presenter;
@@ -77,6 +77,6 @@ class FoundationDrilldownMenuPresenter extends Presenter
<ul class="vertical menu"> <ul class="vertical menu">
' . $this->getChildMenuItems($item) . ' ' . $this->getChildMenuItems($item) . '
</ul> </ul>
</li>' . PHP_EOL;; </li>' . PHP_EOL;
} }
} }
@@ -1,5 +1,5 @@
<?php <?php
namespace App\MenuPresenters; namespace App\CMS\MenuPresenters;
use Pingpong\Menus\Presenters\Presenter; use Pingpong\Menus\Presenters\Presenter;
@@ -56,6 +56,6 @@ class FoundationDropdownMenuPresenter extends Presenter
<ul class="vertical menu"> <ul class="vertical menu">
' . $this->getChildMenuItems($item) . ' ' . $this->getChildMenuItems($item) . '
</ul> </ul>
</li>' . PHP_EOL;; </li>' . PHP_EOL;
} }
} }
@@ -1,5 +1,5 @@
<?php <?php
namespace App\MenuPresenters; namespace App\CMS\MenuPresenters;
class FoundationInitializedDropdownMenuPresenter extends FoundationDropdownMenuPresenter class FoundationInitializedDropdownMenuPresenter extends FoundationDropdownMenuPresenter
{ {
@@ -0,0 +1,85 @@
<?php
namespace App\CMS\MenuPresenters;
use Pingpong\Menus\MenuItem;
use Pingpong\Menus\Presenters\Presenter;
class MetismenuPresenter extends Presenter
{
/**
* {@inheritdoc }
*/
public function getOpenTagWrapper()
{
return PHP_EOL . '<ul class="nav metismenu" id="sidebar-menu">' . PHP_EOL;
}
/**
* {@inheritdoc }
*/
public function getCloseTagWrapper()
{
return PHP_EOL . '</ul>' . PHP_EOL;
}
/**
* {@inheritdoc }
*/
public function getMenuWithoutDropdownWrapper($item)
{
return '<li' . $this->getActiveState($item) . '><a href="' . $item->getUrl() . '">' . $item->getIcon() . ' ' . $item->title . '</a></li>' . PHP_EOL;
}
/**
* {@inheritdoc }
*/
public function getActiveState(MenuItem $item)
{
if ($item->isDivider()) {
return null;
}
$active = ' class="active"';
if (empty($item->getChilds())) {
// for direct menu input return if page is active
return \Request::is($item->getRequest()) ? $active : null;
} else {
// on nested menu inputs loop over children and in case any of them is active, mark active also root level
foreach ($item->getChilds() as $child) {
if ($this->getActiveState($child)) {
return $active;
}
}
}
return null;
}
/**
* {@inheritdoc }
*/
public function getDividerWrapper()
{
return '<li class="divider"></li>' . PHP_EOL;
}
public function getMultiLevelDropdownWrapper($item)
{
return $this->getMenuWithDropDownWrapper($item);
}
/**
* {@inheritdoc }
*/
public function getMenuWithDropDownWrapper($item)
{
$active = $this->getActiveState($item);
return '<li' . $active . '>
<a href="#">' . $item->getIcon() . ' ' . $item->title . ' <i class="fa arrow"></i></a>
<ul>
' . $this->getChildMenuItems($item) . '
</ul>
</li>' . PHP_EOL;
}
}
@@ -0,0 +1,61 @@
<?php
namespace App\CMS\MenuPresenters;
use Pingpong\Menus\MenuItem;
use Pingpong\Menus\Presenters\Presenter;
class ProfileMenuPresenter extends Presenter
{
/**
* {@inheritdoc }
*/
public function getOpenTagWrapper()
{
return PHP_EOL . '<div class="dropdown-menu profile-dropdown-menu">' . PHP_EOL;
}
/**
* {@inheritdoc }
*/
public function getCloseTagWrapper()
{
return PHP_EOL . '</div>' . PHP_EOL;
}
/**
* {@inheritdoc }
*/
public function getMenuWithoutDropdownWrapper($item)
{
return '<a href="' . $item->getUrl() . '" class="dropdown-item">' . $item->getIcon() . ' ' . $item->title . '</a>' . PHP_EOL;
}
/**
* {@inheritdoc }
*/
public function getActiveState(MenuItem $item)
{
return null;
}
/**
* {@inheritdoc }
*/
public function getDividerWrapper()
{
return '<div class="dropdown-divider"></div>' . PHP_EOL;
}
public function getMultiLevelDropdownWrapper($item)
{
return null;//$this->getMenuWithDropDownWrapper($item);
}
/**
* {@inheritdoc }
*/
public function getMenuWithDropDownWrapper($item)
{
return null;
}
}
+168
View File
@@ -0,0 +1,168 @@
<?php
namespace App\CMS\Observers;
use App\CMS\Contracts\Repositories\Content as ContentContract;
use App\Events\CreateNewContent;
use App\Events\DeleteContent;
use App\Events\EditContent;
use App\Events\MakeDraftFromContent;
use App\Events\PublishContent;
use App\Events\SetHomepage;
use App\Models\Content\Content as ContentModel;
use App\Models\Content\ContentHistory;
use Illuminate\Support\Facades\Event;
/**
* Class Content
*
* Model observer for logging changes on content page.
*
* @package App\CMS\Observers
*/
class Content
{
/**
* @var string
*/
protected $changeset;
public function __construct()
{
$this->changeset = uniqid();
}
/**
* Log changes on edit
*
* @param ContentModel $content
*/
public function updated(ContentModel $content)
{
$fields = $this->getDirtyFields($content->getDirty(), $content->getOriginal());
$normalFieldsEdited = 0;
foreach ($fields as $field => $change) {
if ($field == 'viewed')
continue;
$history = [
'user_id' => \Auth::user()->id,
'content_id' => $content->id,
'edit_batch' => $this->changeset,
'column' => $field,
'old_value' => $change['old'],
'new_value' => $change['new']
];
// save history
ContentHistory::create($history);
// fire event
$this->fireEvent($field, $change, $content, $normalFieldsEdited);
}
if ($normalFieldsEdited > 0) {
Event::fire(new EditContent($content, $this->changeset));
}
}
/**
* Fire event when new instance of model is stored into database
*
* @param ContentModel $content
*/
public function created(ContentModel $content)
{
Event::fire(new CreateNewContent($content));
}
/**
* @param ContentModel $content
*/
public function deleted(ContentModel $content)
{
Event::fire(new DeleteContent($content));
}
/**
* Get all dirty changed from model.
*
* If user did not change moduleSettings column, return result from ContentModel::getDirty(). Otherwise append all
* changed columns from module settings.
*
* @param $changed
* @param $original
*
* @return array
*/
protected function getDirtyFields($changed, $original)
{
unset($changed['updated_at']);
$changes = $this->getNewAndOldValues(array_keys($changed), $changed, $original);
if (!isset($changed['moduleSettings'])) {
return $changes;
}
$moduleSettings = (array)json_decode($changed['moduleSettings']);
$originalSettings = isset($original['moduleSettings']) ? (array)json_decode($original['moduleSettings']) : [];
unset($changed['moduleSettings']);
$settingChanged = array_keys(array_diff_assoc($moduleSettings, $originalSettings));
$changesInSettings = $this->getNewAndOldValues($settingChanged, $moduleSettings, $originalSettings);
return array_merge($changes, $changesInSettings);
}
/**
* Get original and changed values in one array
*
* @param $fields
* @param $new
* @param $original
*
* @return array
*/
protected function getNewAndOldValues($fields, $new, $original)
{
$changes = [];
foreach ($fields as $field) {
$changes[$field] = [
'new' => isset($new[$field]) ? $new[$field] : null,
'old' => isset($original[$field]) ? $original[$field] : null
];
}
return $changes;
}
/**
* Fire events for publishing/making draft and setting homepage
*
* @param $field
* @param $change
* @param ContentModel $content
* @param $normalFieldsEdited
*/
protected function fireEvent($field, $change, ContentModel $content, &$normalFieldsEdited)
{
switch ($field) {
case 'status':
if ($change['new'] !== ContentContract::STATUS_DRAFT) {
Event::fire(new PublishContent($content, $this->changeset));
} else {
Event::fire(new MakeDraftFromContent($content, $this->changeset));
}
break;
case 'isHomepage':
Event::fire(new SetHomepage($content, $this->changeset));
break;
default:
$normalFieldsEdited++;
}
}
}
@@ -1,25 +1,32 @@
<?php <?php
namespace App\Cms\Repositories\ActionLog;
namespace App\Models; use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
/** class Actions
* Class ActionLog
*/
class ActionLog extends Model
{ {
protected $table = 'action_log'; /**
public $timestamps = false; * @param null $userId
protected $fillable = [ *
'type_id', * @return array
'user_id', */
'ip', public function getLoggedActions($userId = null)
'user_agent', {
'parameters' $actions = $this->getLogActionsQuery(true);
];
protected $guarded = []; $user = null;
if ($userId) {
$user = User::findOrFail($userId);
$actions->where('users.id', $userId);
}
$actions = $actions->paginate(25);
$this->translateLogMessages($actions);
return [$user, $actions];
}
/** /**
* Query factory for loading action logs with their type (therefore with name and value) * Query factory for loading action logs with their type (therefore with name and value)
@@ -36,7 +43,6 @@ class ActionLog extends Model
->orderBy('action_log.created_at', 'DESC'); ->orderBy('action_log.created_at', 'DESC');
if ($joinUser) { if ($joinUser) {
$actions = $actions->join('users', 'users.id', '=', 'action_log.user_id') $actions = $actions->join('users', 'users.id', '=', 'action_log.user_id')
->select('action_log.*', 'action_log_types.*', 'users.email', 'users.name as user_name'); ->select('action_log.*', 'action_log_types.*', 'users.email', 'users.name as user_name');
} }
@@ -75,8 +81,6 @@ class ActionLog extends Model
* Method used for translating and filling parameters into value of log. * Method used for translating and filling parameters into value of log.
* *
* @param $messages * @param $messages
*
* @return array
*/ */
public function translateLogMessages(&$messages) public function translateLogMessages(&$messages)
{ {
+133
View File
@@ -0,0 +1,133 @@
<?php
namespace App\CMS\Repositories\Content;
use App\CMS\Contracts\Repositories\Content as IContent;
use App\CMS\Enumerations\ContentStatuses;
use App\CMS\Repositories\Repository;
use App\Models\Content\Content as ContentModel;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
class Content extends Repository implements IContent
{
/**
* @var ContentModel
*/
protected $model;
public function __construct(ContentModel $model)
{
$this->model = $model;
}
/**
* Getter for query with setup filter for non-deleted and non-draft pages
*/
protected function getPageQuery()
{
return ContentModel::where('status', '!=', IContent::STATUS_DRAFT)
->select('contents.*')
->with('author')
->join('modules', 'modules.handler', '=', 'contents.moduleHandler')
->where(function ($query) {
$query->where('publishedFrom', '<=', Carbon::now())
->orWhereNull('publishedFrom');
})
->where(function ($query) {
$query->where('publishedTo', '>=', Carbon::now())
->orWhereNull('publishedTo');
})
->where('language', LaravelLocalization::getCurrentLocale())
->whereNull('contents.deleted_at')
->where('modules.enabled', true);
}
public function all()
{
return $this->getPageQuery()
->get();
}
/**
* @param int $id
*
* @return mixed
*/
public function getOne($id = null)
{
if (is_null($id)) {
$page = new ContentModel();
$page->user_id = \Auth::user()->id;
$page->language = LaravelLocalization::getCurrentLocale();
$page->status = IContent::STATUS_DRAFT;
$page->moduleSettings = [];
} else {
$page = $this->model->findOrFail($id);
}
return $page;
}
/**
* Getter for homepage content page
* @return Content
*/
public function getHomepage()
{
return $this->getPageQuery()
->where('isHomepage', 1)
->first();
}
/**
* Getter for any page specified by its url.
*
* @param $url
*
* @return Content
*/
public function getByURL($url)
{
return $this->getPageQuery()
->where('url', $url)
->first();
}
/**
* Getter for overview of all content pages.
* These content pages may be filtered by using some handler
*
* @param bool $handler
* @param null $statuses
*
* @return
*/
public function getOverview($handler = null, $statuses = null)
{
$query = ContentModel::select('contents.*', 'modules.name AS module_name', 'modules.id AS module_id')
->join('modules', 'modules.handler', '=', 'contents.moduleHandler')
->join('users', 'users.id', '=', 'contents.userId')
->addSelect(DB::raw('COALESCE(users.name, users.email) AS author_name'))
->addSelect('users.email AS author_email')
->whereNull('contents.deleted_at')
->where('contents.language', LaravelLocalization::getCurrentLocale())
->where('modules.enabled', 1)
->orderBy('contents.created_at', 'DESC');
// filter results using handler
if (!is_null($handler)) {
$query = $query->where('contents.moduleHandler', $handler);
}
// add filtering by status
if (is_array($statuses) && in_array($statuses, ContentStatuses::getSimple())) {
$query = $query->whereIn('contents.status', $statuses);
}
$pages = $query->paginate(setting('paginatedResults', 20));
return $pages;
}
}
@@ -0,0 +1,54 @@
<?php
namespace App\CMS\Repositories\FileManager;
use App\CMS\Helpers\RootDirectory;
use App\Models\FileManager\Directory as DirectoryModel;
class Directory
{
/**
* Checks if directory with given name does not exist in current directory
*
* @param $name
* @param $parentDirectoryId
* @param bool $ignoredId
*/
public function checkIfNameExists($name, $parentDirectoryId, $ignoredId = false)
{
$exists = DirectoryModel::where('name', $name)
->where('parent_id', $parentDirectoryId);
if ($ignoredId !== false) {
$exists = $exists->where('id', '!=', $ignoredId);
}
return $exists->exists();
}
public function update(DirectoryModel $directory, $data) {
$directory->name = $data['name'];
$directory->description = $data['description'];
$directory->save();
}
/**
* @param DirectoryModel $directory
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function getAllSubdirectories(DirectoryModel &$directory)
{
if (!$directory->exists) {
$directory = DirectoryModel::where('is_root', true)
->first();
if (is_null($directory)) {
$directory = RootDirectory::get();
}
}
$subdirectories = $directory->allSubdirectories();
return $subdirectories;
}
}
+82
View File
@@ -0,0 +1,82 @@
<?php
namespace App\CMS\Repositories;
use App\CMS\Contracts\Repositories\Module as IModule;
use App\Models\Content\Content;
use App\Models\Module as ModuleModel;
class Module extends Repository implements IModule
{
public function __construct(ModuleModel $module)
{
$this->model = $module;
}
public function getOne($id = null)
{
if (is_null($id)) {
return new ModuleModel();
}
if (is_numeric($id)) {
return ModuleModel::findOrFail($id);
} else {
return ModuleModel::where('alias', $id)
->firstOrFail();
}
}
public function getCreateContentTitle(ModuleModel $module)
{
$pageHeading = trans('admin/contents.new', ['name' => trans($module->name)]);
$moduleHeading = $this->call($module, 'getCreateNewTitle');
return is_null($moduleHeading) ? $pageHeading : $moduleHeading;
}
public function getUpdateContentTitle(ModuleModel $module, Content $content = null)
{
$pageHeading = trans('admin/contents.edit', ['name' => trans($module->name)]);
$moduleHeading = $this->call($module, 'getEditTitle', [$content]);
return is_null($moduleHeading) ? $pageHeading : $moduleHeading;
}
public function getOverviewTitle(ModuleModel $module)
{
$pageHeading = 'admin/contents.overview';
$moduleHeading = $this->call($module, 'getOverviewTitle');
return is_null($moduleHeading) ? $pageHeading : $moduleHeading;
}
public function all()
{
return ModuleModel::where('enabled', true)
->get();
}
public function getByHandler($handler)
{
return ModuleModel::where('handler', $handler)
->firstOrFail();
}
public function call(ModuleModel $module, $method, $parameters = [])
{
if (!is_null($module) && is_callable([$module->handler, $method])) {
return call_user_func_array([$module->handler, $method], $parameters);
}
return null;
}
public function callOnString($moduleHandler, $method, $parameters = [])
{
if (is_callable([$moduleHandler, $method])) {
return call_user_func_array([$moduleHandler, $method], $parameters);
}
return null;
}
}
+12
View File
@@ -0,0 +1,12 @@
<?php
namespace App\CMS\Repositories;
abstract class Repository
{
protected $model;
public function __call($method, $args)
{
return call_user_func_array([$this->model, $method], $args);
}
}
@@ -0,0 +1,75 @@
<?php
namespace App\Cms\Repositories\Templates;
use App\CMS\Contracts\Repositories\Template as ITemplate;
use App\CMS\Repositories\Repository;
use App\Models\Module;
use App\Models\Templates\Template as TemplateModel;
class Template extends Repository implements ITemplate
{
public function setModel(TemplateModel $model)
{
$this->model = $model;
}
/**
* Getter for absolute path to template
*
* @param null $mode
*
* @return string
*/
public function getPathToTemplate($mode = null)
{
$templateName = $this->model->template_name;
$path = $this->model->templatePath;
// delete namespace if given
if (strpos($this->model->template_name, '::')) {
list(, $templateName) = explode('::', $templateName);
}
// get template name, do not care about directories (they are already contained in path)
$parts = explode('.', $templateName);
$templateName = last($parts);
// just a shortcut for this unbelievable long constant name...
$_ = DIRECTORY_SEPARATOR;
// path parts
$absolutePath = base_path($path->path);
$templatePath = str_replace('.', $_, $templateName);
$file = $templatePath . ITemplate::TEMPLATE_SUFFIX;
// compose two versions of path - original and with "custom" directory
$original = $absolutePath . $_ . $file;
$edited = $absolutePath . $_ . ITemplate::EDITED_TEMPLATES_DIRECTORY . $_ . $file;
// decide which path will be returned
switch ($mode) {
case ITemplate::CUSTOM_EDIT_DIRECTORY:
return $edited;
break;
case ITemplate::ORIGINAL_DIRECTORY:
return $original;
break;
default:
if (file_exists($absolutePath . $_ . ITemplate::EDITED_TEMPLATES_DIRECTORY . $_ . $file)) {
return $edited;
}
return $original;
break;
}
}
public function getForModule(Module $module)
{
return TemplateModel::where('module_id', $module->id)
->select(['template_name', 'name', 'description'])
->get();
}
}
+88
View File
@@ -0,0 +1,88 @@
<?php
namespace App\CMS\Shortcodes;
use App\Models\Widgets\Widget as WidgetModel;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
class Widget
{
protected static $widgets = [];
protected static $initialized = false;
protected function getRenderedWidget(WidgetModel $widget)
{
$widget->settings = json_decode($widget->settings);
$handler = $widget->widget_type_handler;
$presenter = call_user_func([$handler, 'getPresenter']);
return call_user_func([$presenter, 'render'], $widget);
}
/**
* Load one widget from database
*
* @param $containerId
*/
protected function loadWidget($containerId)
{
$widget = WidgetModel::where('container_id', $containerId)
->first();
if (!$widget) {
return;
}
$widget->timestamps = false;
$widget->viewed++;
$widget->save();
$widget->timestamps = true;
self::$widgets[$containerId] = $this->getRenderedWidget($widget);
}
/**
* Load all frequently used widgets
*
* Frequency of loaded widgets is counted only using direct loading, widgets displayed in widget areas are omitted
*/
protected function initWidgets()
{
if (self::$initialized) {
return;
}
$widgets = WidgetModel::where('viewed', '>=', setting('preloadFrequentedWidgetsTreshold', 10))
->where('language', LaravelLocalization::getCurrentLocale())
->get();
foreach ($widgets as $widget) {
self::$widgets[$widget->container_id] = $this->getRenderedWidget($widget);
}
self::$initialized = true;
}
/**
* Register new shortcode
*
* Initfrequently used widgets and if not presented yet - load desired widget specified as content of shortcode
*
* @param $shortcode
* @param $containerId
* @param $compiler
* @param $name
*
* @return mixed
*/
public function register($shortcode, $containerId, $compiler, $name)
{
$this->initWidgets();
if (!isset(self::$widgets[$containerId])) {
$this->loadWidget($containerId);
}
return self::$widgets[$containerId];
}
}
@@ -1,13 +1,12 @@
<?php <?php
namespace App\Widgets\HtmlCode; namespace App\CMS\Widgets\HtmlCode;
use App\CMS\Contracts\Widgets\PublicWidget;
use App\CMS\Widgets\Exceptions\MissingParameterException;
use App\Http\Requests\Request; use App\Http\Requests\Request;
use App\Models\Widget; use App\Models\Widgets\Widget;
use App\Widgets\Contracts\WidgetRenderer;
use App\Widgets\Exceptions\MissingParameterException;
use App\Widgets\NonInstallableWidget;
class HtmlCode extends NonInstallableWidget class HtmlCode implements PublicWidget
{ {
/** /**
* Name getter * Name getter
@@ -40,25 +39,17 @@ class HtmlCode extends NonInstallableWidget
} }
/** /**
* @param Widget $widget * @param Request $data
* @param \stdClass $data
* *
* @return Widget * @return array
* @throws MissingParameterException * @throws MissingParameterException
*/ */
public static function save(Widget $widget, \stdClass $data) public static function save(Request $data)
{ {
if (!isset($data->code) || empty($data->code)) { if (!isset($data->code) || empty($data->code)) {
throw new MissingParameterException('code'); throw new MissingParameterException('code');
} }
$widgetSettings = [ return ['code' => $data->code];
'code' => $data->code,
'containerClass' => isset($data->containerClass) ? $data->containerClass : ''
];
$widget->settings = json_encode($widgetSettings);
return $widget;
} }
} }
@@ -1,9 +1,9 @@
<?php <?php
namespace App\Widgets\HtmlCode\Presenters; namespace App\CMS\Widgets\HtmlCode\Presenters;
use App\Models\Widget; use App\Models\Widgets\Widget;
use App\Models\WidgetType; use App\Models\Widgets\WidgetType;
use App\Widgets\Contracts\WidgetRenderer; use App\CMS\Contracts\Widgets\WidgetRenderer;
class HtmlCode implements WidgetRenderer class HtmlCode implements WidgetRenderer
{ {
@@ -12,14 +12,15 @@ class HtmlCode implements WidgetRenderer
* *
* This method renders some HTML code for displaying. * This method renders some HTML code for displaying.
* *
* @param object $settings * @param Widget $widget
* *
* @return mixed * @return mixed
*/ */
public static function render($settings) public static function render(Widget $widget)
{ {
$settings = $widget->settings;
$code = ' $code = '
<div class="html-code-widget ' . $settings->containerClass . '"> <div class="html-code-widget ' . $widget->container_class . '">
' . $settings->code . ' ' . $settings->code . '
</div>'; </div>';
@@ -35,7 +36,11 @@ class HtmlCode implements WidgetRenderer
*/ */
public static function renderAddForm(WidgetType $type) public static function renderAddForm(WidgetType $type)
{ {
return view('admin.widgets.htmlCode.new', ['type' => $type]); return view('admin.widgets.htmlCode.new', [
'type' => $type,
'widget' => new Widget(),
'settings' => new \stdClass()
]);
} }
/** /**
@@ -47,7 +52,7 @@ class HtmlCode implements WidgetRenderer
*/ */
public static function renderEditForm(Widget $widget) public static function renderEditForm(Widget $widget)
{ {
$settings = json_decode($widget->settings); $settings = $widget->settings;
return view('admin.widgets.htmlCode.edit', ['widget' => $widget, 'settings' => $settings]); return view('admin.widgets.htmlCode.edit', ['widget' => $widget, 'settings' => $settings]);
} }
} }
@@ -1,7 +1,7 @@
<?php <?php
namespace App\Widgets\Menu\Extensions; namespace App\CMS\Widgets\Menu\Extensions;
use App\Widgets\Contracts\MenuExtensionHandler; use App\CMS\Widgets\Contracts\MenuExtensionHandler;
class URL implements MenuExtensionHandler { class URL implements MenuExtensionHandler {
public function getPossibleValues() public function getPossibleValues()
+87
View File
@@ -0,0 +1,87 @@
<?php
namespace App\CMS\Widgets\Menu;
use App\CMS\Contracts\Widgets\PublicWidget;
use App\CMS\Widgets\Contracts\WidgetRenderer;
use App\CMS\Widgets\Exceptions\MissingParameterException;
use App\Http\Requests\Request;
use App\Models\Content\Content;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
use App\CMS\Contracts\Repositories\Content as IContent;
class Menu implements PublicWidget
{
/**
* Name getter
*
* @return string
*/
public static function getName()
{
return 'widgets/menu.widgetName';
}
/**
* Getter for description
*
* @return string
*/
public static function getDescription()
{
return 'widgets/menu.widgetDescription';
}
/**
* Getter for presenter/renderer of this widget
*
* @return WidgetRenderer
*/
public static function getPresenter()
{
return Presenters\Menu::class;
}
/**
* @param \App\Models\Widget $widget
* @param \stdClass $data
*
* @return mixed
*/
public static function save(Request $data)
{
if (!isset($data->settings) || empty($data->settings) || !json_decode($data->settings)) {
throw new MissingParameterException('settings');
}
$links = json_decode($data->settings);
$pages = self::selectContentPages(array_keys((array)$links));
foreach ($links as $link) {
if (!isset($pages[$link->id])) {
unset($links->{$link->id});
}
}
return ['links' => $links];
}
public static function selectContentPages($ids)
{
$allowedStatuses = [IContent::STATUS_PUBLIC];
if (\Auth::user()) {
$allowedStatuses[] = IContent::STATUS_PROTECTED;
}
$pages = Content::where('language', LaravelLocalization::getCurrentLocale())
->whereIn('id', $ids)
->whereIn('status', $allowedStatuses)
->get();
$used = [];
foreach ($pages as $page) {
$used[$page->id] = $page;
}
return $used;
}
}
@@ -1,47 +1,35 @@
<?php <?php
namespace App\Widgets\Menu\Presenters; namespace App\CMS\Widgets\Menu\Presenters;
use App\Models\Content; use App\CMS\Contracts\Repositories\Content as ContentContract;
use App\Models\Widget; use App\CMS\Contracts\Widgets\WidgetRenderer;
use App\Models\WidgetType; use App\CMS\Widgets\Menu\Menu as WidgetMenu;
use App\Widgets\Contracts\WidgetRenderer; use App\Models\Widgets\Widget;
use App\Models\Widgets\WidgetType;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Request; use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
class Menu implements WidgetRenderer class Menu implements WidgetRenderer
{ {
protected static function selectContentPages($ids)
{
$pages = Content::where('status', '!=', Content::STATUS_DELETED)
->whereIn('id', $ids)
->get();
$used = [];
foreach ($pages as $page) {
$used[$page->id] = $page;
}
return $used;
}
/** /**
* Method used for displaying widget. * Method used for displaying widget.
* *
* This method renders some HTML code for displaying. * This method renders some HTML code for displaying.
* *
* @param object $settings * @param Widget $widget
* *
* @return mixed * @return mixed
*/ */
public static function render($settings) public static function render(Widget $widget)
{ {
$settings = $widget->settings;
$links = (array)$settings->links; $links = (array)$settings->links;
$pageIds = array_keys($links); $pageIds = array_keys($links);
// get pages // get pages
$pages = self::selectContentPages($pageIds); $pages = WidgetMenu::selectContentPages($pageIds);
// sort links using "order" // sort links using "order"
uasort($links, function ($a, $b) { uasort($links, function ($a, $b) {
@@ -53,24 +41,28 @@ class Menu implements WidgetRenderer
$parameters = Route::current() $parameters = Route::current()
->parameters(); ->parameters();
$code = '<ul class="menu ' . $settings->containerClass . '">'; $code = '<ul class="menu ' . $widget->container_class . '"';
if ($widget->container_id) {
$code .= ' id="' . e($widget->container_id) . '"';
}
$code .= '>';
foreach ($links as $pageId => $link) { foreach ($links as $pageId => $link) {
if (!isset($pages[$pageId])) { if (!isset($pages[$pageId])) {
continue; continue;
} }
$page = $pages[$pageId];
$active = false; $page = $pages[$pageId];
if (($page->is_homepage && $route == 'page.index') || ($route == 'page.content' && isset($parameters['url']) && $parameters['url'] == $page->url)) { $active = ($page->isHomepage && $route == 'page.index') || ($route == 'page.content' && isset($parameters['url']) && $parameters['url'] == $page->url);
$active = true; $route = $page->isHomepage ? route('page.index') : route('page.content', ['url' => $page->url]);
}
$code .= '<li'; $code .= '<li';
if ($active) { if ($active) {
$code .= ' class="is-active active"'; $code .= ' class="is-active active"';
} }
$code .= '><a href="' . route('page.content', ['url' => $page->url]) . '">' . e($page->title) . '</a></li>'; $code .= '><a href="' . $route . '">' . e($page->title) . '</a></li>';
} }
$code .= "</ul>"; $code .= "</ul>";
@@ -87,24 +79,19 @@ class Menu implements WidgetRenderer
*/ */
public static function renderAddForm(WidgetType $type) public static function renderAddForm(WidgetType $type)
{ {
$extensions = App::make('registerMenuExtension')
->get();
$widget = new Widget(); $widget = new Widget();
$content = new Content(); $content = app(ContentContract::class);
// get list of all pages // get list of all pages
$pages = $content->getOverview() $pages = $content->getOverview();
->get(); $widget->settings = new \stdClass();
$widget->settings->links = [];
$widget->settingsObj = new \stdClass();
$widget->settingsObj->links = new \stdClass();
return view('admin.widgets.menu.new', [ return view('admin.widgets.menu.new', [
'type' => $type, 'type' => $type,
'widget' => $widget, 'widget' => $widget,
'pages' => $pages, 'pages' => $pages,
'extensions' => $extensions
]); ]);
} }
@@ -117,20 +104,19 @@ class Menu implements WidgetRenderer
*/ */
public static function renderEditForm(Widget $widget) public static function renderEditForm(Widget $widget)
{ {
// $extensions = App::make('registerMenuExtension') /**
// ->get(); * @var $content ContentContract
$content = new Content(); */
$content = App::make(ContentContract::class);
// get list of all pages // get list of all pages
$pages = $content->getOverview() $pages = $content->getOverview();
->get();
// make object from JSON // make object from JSON
$widget->settingsObj = json_decode($widget->settings); if (!isset($widget->settings->links)) {
if (!isset($widget->settingsObj->links)) {
$links = new \stdClass(); $links = new \stdClass();
} else { } else {
$links = (array)$widget->settingsObj->links; $links = (array)$widget->settings->links;
} }
// sort links using "order" // sort links using "order"
@@ -138,7 +124,7 @@ class Menu implements WidgetRenderer
return $a->order - $b->order; return $a->order - $b->order;
}); });
$widget->settingsObj->links = (object)$links; $widget->settings->links = (object)$links;
return view('admin.widgets.menu.edit', ['widget' => $widget, 'pages' => $pages]); return view('admin.widgets.menu.edit', ['widget' => $widget, 'pages' => $pages]);
} }

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