1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-24 07:13:08 +02:00

1325 Commits

Author SHA1 Message Date
Ryan Cramer
51629cdd5f Bump version to 3.0.148 2020-01-02 12:06:16 -05:00
Ryan Cramer
0f0d20c76b README file updates for new master version 2020-01-02 11:50:54 -05:00
Ryan Cramer
49df4445e2 Suppress missing root .htaccess file error in SystemUpdateChecks since the checks wouldn't even be able to run if an .htaccess or suitable replacement file wasn't already present. Fixes processwire/processwire-issues#1059 2020-01-02 06:02:49 -05:00
Ryan Cramer
6b177d0d0d Add InputfieldImage CSS fixes per processwire/processwire-issues#1048 2019-12-31 14:31:02 -05:00
Ryan Cramer
22808c316f Add a few to-do code snippets, comments and notes for 3.0.150+ (next dev branch after master merge) 2019-12-31 14:22:33 -05:00
Ryan Cramer
a0ddedc005 Update SessionHandlerDB to support utf8mb4 charset for its table when $config->dbCharset specifies it. 2019-12-31 14:05:15 -05:00
Ryan Cramer
e7c9a1b79f Fix issue where wrong directory separator slash could end up in language translation files 2019-12-27 15:57:12 -05:00
Ryan Cramer
7d4ef4b786 Add WireLog::disable() and WireLog::enable() functions to temporary disable (and later enable) a log during runtime 2019-12-27 15:55:08 -05:00
Ryan Cramer
deb2065465 Add support for a 'type' option in wireBytesStr() function, enabling you to force return value in bytes, mb, kb, gb rather than auto-detected. 2019-12-27 15:52:06 -05:00
Ryan Cramer
12dbe3f250 Fix issue processwire/processwire-issues#1056 2019-12-27 13:39:31 -05:00
Ryan Cramer
4bcb824e9b Fix issue processwire/processwire-issues#1057 2019-12-27 13:35:00 -05:00
Ryan Cramer
9a9bdb464a Minor tweaks in WireHooks, ProcessForgotPassword and SessionLoginThrottle 2019-12-20 15:06:57 -05:00
Ryan Cramer
a11403b913 Fix issue processwire/processwire-issues#1047 2019-12-20 12:31:06 -05:00
Ryan Cramer
6bae4780f0 Add suggestion from processwire/processwire-issues#1050 2019-12-20 11:59:59 -05:00
Ryan Cramer
8762b7a77a Fix issue processwire/processwire-issues#1051 2019-12-20 11:56:07 -05:00
Ryan Cramer
3bf29d050b Add support for configuring the useAutocomplete threshold in InputfieldSelector via a maxSelectOptions setting 2019-12-08 07:17:19 -05:00
Ryan Cramer
9b624b6602 Bump version to 3.0.147 2019-12-06 14:14:45 -05:00
Ryan Cramer
103adbc0e1 Fix issue processwire/processwire-issues#1046 2019-12-06 14:05:45 -05:00
Ryan Cramer
f95fa3e3a3 Fix issue processwire/processwire-issues#1045 2019-12-06 14:02:04 -05:00
Ryan Cramer
8e91a05fd1 Fix issue processwire/processwire-issues#1043 2019-12-06 13:51:49 -05:00
Ryan Cramer
987d23154d Fix issue processwire/processwire-issues#774 2019-12-03 11:55:49 -05:00
Ryan Cramer
a6e3626992 phpdoc typo fixes per processwire/processwire-issues#797 2019-12-03 11:11:19 -05:00
Ryan Cramer
b5aca0e964 Fix issue processwire/processwire-issues#1042 2019-12-03 10:51:42 -05:00
Ryan Cramer
3717224a5c Fix issue processwire/processwire-issues#1041 2019-12-03 10:44:23 -05:00
Ryan Cramer
71e9c6114e Update HTMLPurifier version to 4.12.0 2019-12-03 10:42:17 -05:00
Ryan Cramer
c81566db7e Fix issue processwire/processwire-issues#1040 2019-12-03 09:57:36 -05:00
Ryan Cramer
b1b968c6b5 Fix issue processwire/processwire-issues#1038 2019-12-03 09:44:40 -05:00
Ryan Cramer
5daa38729a A couple of small improvements to ProcessPageLister, including improved editable() check of mock Page per template that also now includes parent in the check, and improved debug output of selector that now includes modifications to selector made by PageFinder 2019-12-03 08:41:37 -05:00
Ryan Cramer
8a5a4b4461 Add a getNonEmptyValue() method to LanguagesPageFieldValue class and make use of it in ProcessPageEdit to avoid situations where headline was otherwise blank 2019-12-03 08:40:51 -05:00
Ryan Cramer
346218a524 Various minor updates 2019-12-03 08:40:08 -05:00
Ryan Cramer
fd89b7cebd Bump version to 3.0.146 2019-11-22 14:12:51 -05:00
Ryan Cramer
72713948fa Some minor improvements to the Tfa class 2019-11-22 14:06:25 -05:00
Ryan Cramer
8455e497b7 Various unrelated minor updates to numerous classes/files (phpdoc, typo fixes, and related) 2019-11-21 11:25:58 -05:00
Ryan Cramer
efb8f1f2e9 Update documentation for PageRender::renderPage() method per processwire/processwire-issues#987 2019-11-21 09:34:34 -05:00
Ryan Cramer
4656672c81 Fix issue processwire/processwire-issues#1030 2019-11-21 08:59:30 -05:00
Ryan Cramer
14803e22a8 Fix issue processwire/processwire-issues#1031 2019-11-21 08:39:01 -05:00
Ryan Cramer
fd7917c7d1 Fix issue processwire/processwire-issues#1012 2019-11-20 10:54:51 -05:00
Ryan Cramer
4f054c3c19 Fix issue processwire/processwire-issues#1035 2019-11-20 10:08:28 -05:00
Ryan Cramer
719c2ef3a9 Fix issue processwire/processwire-issues#1029 2019-11-20 09:56:47 -05:00
Ryan Cramer
1aaef35474 Fix issue processwire/processwire#1016 and some related minor optimizations 2019-11-20 09:35:54 -05:00
Ryan Cramer
66258de9ee Update MarkupAdminDataTable to correct issue where some properties indicated as readable were not. Also update phpdoc to indicate they are readable but not writable. 2019-11-20 08:32:18 -05:00
Ryan Cramer
cf03946633 Fix issue processwire/processwire-issues#1033 2019-11-20 06:10:21 -05:00
Ryan Cramer
e296542640 Remove phpdoc sort property for Pagefile/Pageimage per processwire/processwire-issues#1034 2019-11-20 05:38:43 -05:00
Ryan Cramer
8c5d3aff72 Accompanies previous commit (forgot to include this file) processwire/processwire-issues#1032 2019-11-15 15:15:12 -05:00
Ryan Cramer
8d82308c4c Add support for feature request issue processwire/processwire-issues#1032 2019-11-15 15:14:15 -05:00
Ryan Cramer
7a41ffb089 Update $pages->touch() method to support specifying date type to update 'modified', 'created' or 'published'. Add $pages->editor()->addStatus(), removeStatus() and saveStatus() methods as simpler alternatives to the existing savePageStatus() 2019-11-15 14:40:57 -05:00
Ryan Cramer
00635bf01d Minor updates, phpdoc tweaks and such 2019-11-15 14:35:15 -05:00
Ryan Cramer
595429d425 Add a minimumAge config setting for PagePathHistory module per processwire/processwire-issues#1014 and add an option for throwing a 404 while still allowing PagePathHistory to perform redirects (if it has any). Also add a function: wire404(); that you can call for this, in addition to: throw new Wire404Exception(true); 2019-11-12 12:06:08 -05:00
Ryan Cramer
4cc587bd1a Update comments fields to use custom 'CommentField' class rather than 'Field' class 2019-11-12 11:15:00 -05:00
Ryan Cramer
4eeca2eeeb Add ability for PageFinder and PagesLoader to retain additional info about selectors and PageFinder instance for debugging purposes 2019-11-12 11:07:39 -05:00
Ryan Cramer
0478c65938 Improvements to $modules->findByPrefix() method 2019-11-12 11:06:46 -05:00
Ryan Cramer
bbddcf1ca0 Add support for Fieldtypes to specify that a Field should use a custom class that extends the Field class, rather than always using the "Field" class. 2019-11-12 11:06:02 -05:00
Ryan Cramer
4e4b3afdcb Bump version to 3.0.145 2019-11-08 13:57:50 -05:00
Ryan Cramer
b972aab11b Various minor pending updates and optimiations to several classes 2019-11-08 10:29:58 -05:00
Ryan Cramer
dd87518987 Minor adjustments to various classes 2019-11-06 15:18:41 -05:00
Ryan Cramer
098e5d03d7 Some code improvements to PW installer 2019-11-06 15:13:36 -05:00
Ryan Cramer
7aa83e3e5f Update $sanitizer->testAll() to make hookable and add a couple more methods to it processwire/processwire-issues#85 2019-11-06 15:09:54 -05:00
Ryan Cramer
5887997698 Fix issue processwire/processwire-issues#1025 2019-11-05 11:56:28 -05:00
Ryan Cramer
a22e6287be Fix issue processwire/processwire-issues#1023 via @matjazpotocnik suggested fix 2019-11-05 11:08:40 -05:00
Ryan Cramer
c9713f0bc4 Fix issue processwire/processwire-issues#1010 add support for dependencies to InputfieldPageListSelectMultiple and InputfieldPageAutocomplete 2019-11-05 10:46:56 -05:00
Ryan Cramer
ceca16506a Fix issue processwire/processwire-issues#1015 2019-11-05 08:55:36 -05:00
Ryan Cramer
76943ac192 Optimizations in PageLoader class plus fix issue processwire/processwire-issues#1021 2019-11-05 07:35:22 -05:00
Ryan Cramer
b3337d0110 Bump version to 3.0.144 2019-11-01 09:58:44 -04:00
Ryan Cramer
d30a937b99 Fix issue processwire/processwire-issues#980 2019-11-01 06:19:10 -04:00
Ryan Cramer
51616e7393 Set default for mergeDups option in FileLog::save() method to 0 (disabled) per processwire/processwire-issues#1019 2019-11-01 05:59:12 -04:00
Ryan Cramer
69e2c0e729 Lots of upgrades to ProcessField overrides tab 2019-10-31 15:39:18 -04:00
Ryan Cramer
da36905422 Add new new full JS API for Inputfields, represented by 'Inputfields' JS var. See top of inputfields.js file for details. 2019-10-31 15:29:32 -04:00
Ryan Cramer
7d5cef5717 Add option for InputfieldCKEditor JS to support option for configuration via data attribute, useful for some dynamic insertion cases like in repeaters 2019-10-31 15:26:32 -04:00
Ryan Cramer
6208e42cac Add option to InputfieldCheckbox to enable it to render without labels (useful for when rendered as in first/last table column or similar situations) 2019-10-31 15:25:08 -04:00
Ryan Cramer
50e916b72f Add simple string diff markup generator to WireTextTools class, via diffMarkup() method 2019-10-29 06:11:10 -04:00
Ryan Cramer
80eb2ff3f1 Fix issue with bitwise operators not properly return true on $database->isOperator() call 2019-10-27 07:24:17 -04:00
Ryan Cramer
350b61d6ee Bump version to 3.0.143 2019-10-25 13:00:25 -04:00
Ryan Cramer
6fcc0502b6 Update $database->isOperator() method to allow for isolation or exclusion of bitwise operators 2019-10-25 11:07:42 -04:00
Ryan Cramer
4b8b06af0f Add new wireEmpty() function as more helpful PW alternative to PHP's empty() function, plus improvements to wireClassName() function 2019-10-25 10:59:46 -04:00
Ryan Cramer
7544e0b56f Fix issue processwire/processwire-issues#933 2019-10-25 10:53:50 -04:00
Ryan Cramer
063203af26 Fix issue processwire/processwire-issues#1011 2019-10-25 10:20:28 -04:00
Ryan Cramer
1d0dc756b6 Add support for custom type-specific Inputfield classes and attributes to be added via markup array in InputfieldWrapper 2019-10-25 10:06:18 -04:00
Ryan Cramer
a3251d8571 Fix issue processwire/processwire-issues#1008 2019-10-25 09:33:07 -04:00
Ryan Cramer
4012949146 Fix issue processwire/processwire-issues#1009 2019-10-25 09:25:48 -04:00
Ryan Cramer
7d4ca45673 Fix issue processwire/processwire-issues#1000 2019-10-21 09:04:34 -04:00
Ryan Cramer
8d12cb2340 Fix issue processwire/processwire-issues#1005 2019-10-21 06:15:30 -04:00
Ryan Cramer
a7a91b668e Fix issue processwire/processwire-issues#1003 2019-10-18 10:59:11 -04:00
Ryan Cramer
34bc5094b0 Fix issue processwire/processwire-issues#1001 2019-10-18 10:37:59 -04:00
Ryan Cramer
0154f9defb Fix a couple of multilanguage issues with the new file/image custom fields options 2019-10-18 09:52:58 -04:00
Ryan Cramer
2f9976b6f3 Updates/improvements to phpdoc in FieldtypeText 2019-10-18 06:19:24 -04:00
Ryan Cramer
7be5fb4a68 Refactor FieldtypeDatetime module so that it no longer extends FieldtypeText 2019-10-18 06:18:45 -04:00
Ryan Cramer
22fe5bf9ef Fix issue processwire/processwire-issues#992 2019-10-16 09:02:59 -04:00
Ryan Cramer
adbc5bf95a Partial update to modal.js per processwire/processwire-issues#993 2019-10-16 08:10:48 -04:00
Ryan Cramer
c57e29e0c4 Fix issue processwire/processwire-issues#996 2019-10-16 08:01:25 -04:00
Ryan Cramer
3e4e690040 Fix issue processwire/processwire-issues#998 2019-10-16 07:54:10 -04:00
Ryan Cramer
6ffa041c3f Fix issue processwire/processwire-issues#999 2019-10-16 06:12:14 -04:00
Ryan Cramer
d7596426f8 Fix issue processwire/processwire-issues#995 2019-10-16 05:41:45 -04:00
Ryan Cramer
71a726f13d Fix issue processwire/processwire-issues#991 2019-10-15 12:23:18 -04:00
Ryan Cramer
d4dca0ed73 Attempt fix for issue processwire/processwire-issues#989 2019-10-15 11:47:51 -04:00
Ryan Cramer
3be2c31d41 Attempt to fix issue processwire/processwire-issues#973 plus add support for partial string matching operators %= and ^= in dates 2019-10-15 09:48:46 -04:00
Ryan Cramer
42e87fef74 Add some extra descriptive text in ProcessRole for page-edit and page-view permissions. 2019-10-14 10:47:08 -04:00
Ryan Cramer
59ec2ee8cc Update FileLog to support some new save options, including the ability to collapse duplicate log ones to 1. Also update ProcessLogger to improve output in some instances. 2019-10-14 10:46:11 -04:00
Ryan Cramer
3702ef8408 Bump version to 3.0.142 2019-10-11 14:29:03 -04:00
Ryan Cramer
573048abb4 Add support for custom fields in file/image fields. Details and instructions coming on Friday, but if you want to preview, create a template with name "field-images" where the "images" part is the name of your file/image field. Add the fields to it that you want to represent your custom fields, and it's ready to use. Supports most core Fieldtypes (including multi-language) with the following exceptions that are not supported as custom fields in an File/Image field: CKEditor, Repeaters, PageTable, Files (nested), Images (nested), Comments, Cache, Selector. 2019-10-09 12:01:19 -04:00
Ryan Cramer
eae15ce88a Update ProcessPageEdit so that submit dropdown actions are hookable 2019-10-09 11:54:37 -04:00
Ryan Cramer
e0e7f6eae6 Update InputfieldPageAutocomplete so that its icon is better positioned when the field initially starts out hidden 2019-10-09 11:53:36 -04:00
Ryan Cramer
6b20f429a9 Various minor updates and optimizations to several core classes 2019-10-09 11:52:36 -04:00
Ryan Cramer
0a01b472a1 Update InputfieldAsmSelect to reduce number of options needed in ProcessWire.config, isolating options common to all InputfieldAsmSelect instances from those unique to individual instances 2019-10-04 10:45:24 -04:00
Ryan Cramer
655c4cdd24 Update markup regions to support nested pw-optional regions as well as include removed pw-optional regions in debug info per processwire/processwire-issues#883 and processwire/processwire-issues#984 2019-09-30 09:51:25 -04:00
Ryan Cramer
3c11bbf1d8 Fix issue processwire/processwire-issues#982 2019-09-27 11:44:20 -04:00
Ryan Cramer
869c6f4a60 Fix issue processwire/processwire-issues#979 2019-09-27 11:41:50 -04:00
Ryan Cramer
7bbf1a53b2 Add support for more automatic file inclusions during processwire status/state changes, and make it possible to customize the filenames for them, including the existing ones like ready.php, init.php, etc. Also some other related system level improvements. 2019-09-27 11:36:23 -04:00
Ryan Cramer
27235ca6b6 Fix issue processwire/processwire-issues#978 2019-09-26 15:20:30 -04:00
Ryan Cramer
5586dd074b Fix issue processwire/processwire-issues#970 2019-09-26 14:38:34 -04:00
Ryan Cramer
648d2731d5 Update to docs in MarkupPagerNav.module per processwire/processwire-issues#969 2019-09-26 12:16:46 -04:00
Ryan Cramer
1fe7172cd8 Fix issue processwire/processwire-issues#965 2019-09-26 11:31:27 -04:00
Ryan Cramer
aa50edcbb0 Update a non-wired WireArray to automatically inherit an added item's ProcessWire instance 2019-09-26 06:49:13 -04:00
Ryan Cramer
e634d7a4a5 Update PageArray phpdoc per processwire/processwire-issues#960 2019-09-26 06:43:42 -04:00
Ryan Cramer
6d8b1ff2e4 Fix issue processwire/processwire-issues#946 2019-09-25 07:46:41 -04:00
Ryan Cramer
f495baa911 Fix issue processwire/processwire-issues#942 2019-09-25 06:48:26 -04:00
Ryan Cramer
b80df87c82 Fix issue processwire/processwire-issues#939 2019-09-24 16:37:35 -04:00
Ryan Cramer
9f4807cf56 Fix issue processwire/processwire-issues#931 2019-09-24 12:30:11 -04:00
Ryan Cramer
6758fb525e Fix issue processwire/processwire-issues#930 2019-09-24 12:04:14 -04:00
Ryan Cramer
6c755a8a9c Optimization to Page::setQuietly() method to support more direct setting of integer base properties in Page::$settings 2019-09-24 11:43:23 -04:00
Ryan Cramer
aae7302283 Update PageArray setSelectors() and getSelectors() methods to support setting/getting as string as an alternate to previously only allowing a Selectors-instance 2019-09-24 11:42:08 -04:00
Ryan Cramer
a2fb255de0 Fix issue processwire/processwire-issues#927 use current created user for cloned pages rather than original created user 2019-09-24 09:34:08 -04:00
Ryan Cramer
8fbf63ce85 Update error message in Modules class to make it less ambiguous per processwire/processwire-issues#926 2019-09-24 08:47:51 -04:00
Ryan Cramer
6f71ff3f93 Bumping version to 3.0.141, some other updates in progress but will commit those next week 2019-09-20 15:40:23 -04:00
Ryan Cramer
b868320b70 Add $config->setLocation(), $config->setPath() and $config->setUrl() methods to allow runtime modification of system paths/URLs. 2019-09-20 10:48:12 -04:00
Ryan Cramer
710c222b5a Add a new Templates::fileModified() hookable method that is called whenever a change is detected to a template file. Plus update the Template and Templates class to make it possible for runtime modification of the templates path. 2019-09-20 10:46:47 -04:00
Ryan Cramer
7a76f4b857 Update InputfieldCheckbox to support label element attributes in output ($labelAttrs array), for API usage 2019-09-18 11:30:01 -04:00
Ryan Cramer
83b8b8c50f Update DatabaseQuerySelectFulltext class to suppor OR values when used outside of PageFinder (like when used directly from FieldtypeMulti) 2019-09-18 11:28:31 -04:00
Ryan Cramer
215e2c56e1 Update $input->cookie API variable so that it can now also set cookies (in addition to just getting them). Default cookie settings are controlled from new $config->cookieOptions array. 2019-09-18 11:26:42 -04:00
Ryan Cramer
e94f8bc089 Bump version to 3.0.140 2019-09-06 15:43:03 -04:00
Ryan Cramer
b01a7d77bd Additional updates to InputfieldToggle 2019-09-06 15:42:10 -04:00
Ryan Cramer
d37d38d8a2 Add a new FieldtypeToggle module to accompany the InputfieldToggle module so that you can create ProcessWire fields that use this type as an alternative to checkboxes (you can also convert existing checkbox fields to use this type) 2019-09-03 11:51:26 -04:00
Ryan Cramer
56acc68b83 Numerous updates/improvements to the new InputfieldToggle module 2019-09-03 11:51:05 -04:00
Ryan Cramer
86695f8499 Update FieldtypeMulti::savePageField to use InnoDB transactions when possible, plus add a new shortcut method: WireDatabasePDO::allowTransaction() 2019-09-03 11:49:58 -04:00
Ryan Cramer
38d66af6f1 Various minor updates 2019-09-03 11:48:48 -04:00
Ryan Cramer
51f554176d Add support for an Inputfield 'detail' property, similar to existing 'notes' property, but appears below it and in muted text. 2019-09-03 11:44:26 -04:00
Ryan Cramer
e2529912da Bump version to 3.0.139 2019-08-30 14:26:56 -04:00
Ryan Cramer
98b42a9958 Add support for inline column width adjustment when editing a template’s fields in ProcessTemplate 2019-08-30 14:25:56 -04:00
Ryan Cramer
68940a6a9c Minor adjustment to previous commit (removing development output) 2019-08-30 11:36:25 -04:00
Ryan Cramer
e94e10c631 Add new "Toggle" Inputfield module providing an often more useful alternative to the InputfieldCheckbox module. There is also an accompanying FieldtypeToggle, but it is still in development so probably won't be till next week's commits. 2019-08-30 11:34:07 -04:00
Ryan Cramer
f5d955ef5f Bump version to 3.0.138 2019-08-16 14:11:13 -04:00
Ryan Cramer
742ed9c479 Improvements to Comment and CommentList class to support hookable modification of individual comments during rendering 2019-08-16 14:10:25 -04:00
Ryan Cramer
0392e9babf Update htmlpurifier version to 4.11.0 2019-08-15 06:20:38 -04:00
Ryan Cramer
d5795c4bc3 Add methods/properties to Page class for querying file paths without referencing filesManager: hasFilesPath(), hasFiles(), filesPath(), filesUrl() 2019-08-15 05:44:24 -04:00
Ryan Cramer
8a148c80f4 Minor update to CommentList class when using {url} or {page.url} placeholders to make it remove a useless href attribute on any links when the page is unpublished or no longer exists. 2019-08-15 05:40:08 -04:00
Ryan Cramer
429b98023b Upgrade ImageSizer to provide a new getEngineInfo() method that enables you to get verbose information about all installed engines (such as supported source/target formats, and more) without having to supply a file to inspect. 2019-08-15 05:37:38 -04:00
Ryan Cramer
4a9a7fadb8 Fix issue where InputfieldSelector didn't properly recognize the 'pgesFindSelect' option from FieldtypePage/InputfieldPage (custom find) option 2019-08-10 10:25:54 -04:00
Ryan Cramer
8982c976c5 Various minor unrelated updates 2019-08-08 14:14:36 -04:00
Ryan Cramer
f22302aaaf Fix issue processwire/processwire-issues#952 2019-08-07 06:11:31 -04:00
Ryan Cramer
81c745c5ad Fix issue processwire/processwire-issues#953 2019-08-07 06:00:11 -04:00
Ryan Cramer
481eb7c545 Remove field name indicator from MarkupFieldtype.php output that wasn't intended to stay 2019-08-05 07:53:07 -04:00
Ryan Cramer
c2ee22b334 Fix issue processwire/processwire-issues#951 2019-08-05 05:48:59 -04:00
Ryan Cramer
e160920984 Fix php notice in new PageimageVariations.php class 2019-08-04 11:18:06 -04:00
Ryan Cramer
918b894d82 Bump version to 3.0.137 2019-08-02 14:25:28 -04:00
Ryan Cramer
d6c7273c63 Update the PagefileExtra class to support some additional properties related to file size 2019-08-02 10:32:42 -04:00
Ryan Cramer
0e9cc868b7 Minor refactoring in ProcessPageEditImageSelect, plus update the executeVariations() method to also show webp extras when applicable. 2019-08-02 10:30:57 -04:00
Ryan Cramer
2f6497d1e5 Add support for hooking multiple methods to the same event handler via addHook*() calls by separating the methods to hook with commas, or by providing an array rather than a string. Also updated the corresponding removeHook() method to support removing multiple in the same call. 2019-08-01 11:44:33 -04:00
Ryan Cramer
b179aa9afc Various improvements to asmSelect jQuery plugin, including better parent/child option support with technical improvements (now works with Safari too), and some code and documentation improvements as well. 2019-07-31 09:36:19 -04:00
Ryan Cramer
6479e78288 Minor refactor of Page::getFieldSubfieldValue() method 2019-07-31 09:34:11 -04:00
Ryan Cramer
3cb1f33a97 Minor improvements in core MarkupFieldtype class 2019-07-31 09:33:43 -04:00
Ryan Cramer
83369f1173 Some updates and refactoring in ProcessPageLister module 2019-07-31 09:32:51 -04:00
Ryan Cramer
d4318fbd2c Bump version to 3.0.136 2019-07-26 11:55:37 -04:00
Ryan Cramer
ddb4aebf60 Add new static Debug::backtrace() method to the Debug class. This returns a backtrace array that is simpler and more PW-specific than PHP's version. By default it excludes likely irrelevant (for most) hook-related method calls that usually fill up the backtrace. 2019-07-26 11:41:05 -04:00
Ryan Cramer
072536dc72 Update CKEditor version to 4.12.1 2019-07-26 05:59:01 -04:00
Ryan Cramer
ddc72b3aff Fix ProcessPageLister issue where it was resetting filters sometimes when it shouldn't 2019-07-21 07:11:42 -04:00
Ryan Cramer
960998cb46 Work-in-progress minor update to pwlink cke plugin 2019-07-06 18:02:33 -04:00
Ryan Cramer
851803396d Bump version to 3.0.135 2019-07-05 14:45:36 -04:00
Ryan Cramer
7b4fe18f85 Minor adjustment to SystemUpdaterChecks class 2019-07-05 11:54:23 -04:00
Ryan Cramer
5f705459a6 Improvements to Session and Notices classes 2019-07-05 11:53:49 -04:00
Ryan Cramer
161d6fb737 Upgrade ProcessWire installer (and related site profile files) to support specification of debug mode as one of the interactive installation options. Also updated some wording in various parts of the installer. 2019-07-05 11:35:25 -04:00
Ryan Cramer
562565ff42 Add system update #17 which adds a secondary layer of file protection with dedicated .htaccess files in various site directories that take over if the root .htaccess file ever goes missing 2019-07-04 10:52:37 -04:00
Ryan Cramer
b5b1a796ec Small improvements to ProcessWire installer 2019-07-04 10:47:58 -04:00
Ryan Cramer
5663803e05 Move post-login superuser system checks from ProcessLogin into their own class SystemUpdaterChecks as part of the SystemUpdater module 2019-07-04 10:43:24 -04:00
Ryan Cramer
05bbfe0ba1 Some additional minor .htaccess updates 2019-07-03 13:18:15 -04:00
Ryan Cramer
a336acfaa4 Improvements to SystemUpdater module so that it can support manual API calls 2019-07-03 12:20:27 -04:00
Ryan Cramer
0e00f24004 htaccess version update, including suggestions from Teppo, Netcarver, and several other updates while I was there. See the htaccess.txt file for upgrade instructions. 2019-07-02 14:31:08 -04:00
Ryan Cramer
c02420796d Improvements to LanguageSupportPageNames::languageAdded hook to to avoid potential of unnecessary error message appearing when adding language 2019-07-01 06:42:02 -04:00
Ryan Cramer
aaab673117 Minor optimization to static storage of Page runtime helper classes 2019-07-01 06:40:54 -04:00
Ryan Cramer
2b0d8f333a Some updates preparing for additonal webp options (new options not ready to use quite yet). 2019-06-30 11:01:23 -04:00
Ryan Cramer
f1913df4d4 Bump version to 3.0.134 2019-06-28 12:09:37 -04:00
Ryan Cramer
d1bfd2f0e8 Minor adjustments in ProcessPageLister.module 2019-06-28 12:08:58 -04:00
Ryan Cramer
04cfe51306 Fix issue processwire/processwire-issues#875 2019-06-28 11:05:35 -04:00
Ryan Cramer
277f16dba7 Fix issue processwire/processwire-issues#866 2019-06-27 13:53:01 -04:00
Ryan Cramer
46f62fa34f Fix issue processwire/processwire-issues#867 2019-06-27 12:31:23 -04:00
Ryan Cramer
52405a6a77 Fix issue processwire/processwire-issues#870 2019-06-27 12:26:59 -04:00
Ryan Cramer
e1a664826f Fix issue processwire/processwire-issues#871 2019-06-27 10:27:42 -04:00
Ryan Cramer
4d2e8faece Add feature request for InputfieldSubmit to support independent button text from button value per processwire/processwire-issues#877 2019-06-27 10:04:57 -04:00
Ryan Cramer
2b29ffaf75 Fix issue processwire/processwire-issues#878 2019-06-27 08:27:43 -04:00
Ryan Cramer
08551319c9 ProcessTemplate field wording update per processwire/processwire-issues#876 2019-06-26 12:11:40 -04:00
Ryan Cramer
7e0f2080e6 Fix issue processwire/processwire-issues#880 2019-06-26 11:30:32 -04:00
Ryan Cramer
fdd2e7216d Fix issue processwire/processwire-issues#884 2019-06-26 10:51:30 -04:00
Ryan Cramer
f6702407ff Fix issue processwire/processwire-issues#897 2019-06-26 10:43:03 -04:00
Ryan Cramer
ab94e7b682 Attempt fix for processwire/processwire-issues#907 2019-06-26 10:15:54 -04:00
Ryan Cramer
3024314680 Fix issue processwire/processwire-issues#905 2019-06-25 11:16:43 -04:00
Ryan Cramer
d193bb4fb1 Fix issue processwire/processwire-issues#910 2019-06-25 10:59:14 -04:00
Ryan Cramer
57e2137fc4 Fix issue processwire/processwire-issues#913 2019-06-25 07:54:27 -04:00
Ryan Cramer
63ed8bff65 Test commit 2019-06-23 07:16:16 -04:00
Ryan Cramer
35e3e4266a Fix issue processwire/processwire-issues#904 2019-06-21 11:10:59 -04:00
Ryan Cramer
49d0348b4a Fix issue processwire/processwire-issues#902 2019-06-21 11:09:53 -04:00
Ryan Cramer
903e6b3527 Improvements to InputfieldSelector so that it is no longer necessary to have separate "Field" and "Field..." options, as now there is just "Field..." with default behavior being the same as the previous "Field" but with the ability to select subfields. This makes selection quite a bit simpler and less verbose and and saves a step. 2019-06-20 10:47:01 -04:00
Ryan Cramer
734c56dbbe Improve operator extraction in Selectors class so that it doesn't potentially match non-existent operators and throw an unnecessary "unknown operator - selector value property escaped?" error message 2019-06-20 10:41:21 -04:00
Ryan Cramer
fdc1c61833 Improvements in PageFinder to handle some additional types of native field queries like partial match of parent.path, matching parent.status, and some others. 2019-06-20 10:27:52 -04:00
Ryan Cramer
3577b81591 Add PageFinder support for partial match operator on parent.path selectors like: parent.path%=something 2019-06-19 10:34:26 -04:00
Ryan Cramer
62b81164a0 Add some new option manipulation methods to InputfieldSelect.module, which are also inherited by all of PW's single/multi-selection Inputfields 2019-06-19 09:32:10 -04:00
Ryan Cramer
5f3e9af378 A few minor adjustments 2019-06-19 09:30:53 -04:00
Ryan Cramer
bccdc2ace4 Bump version to 3.0.133 2019-06-14 13:07:49 -04:00
Ryan Cramer
108f8c5511 Minor adjustments to ProcessPageLister and InputfieldSelector 2019-06-14 11:53:58 -04:00
Ryan Cramer
51c6becf9a Update asmSelect to support 1-level of parent/child relationships for selectable options 2019-06-14 11:51:20 -04:00
Ryan Cramer
eb299ee598 Add support for Lister user-specific bookmarks. This enables users to create their own private or shared bookmarks in Lister. 2019-06-12 15:37:26 -04:00
Ryan Cramer
d977cabb82 Add optional support for an executeUnknown() method to Process modules. This method is called when an unknown method is requested. It is not enabled by default, unless you add an executeUnknown() method to the Process module. 2019-06-12 15:34:18 -04:00
Ryan Cramer
2ba96d3f2b A couple of adjustments to ProcessPageLister module to correct issue of neverending spinner when ajax result was missing an expected row, plus improve the ajax spinner output, especially for AdminThemUikit which previously was not showing a spinner. 2019-06-10 09:03:18 -04:00
Ryan Cramer
f95b026a95 Improvements to boolean html attribute handling for Inputfield class setAttribute() method 2019-06-07 12:20:11 -04:00
Ryan Cramer
204335e2d3 Add support for $page->meta() method for maintaining persistent meta data for pages independent of fields and the normal load/save process. 2019-06-07 12:17:27 -04:00
Ryan Cramer
50a6f3585e Add InputfieldCheckbox checked() method as an optional shortcut for getting/setting checkbox state 2019-06-07 12:13:02 -04:00
Ryan Cramer
08bea26a17 Add WireDatabasePDO tableExists() method 2019-06-07 12:12:24 -04:00
Ryan Cramer
5235ab2a89 Add $session->getVal() methods which work the same as get() but let you speicfy the fallback value 2019-06-07 12:11:52 -04:00
Ryan Cramer
26435563d3 Add new $sanitizer->attrName() method and some other optimizations to Sanitizer class 2019-06-07 12:11:03 -04:00
Ryan Cramer
f6c210f686 Various minor updates 2019-06-07 12:08:55 -04:00
Ryan Cramer
a88c6a49c7 Fix issue where using crop/resize tools on an image within a NESTED repeater item would not save the changes after clicking the Save button in the page editor. 2019-06-04 09:30:35 -04:00
Ryan Cramer
ad2f60b544 Settings label typo fix in InputfieldRepeater.module 2019-05-26 08:39:37 -04:00
Ryan Cramer
4b733af0bf Fix issue with repeater item settings toggle and visibility (as used by RM) not working when ajax repeater item starts collapsed 2019-05-26 08:34:35 -04:00
Ryan Cramer
680d885abb Bump version to 3.0.132 2019-05-24 16:29:44 -04:00
Ryan Cramer
d8bbde74d7 A couple other ImageSizerEngine adjustments for webp 2019-05-24 14:22:07 -04:00
Ryan Cramer
50f8024ba3 Additional updates to PR #141 expanding WEBP support 2019-05-24 12:20:13 -04:00
Ryan Cramer
bbd3aba191 Merge branch 'horst-n-webP-support' into dev 2019-05-23 15:50:21 -04:00
Ryan Cramer
9964473776 Updates to @horst-n PR #141 for webp image support 2019-05-23 15:49:06 -04:00
Ryan Cramer
7f218c6a6b Merge branch 'webP-support' of https://github.com/horst-n/processwire into horst-n-webP-support 2019-05-22 14:49:45 -04:00
Ryan Cramer
0fd6a2aaed Update RepeaterPage class with methods to identify root forField and forPage in nested repeaters 2019-05-22 14:03:34 -04:00
Ryan Cramer
73f0c915de Various minor primarily phpdoc updates 2019-05-22 14:03:03 -04:00
Ryan Cramer
2b7f80d575 Several major improvements to WireTextTools::markupToText() method, plus related updates in Sanitizer and WireMail classes 2019-05-22 13:50:12 -04:00
Ryan Cramer
f1d5f12835 A couple of minor adjustments 2019-05-17 13:26:38 -04:00
Ryan Cramer
bac9736e49 Update WireHttp::sendFile() to support a 'data' option that lets you provide a string of content to send as a download, rather than pulling from existing file. 2019-05-17 13:21:12 -04:00
horst-n
6e580f71e1 changed the return of webpUrl()
to return the calculated URL, even if there is no webp copy available!
2019-05-04 18:42:11 +02:00
horst-n
88d4b11db4 added object as output format to the getDebugInfo
I figured out that this is useful to have for (automated) testing and debugging too. It is much easier to get a value for conditional coding via an object:
```
$dbgInfo = $image->getDebugInfo($options, 'object');
if($dbgInfo->engines->selectedEngine == ....
```
2019-05-04 11:21:58 +02:00
Ryan Cramer
f83b4ae259 Bump version to 3.0.131 2019-05-03 15:11:41 -04:00
Ryan Cramer
2880ff6d57 Various minor updates, mostly phpdoc related 2019-05-03 11:59:18 -04:00
Ryan Cramer
3a094f6447 Adjustment to WireTextTools::markupToText() method to prevent it from adding redundant newlines 2019-05-03 11:54:54 -04:00
Ryan Cramer
12f6253af6 Update WireHttp::sendFile() method to support partial downloads or ranges via HTTP_RANGE headers. Browsers use this particularly with media files (audio/video), especially Safari and iOS devices. But this also enables partial and pause/resume of any kind of downloads. 2019-05-03 11:40:29 -04:00
horst-n
d361c6c11e added a webp support check for engines
see: https://processwire.com/talk/topic/14236-webp-support/page/3/?tab=comments#comment-185020
2019-05-03 13:54:04 +02:00
horst-n
fa917b30e0 fixed a bug with internal checking
if a webp copy exists or not. Using $this->hasWebp() within the resize method doesn't work. Changed to test for file_exists of the temporary webp copy.
2019-05-02 10:37:08 +02:00
horst-n
b46bdfea94 updated font-family with more monospace fonts 2019-04-29 19:29:53 +02:00
horst-n
e8e2594d3e force new creation for condition
where the regular variation exists and should not be recreated, but where a webp copy, that do not exist, is requested too.
2019-04-29 17:30:06 +02:00
horst-n
540ae4f2d9 rearanged and grouped the output of getDebugInfo 2019-04-29 16:51:57 +02:00
Ryan Cramer
f2dbdb118e Fix issue processwire/processwire-issues#865 and processwire/processwire-issues#864 2019-04-29 10:07:49 -04:00
horst-n
53c414aa60 reworked the getDebugInfo method,
trying to make the code more readable.
But the main goal for me is, that the output is best readable! :)
2019-04-27 23:15:44 +02:00
horst-n
d062bc4345 added to phpdocs 2019-04-27 23:13:26 +02:00
horst-n
e6f4fbcb40 minor changes and cleaning phpdoc 2019-04-27 23:12:37 +02:00
horst-n
ba6803f425 refactored getDebugInfo() method 2019-04-27 13:21:07 +02:00
horst-n
32afdbdf52 cleaning up a bit 2019-04-27 11:55:50 +02:00
horst-n
1b96368aa2 cleaning and beautyfying 2019-04-27 10:46:39 +02:00
horst-n
a4431829d9 added method and property webpFilename 2019-04-27 10:37:10 +02:00
horst-n
ec2c2cf4e0 cleaning minor things and whitespaces
and added "Options Hierarchy" to the getDebugInfo method
2019-04-27 10:27:07 +02:00
horst-n
8da54040d4 added a verbose debug method
that can be called manually on every imagevariation and with individual options array.
2019-04-27 00:36:42 +02:00
horst-n
ebe7a5d80f added a verbose debug info method 2019-04-27 00:30:58 +02:00
horst-n
a6aa39d75d added webp params to imagesizerOptions 2019-04-26 19:01:05 +02:00
horst-n
9f01172a4b added 2 aliases
beginning with webp, as we also use two new options beginning with webp. Maybe this is better to avoid confusion.
webpAdd, webpQuality, webpUrl, webpSrc
2019-04-26 18:32:23 +02:00
horst-n
04135462c8 refactored webp creation and compression 2019-04-26 18:11:04 +02:00
horst-n
023a672e26 Update Pageimage.php 2019-04-25 11:01:57 +02:00
horst-n
232f66dedf minor changes 2019-04-25 11:01:31 +02:00
horst-n
35b9a403e1 rearranged code for webP saving 2019-04-25 11:01:13 +02:00
horst-n
4e534e0315 add property and method for webpAdd 2019-04-25 08:59:58 +02:00
horst-n
8a63886da1 remove debug code 2019-04-25 00:41:47 +02:00
horst-n
b531275a6e make more robust filename creation
and remove debug code
2019-04-25 00:41:14 +02:00
horst-n
f3f4e427b0 changed to more robust filename creation 2019-04-25 00:21:09 +02:00
horst-n
a5a101a456 documentation for webp properties 2019-04-24 23:40:16 +02:00
horst-n
7f132638d0 added properties for webp support
with the property $image->hasWebp we can detect, for example in a template file, if an image variation has a dependant webp file, so we can render conditional markup with srcset or picture elements.
And urlWebp and srcWebp for returning the URL.
2019-04-24 23:23:05 +02:00
horst-n
63018ab1cd add support for webp
added option "webpAdd" and "webpQuality" that allows to create a webp file as sidecar file when creating a JPEG, GIF or PNG file.
2019-04-24 21:32:05 +02:00
horst-n
bbe13583c5 add webp support to GD 2019-04-24 21:29:09 +02:00
horst-n
c1814c8e6b add option webpQuality 2019-04-24 21:28:33 +02:00
Ryan Cramer
6d38df65b4 Fix issue processwire/processwire-issues#857 2019-04-23 06:29:02 -04:00
Ryan Cramer
889f1e753d Fix issue processwire/processwire-issues#850 2019-04-23 06:07:37 -04:00
Ryan Cramer
6ed9385052 Add support for Comment::statusFeatured to InputfieldCommentsAdmin 2019-04-19 09:36:30 -04:00
Ryan Cramer
5cbe7d562d Upgrade ProcessCommentsManager to take better advantage of Uikit admin output and add support for featured comment status 2019-04-19 09:26:26 -04:00
Ryan Cramer
ebed9f6279 Add FieldtypeComments::count() method which does the same thing as the find() but instead returns a count. Plus add support for new Comment::statusFeatured status for support of featured comments 2019-04-19 09:23:40 -04:00
Ryan Cramer
f41c61e490 Upgrades and improvements to CommentFilterAkismet class 2019-04-19 09:22:46 -04:00
Ryan Cramer
2a63a44485 Minor adjustments 2019-04-19 09:19:03 -04:00
Ryan Cramer
761c7640c7 Add new $config->sessionForceIP property that lets you override the return value of $session->getIP() 2019-04-19 09:16:58 -04:00
Ryan Cramer
0b98667bc9 Fix issue processwire/processwire-issues#770 2019-04-17 11:11:02 -04:00
Ryan Cramer
f30e084e3d Fix issue processwire/processwire-issues#772 2019-04-17 05:59:58 -04:00
Ryan Cramer
e5582f5555 Fix issue processwire/processwire-issues#851 2019-04-16 10:17:05 -04:00
Ryan Cramer
72324fdbb1 Fix issue processwire/processwire-issues#854 2019-04-16 10:03:42 -04:00
Ryan Cramer
21164eaef5 Fix issue processwire/processwire-issues#852 2019-04-16 06:17:23 -04:00
Ryan Cramer
f0c79f38cf Additional update for processwire/processwire-issues#849 2019-04-16 06:08:52 -04:00
Ryan Cramer
3b8d92a975 Fix issue processwire/processwire-issues#849 2019-04-16 05:59:55 -04:00
Ryan Cramer
144ce409b1 Bump version to 3.0.130 2019-04-12 21:44:44 -04:00
Ryan Cramer
932ad94dc4 Add WireDatabaseBackup::dropAllTables() method for feature request processwire/processwire-requests#296 2019-04-12 10:46:51 -04:00
Ryan Cramer
099acacc61 Improvements to the __debugInfo() return values for some classes as well as those requested in processwire/processwire-issues#575 2019-04-12 10:28:23 -04:00
Ryan Cramer
ca94bd3958 Additional fix for processwire/processwire-issues#587 2019-04-12 06:19:54 -04:00
Ryan Cramer
da309f335b Fix issue processwire/processwire-issues#744 2019-04-11 12:23:01 -04:00
Ryan Cramer
12aede03fe Add new $cache->renderFile() method that works like $files->render() but caches the output according to WireCache rules. Also updated $files->render() to support a 'cache' option, which provides the same result. 2019-04-11 11:48:34 -04:00
Ryan Cramer
b0f11c15c5 Fix issue processwire/processwire-issues#847 2019-04-10 15:06:17 -04:00
Ryan Cramer
dcf226995c Improvements and minor refactoring in WireCache.php ($cache API var) 2019-04-10 11:55:17 -04:00
Ryan Cramer
d03ed24c75 Various phpdoc improvements 2019-04-10 05:58:23 -04:00
Ryan Cramer
612d886dd8 Typo fixes per processwire/processwire-issues#797 2019-04-09 06:16:13 -04:00
Ryan Cramer
3bed484c80 Fix issue processwire/processwire-issues#845 2019-04-09 06:08:10 -04:00
Ryan Cramer
a6cb458c06 Fix issue processwire/processwire-issues#826 2019-04-08 13:51:38 -04:00
Ryan Cramer
5af01e44d0 Fix issue processwire/processwire-issues#844 2019-04-08 06:22:13 -04:00
Ryan Cramer
da73398da1 Fix issue processwire/processwire-issues#843 2019-04-08 06:12:24 -04:00
Ryan Cramer
357ce2d642 Bump version to 3.0.129 2019-04-05 15:08:36 -04:00
Ryan Cramer
e2176d9446 Add $datetime->elapsedTimeStr() method to accompany existing relativeTimeStr() method. This one enables you to show difference in two dates/times rather than just relative to current date/time. 2019-04-05 15:05:05 -04:00
Ryan Cramer
d4ca0d6e45 Update SessionCSRF to use more purpose-specific WireRandom class rather than Password class for random string generation 2019-04-05 15:01:49 -04:00
Ryan Cramer
c58d00863a Add $sanitizer->httpUrl() method to accompany existing url() method 2019-04-05 15:01:14 -04:00
Ryan Cramer
b122456eb2 Fix issue processwire/processwire-issues#441 2019-04-05 13:12:18 -04:00
Ryan Cramer
e0b1c5c1bd Add update to accommodate different "not" logic in a new notAll() method for WireArray/PageArray per processwire/processwire-issues#383 2019-04-05 12:06:08 -04:00
Ryan Cramer
711b6b8f3d Add additional check to make sure one isn't deleting the page being viewed per processwire/processwire-issues#356 2019-04-05 11:03:31 -04:00
Ryan Cramer
6d7198f2a6 Add $config->installedAfter($date) and $config->installedBefore($date) methods 2019-04-05 11:02:28 -04:00
Ryan Cramer
45d6158be1 Fix issue processwire/processwire-issues#332 FieldsetTab was sometimes not auto-loading AJAX-loaded tab after form submit 2019-04-05 08:48:10 -04:00
Ryan Cramer
40c8eea7c6 update Fieldtypes::sort() method for new argument per processwire/processwire-issues#841 2019-04-02 14:04:42 -04:00
Ryan Cramer
8cdb9886e7 Fix phpdoc typos per processwire/processwire-issues#797 2019-04-02 11:29:18 -04:00
Ryan Cramer
dd183c0571 Fix issue processwire/processwire-issues#840 2019-04-02 11:24:29 -04:00
Ryan Cramer
e9620a3a47 Fix issue processwire/processwire-issues#726 2019-04-02 10:44:06 -04:00
Ryan Cramer
7793b79e03 Add support for PHP SORT_* constants per processwire/processwire-issues#841 2019-04-02 09:43:40 -04:00
Ryan Cramer
2e7db9c40f Fix issue processwire/processwire-issues#778 2019-04-01 11:49:10 -04:00
Ryan Cramer
7bd2e3fc7f Add versions of many PHP string functions to WireTextTools to abstract away the mb_string test and update Sanitizer to use them where appropriate 2019-04-01 11:32:04 -04:00
Ryan Cramer
dac7be6af4 Add support for email blacklists via $config->wireMail('blacklist') property 2019-04-01 11:31:07 -04:00
Ryan Cramer
b87404ade8 Bump version to 3.0.128 2019-03-29 15:34:22 -04:00
Ryan Cramer
d9ab9c56d5 Add $sanitizer->checkbox() method 2019-03-29 15:30:32 -04:00
Ryan Cramer
2a108b4cd0 Fix issue processwire/processwire-issues#239 (display Password description and notes in correct locations). Plus add support for disabling and/or relaxing some of the strict password requirements per a previous request (which I can't seem to find at the moment). 2019-03-29 11:49:01 -04:00
Ryan Cramer
478d299322 Add support for combined hooks that can be executed as either method or property per issue processwire/processwire-issues#232 2019-03-29 11:07:40 -04:00
Ryan Cramer
c4086084d8 Fix issue processwire/processwire-issues#214 and refactoring of dependent selects code in InputfieldPage.js 2019-03-29 10:11:35 -04:00
Ryan Cramer
bc917c6553 Enhancement/fix for processwire/processwire-issues#838 2019-03-28 10:31:32 -04:00
Ryan Cramer
ce8c369510 Fix issue processwire/processwire-issues#834 2019-03-28 05:52:44 -04:00
Ryan Cramer
f7b99d7676 Fix issue processwire/processwire-issues#837 2019-03-26 12:21:18 -04:00
Ryan Cramer
e0a96d40a2 Attempt fix for issue processwire/processwire-issues#835 2019-03-26 11:09:18 -04:00
Ryan Cramer
cf2be06dfc Add AdminThemeDefault/AdminThemeReno column widths fix as suggested by @Toutouwai in processwire/processwire-issues#306 plus add typo fix per processwire/processwire-issues#797 2019-03-26 11:00:11 -04:00
Ryan Cramer
f121425f0d Some updates to PagesType class and update all PagesType hooks from deprecated to fully functional per processwire/processwire-issues#360 2019-03-26 10:08:26 -04:00
Ryan Cramer
768b27f4c8 Fix issue in ProcessPageLister where images were not displaying if the gridMode was set to "list (verbose)", plus a couple of other minor related adjustments 2019-03-23 08:02:45 -04:00
Ryan Cramer
2cad3ed11b Various minor adjustments, but nothing interesting 2019-03-22 17:02:44 -04:00
Ryan Cramer
a2e7c35f95 Typo fix in AdminThemeUikit.module per processwire/processwire-issues#797 2019-03-21 13:05:32 -04:00
Ryan Cramer
505429e517 Fix issue processwire/processwire-issues#832 2019-03-21 11:12:01 -04:00
Ryan Cramer
40f35241e4 Fix issue processwire/processwire-issues#831 2019-03-21 10:14:03 -04:00
Ryan Cramer
1995067b96 Additional fix for issue processwire/processwire-issues#724 2019-03-21 10:06:48 -04:00
Ryan Cramer
02f05c6f67 Update to allow spaces in URL segments when allowed by config.pageNameWhitelist and config.pageNameCharset=UTF8 per processwire/processwire-issues#720 2019-03-21 09:19:01 -04:00
Ryan Cramer
7905fb5180 Add support for page.sort in PageFinder (DB selectors) per processwire/processwire-issues#496 2019-03-21 06:26:06 -04:00
Ryan Cramer
99cc9f8be7 Add support for noTrim option to InputfieldText/InputfieldTextarea per processwire/processwire-issues#459 2019-03-19 10:52:47 -04:00
Ryan Cramer
4ef5ebc8fb Fix issue processwire/processwire-issues#364 2019-03-19 10:29:36 -04:00
Ryan Cramer
cfb533869d Fix issue processwire/processwire-issues#334 2019-03-19 09:54:37 -04:00
Ryan Cramer
019057f846 Fix phpdoc typo in Module.php per processwire/processwire-issues#797 2019-03-19 09:35:45 -04:00
Ryan Cramer
2aa2342e18 Fix issue processwire/processwire-issues#768 2019-03-19 09:09:44 -04:00
Ryan Cramer
d72062a805 Fix issue processwire/processwire-issues#668 2019-03-19 08:48:05 -04:00
Ryan Cramer
40c05893ea Fix issue processwire/processwire-issues#828 2019-03-19 06:25:34 -04:00
Ryan Cramer
762439bcc3 Fix issue processwire/processwire-issues#766 2019-03-19 05:58:58 -04:00
Ryan Cramer
23208d1ade Update InputfieldSubmit to support disabling of populating selected dropdown value to submit button value 2019-03-17 07:43:20 -04:00
Ryan Cramer
6013f16c96 Small adjustment to liveSearch and missing 'n' property notice in last commit per @matjazpotocnik 2019-03-13 05:45:53 -04:00
Ryan Cramer
cbfe97de20 Upgrade ProcessPageSearchLive "view all" search results to use tabs rather than headings for improved usability 2019-03-11 12:48:42 -04:00
Ryan Cramer
68150887f6 Update JqueryWireTabs to add a render() method that provides an all-in-one tab rendering solution for the PW admin without any other setup/initialization needed. 2019-03-11 12:45:00 -04:00
Ryan Cramer
e427a03ff5 Fix issue processwire/processwire-issues#679 2019-03-08 11:54:08 -05:00
Ryan Cramer
a307d441d1 Add $sanitizer->minLength() method and another minor phpdoc typo fix per processwire/processwire-issues#797 2019-03-08 10:27:18 -05:00
Ryan Cramer
207b1ed26c Fix typo in phpdoc on WireDatabaseBackup per processwire/processwire-issues#797 2019-03-08 10:23:17 -05:00
Ryan Cramer
fc88d64a03 Additional fix for processwire/processwire-issues#817 2019-03-05 11:42:10 -05:00
Ryan Cramer
a7c3b001ce Attempt fix for issue processwire/processwire-issues#205 2019-03-05 09:15:10 -05:00
Ryan Cramer
1d9279f421 Fix issue processwire/processwire-issues#824 2019-03-04 11:44:30 -05:00
Ryan Cramer
621c34a417 Fix issue processwire/processwire-issues#124 2019-03-04 11:01:33 -05:00
Ryan Cramer
c41b670330 Fix issue processwire/processwire-issues#305 2019-03-04 10:54:07 -05:00
Ryan Cramer
b158b71c42 Update per processwire/processwire-issues#648 2019-03-04 10:10:58 -05:00
Ryan Cramer
cdb6dbaee1 Additional update for processwire/processwire-issues#183 also make repeaters editable to non-superusers with front-end editing options A and B. 2019-03-04 09:29:10 -05:00
Ryan Cramer
c38c166c67 Fix issue processwire/processwire-issues#817 2019-03-04 06:24:19 -05:00
Ryan Cramer
906640ecb7 Bump version to 3.0.127 plus some other minor updates 2019-03-01 15:38:48 -05:00
Ryan Cramer
ba8202d869 Improvements to PagesNames::pageNameFromFormat() method 2019-03-01 13:25:25 -05:00
Ryan Cramer
a43ea0ffa1 Fix issue processwire/processwire-issues#820 2019-03-01 11:58:30 -05:00
Ryan Cramer
8952afbddb Fix issue processwire/processwire-issues#183 allow for front-end editing of fields in repeater items, individually per-field by non-superusers with edit access to the page the owns the repeater field. 2019-03-01 11:36:52 -05:00
Ryan Cramer
731cc3e92e Fix issue processwire/processwire-issues#432 2019-03-01 09:39:00 -05:00
Ryan Cramer
be84331dbd Fix issue processwire/processwire-issues#818 where listable but not editable or viewable page in dropdown for Pages > Tree > [items] was navigating to some useless JSON output. Also disabled the fallback to view URL when page is viewable but not editable. 2019-03-01 09:21:18 -05:00
Ryan Cramer
b76deded68 Fix issue processwire/processwire-issues#821 front-edit modal "link" dialog in CKEditor was not respecting current language for link helper tools (autocomplete and PageList selection) 2019-03-01 06:28:10 -05:00
Ryan Cramer
7dbdcec2a0 Fix issue processwire/processwire-issues#819 2019-02-28 12:59:32 -05:00
Ryan Cramer
e73c2343d1 Add "New" dropdown menu option to ProcessModule per comment in processwire/processwire-issues#641 2019-02-28 12:26:25 -05:00
Ryan Cramer
fc2103b024 doc typo fixes per processwire/processwire-issues#797 2019-02-28 12:20:24 -05:00
Ryan Cramer
bb571b66c0 Fix issue processwire/processwire-issues#615 2019-02-28 12:10:18 -05:00
Ryan Cramer
a2ec801a1a Fix issue processwire/processwire-issues#795 2019-02-28 09:03:15 -05:00
Ryan Cramer
2f908e44e7 Add support for previously unimplemented status Page::statusUnique, which adds support for globally unique page names. This commit also changes the the previous commit Page::statusIncomplete to value 128 because it turns out 256 was used in a 3rd party module and it seemed safer to use 128, which was occuped by Page::statusVersions, which has never been used. I've also changed the name of Page::statusIncomplete to Page::statusFlagged since the status indicates an error occurred during last interactive save rather than specifically incomplete. 2019-02-27 11:55:08 -05:00
Ryan Cramer
8b82f48c4d Fix typo from earlier today for processwire/processwire-issues#252 2019-02-26 13:12:07 -05:00
Ryan Cramer
9af4df040e Update WireHooks to allow for (and ignore) an empty argument string "()" after hook definition (first argument) in addHook() methods. 2019-02-26 10:22:47 -05:00
Ryan Cramer
1b53ed15a9 Apply solution for processwire/processwire-issues#252 - publish via page tree was showing "pub" button even when page was missing requirements. Note that this solution takes effect only upon page save in the page editor, so will not affect pages in an existing installation that may not be meeting requirements. 2019-02-26 09:43:08 -05:00
Ryan Cramer
9a75980f8d Add ProcessPageListRender::getNumChildren() hook per issue/request processwire/processwire-issues#649 2019-02-26 08:39:29 -05:00
Ryan Cramer
706dd519af Fix issue in InputfieldPageAutocomplete where it was displaying the wrong icon for item-sort, and it was also wasn't positioned quite right if the autocomplete appeared in a non-default tab. 2019-02-26 08:07:45 -05:00
Ryan Cramer
c80ae0acd8 A few minor phpdoc/code comments 2019-02-22 17:45:20 -05:00
Ryan Cramer
c6410c6478 Update MarkupFieldtype to auto-link page references when page is viewable 2019-02-22 16:34:37 -05:00
Ryan Cramer
41e2bbd22b Some updates for processwire/processwire-issues#126 plus some improvements to ProcessLanguage and add a refresh link in the language translation phrase live-search 2019-02-22 07:43:47 -05:00
Ryan Cramer
f968137514 Apply additional fix for processwire/processwire-issues#539 2019-02-21 15:32:36 -05:00
Ryan Cramer
0326b1c67d Fix issue processwire/processwire-issues#773 2019-02-21 11:21:55 -05:00
Ryan Cramer
1aa2c55b9f Add support for children.custom_field=value selectors per processwire/processwire-requests#174 and processwire/processwire-issues#809 2019-02-21 11:10:57 -05:00
Ryan Cramer
5483804d49 Improve __debugInfo() in Selector and Selectors classes 2019-02-21 11:08:25 -05:00
Ryan Cramer
774c2152ee Update $sanitizer->selectorValue() method to also accept array for its value argument. When array ['a','b','c'] is present, it converts to a sanitized a|b|c string. 2019-02-21 09:58:31 -05:00
Ryan Cramer
65e283055a Update PaginatedArray::getPaginationString() to allow specifying an alternate label (zeroLabel) when there are zero items available. 2019-02-21 09:57:42 -05:00
Ryan Cramer
0c150eec42 Update Pageimage::render() method to accept manually defined alt or class attributes 2019-02-21 09:57:10 -05:00
Ryan Cramer
d49df28641 Fix issue processwire/processwire-issues#513 2019-02-21 05:31:00 -05:00
Ryan Cramer
16b69f4a10 Some improvements and fixes to the multi-language support in Lister (also affects ListerPro) 2019-02-16 10:16:33 -05:00
Ryan Cramer
2c3e949d6e Bump version to 3.0.126 plus some other minor adjustments 2019-02-15 14:31:26 -05:00
Ryan Cramer
33bac98b83 Fix issue processwire/processwire-issues#599 2019-02-15 10:16:02 -05:00
Ryan Cramer
126c1392d4 Fix additional phpdoc typos per processwire/processwire-issues#797 2019-02-14 08:01:39 -05:00
Ryan Cramer
a993a14de9 Additional adjustment for processwire/processwire-issues#811 2019-02-14 06:02:09 -05:00
Ryan Cramer
3f5773e674 Add new $page->if(condition, yes, no) convenience method 2019-02-13 14:53:59 -05:00
Ryan Cramer
b499aad60e Fix issue processwire/processwire-issues#811 2019-02-12 10:53:54 -05:00
Ryan Cramer
82d513ec54 Update for processwire/processwire-issues#358 to prevent port 443 from being appended to undefined $config->httpHost when scheme is HTTPS. 2019-02-12 09:14:44 -05:00
Ryan Cramer
feb071b006 Make WireDateTime::relativeTimeStr() hookable per processwire/processwire-issues#793 2019-02-12 08:27:40 -05:00
Ryan Cramer
a47c6102f7 Some updates to the Pageimage::render() method plus add a Pageimages::render() method to accompany it. 2019-02-12 06:13:00 -05:00
Ryan Cramer
6c8d0e5eff Move implementation for wirePopulateStringTags() function into WireTextTools class. Add new WireData() shortcut function for consistency with existing WireArray() and PageArray() functions. 2019-02-12 05:25:37 -05:00
Ryan Cramer
839d9325cc Fix issue processwire/processwire-issues#331 2019-02-08 15:21:25 -05:00
Ryan Cramer
09a7d8e7ed Add $sanitizer->fieldSubfield() method for sanitizing "field.subfield" strings. 2019-02-08 14:46:48 -05:00
Ryan Cramer
65454985b1 Various minor adjustments 2019-02-08 14:46:20 -05:00
Ryan Cramer
5b45d17991 Attempt fix for issue processwire/processwire-issues#304 2019-02-07 11:34:35 -05:00
Ryan Cramer
ea62d73215 Fix issue processwire/processwire-issues#807 2019-02-07 09:35:07 -05:00
Ryan Cramer
ac0f4f0017 Fix issue processwire/processwire-issues#805 2019-02-05 09:27:56 -05:00
Ryan Cramer
09c936dba9 Attempt to fix processwire/processwire-issues#801 2019-02-05 08:46:10 -05:00
Ryan Cramer
1215e706e1 Fix issue processwire/processwire-issues#787 2019-02-05 08:39:21 -05:00
Ryan Cramer
69bd63eb4c Fix issue processwire/processwire-issues#779 where $pages('/') wasn't working 2019-02-05 08:32:39 -05:00
Ryan Cramer
733780f8f6 Fix phpdoc typos per issue processwire/processwire-issues#797 2019-02-04 15:19:08 -05:00
Ryan Cramer
7d79864dc9 Fix issue processwire/processwire-issues#790 plus add Pageimage::render() method 2019-02-04 14:53:49 -05:00
Ryan Cramer
c6cb728b60 Fix issue processwire/processwire-issues#788 2019-02-04 12:07:16 -05:00
Ryan Cramer
0ac9046b28 Add error message to resolve issue processwire/processwire-issues#786 2019-02-04 11:58:51 -05:00
Ryan Cramer
4348ca746a Fix issue processwire/processwire-issues#785 2019-02-04 11:20:47 -05:00
Ryan Cramer
0be324b4d5 Fix issue processwire/processwire-issues#781 2019-02-04 10:51:31 -05:00
Ryan Cramer
debb1cd511 Add $sanitizer->chars() method and update phpdoc at top of Sanitizer class for documentation purposes on API reference site. 2019-02-04 10:32:36 -05:00
Ryan Cramer
6ad5239666 Bump version to 3.0.125 2019-01-25 15:00:32 -05:00
Ryan Cramer
1aad2430d5 Update WireInput/WireInputData to optimize for sanitizer updates 2019-01-25 11:58:26 -05:00
Ryan Cramer
e879baf189 Add new $sanitizer methods: range(), min(), max(), bit(), maxLength(), maxBytes(), methodExists(), validate() and valid(). Also added support for executing multiple sanitizers in one call with the new sanitize() method. 2019-01-25 11:42:38 -05:00
Ryan Cramer
4a7919c9a9 Update the __('text') translation function to support setting of options with a __(true, 'optionName', 'optionValue') call. Added support for an entityEncode option and translations option for setting predefined fallback translations. Also made major updates to the phpdoc for the translation functions. 2019-01-25 11:33:17 -05:00
Ryan Cramer
02f9220529 Some minor 1-line tweaks to various files 2019-01-25 11:32:47 -05:00
Ryan Cramer
411cce0418 Update the $input->get(), $input->post(), and $input->cookie() methods to support new arguments for support of auto-sanitization, validation and fallback values. 2019-01-18 11:51:59 -05:00
Ryan Cramer
7bbb97e6e4 phpdoc improvements to WireFileTools, FunctionsWireAPI, FunctionsAPI and Functions files. 2019-01-18 11:50:22 -05:00
Ryan Cramer
97adac20ed Bump version to 3.0.124 2019-01-11 15:53:43 -05:00
Ryan Cramer
52e20c489d Add new $sanitizer->trim() method that can trim of all known UTF-8 whitespace types (or given chars) from beginning and ending of string. This is something I discovered PHP's trim() fucntion does not do. 2019-01-11 09:30:51 -05:00
Ryan Cramer
f9ded64ba3 Improve the WireHttp class by adding a CURL-based get(), post(), head() or send() fallback that is used (if CURL available) when the fopen() method fails. It can also be f orced from the new $options argument added to all the sending methods. 2019-01-11 09:29:32 -05:00
Ryan Cramer
2735ee397e Update PW's error handler to recognize failed functions API calls when functions API isn't enabled, and provide instruction on how to enable it 2019-01-11 09:13:40 -05:00
Ryan Cramer
1a94578b1e Make the functions API automatically enabled by default in core site profiles 2019-01-11 09:10:43 -05:00
Ryan Cramer
3517295e27 Minor improvements to default CKEditor contents.css, bumping up the font size slightly and increasing the left/right margin a bit 2019-01-11 09:08:58 -05:00
Ryan Cramer
770b75bb8a Inconsequential SCSS to CSS recompiles from phpstorm 2019-01-11 09:01:07 -05:00
Ryan Cramer
703fa29c85 Improvements to searchability of FieldtypeOptions fields, now enabling it to match either value or title. 2019-01-11 08:52:12 -05:00
Ryan Cramer
36c0f401e7 Phpdoc improvements to various core classes 2019-01-11 08:48:20 -05:00
Ryan Cramer
1601cdec73 Update MarkupQA class to support custom settings in $config->markupQA with the most useful one being 'ignorePaths' array containing starting paths that should be ignored when it comes to link abstraction. 2019-01-11 08:45:13 -05:00
Ryan Cramer
649d2569ab Bump version to 3.0.123 2018-12-21 14:30:49 -05:00
Ryan Cramer
df4ab1dbd3 Update for processwire/processwire-issues#408 InputfieldSelector count() to wireCount() 2018-12-20 11:52:01 -05:00
Ryan Cramer
05367367c0 Add $options argument support to WireMailTools::new(), i.e. $mail->new(array $options) 2018-12-20 11:17:02 -05:00
Ryan Cramer
67898cb36a Add additional mbstring check to Sanitizer and add two new whitespace reduction methods (for internal use) 2018-12-20 11:15:34 -05:00
Ryan Cramer
e20b6917d3 Fix issue processwire/processwire-issues#724 UTF-8 pagenames in non-default language 2018-12-20 10:58:26 -05:00
Ryan Cramer
a0570bb2a0 Update for processwire/processwire-issues#767 2018-12-19 12:24:22 -05:00
Ryan Cramer
1b059b0ced Bump version to 3.0.122 plus some other minor adjustments 2018-12-14 14:24:50 -05:00
Ryan Cramer
a89543944d Update WireArray::import() to support duplicate items when duplicate checking is turned off per processwire/processwire-issues#767 2018-12-14 13:12:52 -05:00
Ryan Cramer
b773c81ae9 Fix issue processwire/processwire-issues#766 auto-remove UTF-8 value of &#8232 line-seperator entity in $sanitizer->text() function 2018-12-14 13:07:26 -05:00
Ryan Cramer
e8d2eed1ba Add @horst-n fix for processwire/processwire-issues#715 2018-12-14 11:05:07 -05:00
Ryan Cramer
05fd707b9c Fix issue processwire/processwire-issues#763 for ProcessRole, plus this commit also contains some minor tweaks to ProcessPageLister 2018-12-14 09:03:23 -05:00
Ryan Cramer
c1b9349a7c Fix issue with Safari column widths in AdminThemeUikit that use showIf conditions per processwire/processwire-issues#480 2018-12-14 07:58:26 -05:00
Ryan Cramer
6801e2df94 Fix issue processwire/processwire-issues#756 make link modal files selection not require being open before it can be populated with files from page selection 2018-12-14 06:01:02 -05:00
Ryan Cramer
6d1b558d85 Fix issue processwire/processwire-issues#723 2018-12-12 14:28:24 -05:00
Ryan Cramer
365d49bcaa Fix issue processwire/processwire-issues#749 2018-12-12 14:19:57 -05:00
Ryan Cramer
a40f24d722 A couple of updates to account for new PHP 7.2/7.3 notices per processwire/processwire-issues#408 2018-12-12 11:59:02 -05:00
Ryan Cramer
f39d4387d5 Various minor tweaks/updates and bump version to 3.0.121 2018-12-07 14:58:03 -05:00
Ryan Cramer
3b6b5eea7d Update locale warning message in ProcessLogin per processwire/processwire-issues#732 2018-12-07 12:09:57 -05:00
Ryan Cramer
3e690b8ea4 Update phpdoc @return statement for Page::index() 2018-12-07 10:15:32 -05:00
Ryan Cramer
af0afe9f95 Additional updates for processwire/processwire-issues#751 plus some enhancements to PageFinder 2018-12-07 09:57:07 -05:00
Ryan Cramer
0dc8766491 Fix issue processwire/processwire-issues#751 with Page::index() not working for hidden/unpublished pages 2018-12-06 14:05:17 -05:00
Ryan Cramer
d7b7acb8e5 Various minor adjustments and bump version to 3.0.120 2018-11-30 13:52:24 -05:00
Ryan Cramer
0442572885 Fix issue processwire/processwire-issues#758 unable to select "17:10:02" time format in FieldtypeDatetime 2018-11-30 13:42:15 -05:00
Ryan Cramer
65b3edb761 Fix issue processwire/processwire-issues#750 where WireShutdown email was missing a send() call 2018-11-30 13:21:33 -05:00
Ryan Cramer
9aa0de6351 Fix issue processwire/processwire-issues#743 2018-11-30 13:15:27 -05:00
Ryan Cramer
45e8a00395 Fix issue processwire/processwire-issues#741 2018-11-30 12:29:20 -05:00
Ryan Cramer
a8d4a6cd46 Add phpdoc for Template::icon per processwire/processwire-issues#739 2018-11-30 12:03:49 -05:00
Ryan Cramer
91fdca02ea Fix issue processwire/processwire-issues#728 2018-11-29 14:12:33 -05:00
Ryan Cramer
ffdc1031de Fix issue processwire/processwire-issues#727 2018-11-29 14:02:46 -05:00
Ryan Cramer
c7447bf838 Fix issue processwire/processwire-issues#726 where Pages > Tree > dropdown was displaying liternal icon names (in addition to actual icon) in some cases when it shouldn't 2018-11-29 13:59:36 -05:00
Ryan Cramer
009dec332c Fix issue processwire/processwire-issues#725 where fallback email for comment notifications not working if hostname had port number 2018-11-29 13:31:05 -05:00
Ryan Cramer
e6213c23b3 Attempt fix for issue processwire/processwire-issues#724 where creating page from page reference field using UTF8 charset was failing. 2018-11-29 13:19:40 -05:00
Ryan Cramer
0cb693c450 Fix issue processwire/processwire-issues#719 where 'parent' property didn't work for $page->setAndSave() 2018-11-29 12:01:57 -05:00
Ryan Cramer
fcc353bc3c Updates per processwire/processwire-issues#704 with WireTempDir log entries 2018-11-29 11:12:11 -05:00
Ryan Cramer
cc212b3254 Add hasNext() and hasPrev() methods to PaginatedArray class 2018-11-21 10:49:53 -05:00
Ryan Cramer
b083c96717 Test commit, please ignore 2018-11-20 17:01:10 -05:00
Ryan Cramer
e61bb4982a Bump version to 3.0.119 2018-11-16 16:05:14 -05:00
Ryan Cramer
fad1e11575 Add a setting() function to the Functions API for more convenient management of site-specific runtime settings 2018-11-16 11:39:12 -05:00
Ryan Cramer
3eecb6cf25 Update Pageimage::getVariations() and Pageimage::removeVariations() methods with several new options, primarily to support a new PageAction module for clearing out old image variations 2018-11-16 09:51:06 -05:00
Ryan Cramer
96f62b313f Add WireArray::slices($qty) method that returns the WireArray sliced into $qty equal parts (new WireArray objects) 2018-11-09 14:26:32 -05:00
Ryan Cramer
32f594de2a Update Pages Export/Import class to support export/import of page created and modified dates 2018-11-09 14:25:34 -05:00
Ryan Cramer
93a9747657 Add @BitPoet processwire/processwire-requests#233 to make ajax file upload use chunked method for potential reduced memory usage and support of larger file uploads 2018-11-08 09:28:03 -05:00
Ryan Cramer
c146c71de3 Add @Toutouwai processwire/processwire-requests#242 to support automatic open of collapsed Inputfield when file dragged into file/image or CKEditor field that supports it 2018-11-08 09:04:04 -05:00
Ryan Cramer
7629e518e3 Remove unintended dev constant from AdminThemeUikit.module 2018-11-05 14:58:56 -05:00
Ryan Cramer
a46336de00 Bump version to 3.0.118 2018-11-02 08:07:24 -04:00
Ryan Cramer
e65c287099 Update AdminThemeUikit Uikit version to rc17 2018-11-02 06:55:19 -04:00
Ryan Cramer
a355bd74e6 Add renderedExtras() hook to ProcessPageLister, plus make the finalSelector property modifiable if needed by hooks, per request from @renobird 2018-11-02 06:44:49 -04:00
Ryan Cramer
a0192327e0 Some refactoring in ProcessPageEdit, plus fix issue where user having page-edit-lang-[lang] permission while having non-default language selected in their profile could receive incorrect "missing required field" error for Page Name, when making edits. 2018-11-02 06:41:35 -04:00
Ryan Cramer
09ee56b41d Add new “What URLs redirect to this page?” fieldset in the page editor Settings tab (near the bottom). This section enables you to see all URLs that redirect to the page, and also lets you add new redirect URLs or remove existing ones. Currently available to superusers only. 2018-10-31 10:29:06 -04:00
Ryan Cramer
6fb6837406 Add $page->addUrl($url, [$language]); and $page->removeUrl($url); methods that allow you to add or remove redirects to a page programatically. This is provided by updates to the PagePathHistory module, which also received several unrelated updates, like support for virtual path history, which is historical URLs for a page determined by changes to parent pages. 2018-10-31 10:22:47 -04:00
Ryan Cramer
54537e77fa Add new $files->unlink() and $files->rename() methods to files API var, plus enhance many file methods with additional security for path verification, and update PW's various usages of unlink/rename to use the new versions provided by the files API var. 2018-10-31 09:30:15 -04:00
Ryan Cramer
0f9eb0aaf5 Add hasValue(), hasTitle() and hasID() methods to SelectableOptionArray class 2018-10-26 12:21:10 -04:00
Ryan Cramer
7331bac132 Addition of a $page->numParents() method/property which reflects the number of parents the page has, aka depth in the tree. This commit also has several small adjustments and fixes, including a fix for the issue introduced last week that caused issues with WireArray in versions of PHP prior to 7.x 2018-10-26 12:18:46 -04:00
Ryan Cramer
b964fd1a15 Bump version to 3.0.117 2018-10-19 14:48:19 -04:00
Ryan Cramer
d12a78d07b Update WireArray class to support usage as a general purpose array (that can hold strings, numbers, etc.), rather than being limited to support of Wire derived objects. Also add WireArray::new($items) static method that lets you create a new WireArray and add $items to it in one shot. The same new() method works on any WireArray derived type such as PageArray and any others. 2018-10-19 11:14:29 -04:00
Ryan Cramer
f5cda0acdb Update the FieldtypeComments classes to support for more output configuration options for CommentForm and CommentList 2018-10-19 11:12:58 -04:00
Ryan Cramer
faaf8adec9 Add support for a "pw-optional" or "data-pw-optional" attribute to Markup Regions. When a defined region of markup has this boolean attribute present, the wrapping region tags will be removed from the document if they will be blank (i.e. nothing was populated to them). 2018-10-19 11:06:52 -04:00
Ryan Cramer
cf322b6dca Improvements to FieldtypeTextarea::importValue() to improve accuracy of linked asset URLs on pages imported from one site to another where the root URL has changed (subdirectory vs. non subdirectory), as well as better handling asset URLs where the link included a hostname 2018-10-19 11:04:30 -04:00
Ryan Cramer
0b8382ab3e A few small improvements to the PagesExportImport class 2018-10-19 11:02:54 -04:00
Ryan Cramer
6367fb7668 Forgot to bump version to 3.0.116 on Friday, so this fixes that 2018-10-08 13:45:46 -04:00
Ryan Cramer
e2b6453a96 Fix issue processwire/processwire-issues#718 FieldtypeFloat float typecast in getPrecision() method 2018-10-05 10:00:12 -04:00
Ryan Cramer
8e22bee1b5 Fix issue processwire/processwire-issues#716 where $page traversal methods like next() and prev() were not working on RepeaterPage items 2018-10-05 09:48:20 -04:00
Ryan Cramer
5fddd95b43 Add new Page::numDescendants() method and property, plus descendants() and descendant() alias methods. Add Page::findOne() method. Update ProcessPageList with the ability to customize what is shown in the numChildren/count shown for each Page, along with the ability to display the the newly added Page descendants numbers instead of or in addition to the Page numChildren. 2018-10-05 08:11:16 -04:00
Ryan Cramer
faa0d4f4df Update ProcessPageTrash module to be more clear, showing confirmation before page list, and adding option to specify time limit. Also now reports more verbose info about the trash operation including a "pages trashed per second" rate for comparison purposes on different DB engines and settings. Related to processwire/processwire-issues#692 2018-10-03 11:50:30 -04:00
Ryan Cramer
e9c7178a22 Optimizations and updates to PagesTrash class emptyTrash() process, plus add transaction support to empty trash process per processwire/processwire-issues#692 though currently I'm not seeing any effect from it 2018-10-03 11:46:22 -04:00
Ryan Cramer
0ec70c875f Update PagesEditor::savePageStatus() method to also accept array of page IDs (rather than just 1 page ID). Also accepts Page or PageArray objects now too. 2018-10-03 11:45:07 -04:00
Ryan Cramer
17c872b079 Set a default user-agent header in WireHttp to resolve issues where accessed hostname seems to require it (noticed this on a couple hosts running Varnish, which seem to throw a 400/403 if no user-agent header present) 2018-10-03 06:10:26 -04:00
Ryan Cramer
546340652f Attempt fix for issue processwire/processwire-issues#661 where cached PageList data could re-appear when closing then re-opening an item in the PageList. Was an issue if an item was moved or trashed, because it could appear to still be in the old location in the PageList even when it wasn't. This commit also refactors several parts in ProcessPageList.js. 2018-10-01 11:21:43 -04:00
Ryan Cramer
11cb8deaa2 Bump version to 3.0.115 2018-09-28 14:23:16 -04:00
Ryan Cramer
3ab9b358e5 Update ProcessLogin to support a configuration option to specify roles that should be prompted to enable two-factor authentication 2018-09-28 11:14:19 -04:00
Ryan Cramer
2f2bb118b0 Update ProcessProfile to use a dialog window prompt (via vex) when saving changes to a field requires current password. 2018-09-28 10:31:50 -04:00
Ryan Cramer
c55a547458 Add allowOriginal option to Pageimage::size() and Pageimage::maxSize() to avoid creating variations when original image is already at target dimension 2018-09-27 08:48:02 -04:00
Ryan Cramer
45f0c14831 Adjustment for issue processwire/processwire-issues#709 2018-09-27 07:54:57 -04:00
Ryan Cramer
e6bcd3e44c Fix issue processwire/processwire-issues#712 to correct issue with $config->sessionHistory=1; setting not working correctly 2018-09-27 07:39:53 -04:00
Ryan Cramer
a1bac571f3 Fix issue processwire/processwire-issues#708 Modules refresh showing unnecessary error message 2018-09-26 11:11:54 -04:00
Ryan Cramer
42511ebb77 Additional tweak for processwire/processwire-issues#709 2018-09-26 11:04:53 -04:00
Ryan Cramer
42ec0e3cf4 Some refactoring in WireTempDir class and attempt fix of issue processwire/processwire-issues#704 2018-09-26 09:40:40 -04:00
Ryan Cramer
bf8baf36dd Add new methods to $files API var: fileInPath($file, $path); unixDirName($dir); unixFileName($file); filePutContents($file, $contents); 2018-09-26 09:40:16 -04:00
Ryan Cramer
e5900e7567 Attempt fix for processwire/processwire-issues#704 2018-09-25 10:50:54 -04:00
Ryan Cramer
bddea16255 Clarify wording in phpdoc of Wire::trackChanges() method per processwire/processwire-issues#707 2018-09-25 10:26:02 -04:00
Ryan Cramer
f2edf04602 Fix issue processwire/processwire-issues#709 where Pageimage::maxSize() could crop when it shouldn't when source image smaller than maxSize requested dimensions 2018-09-25 10:13:51 -04:00
Ryan Cramer
3b4cdeb991 Make it possible to add Pagefile from another Page to Pagefiles WireArray per request processwire/processwire-issues#710 2018-09-25 09:32:39 -04:00
Ryan Cramer
96bd37eec9 Bump version to 3.0.114 2018-09-21 14:03:32 -04:00
Ryan Cramer
b4aec46a67 Add $database->supportsTransaction($table = '') method to return true or false as to whether the database (or a specific table) supports transactions 2018-09-21 13:30:57 -04:00
Ryan Cramer
9a7e5d5bd3 Add PagefilesManager->importFiles() and PagefilesManager->replaceFiles() methods 2018-09-21 13:29:47 -04:00
Ryan Cramer
f0cc6f1134 Update to support module autoload order per request from @adrianbj 2018-09-21 12:29:15 -04:00
Ryan Cramer
8add252813 Fix issue processwire/processwire-issues#700 where ProcessPermission "add new" was incorrectly showing Save+Publish buttons 2018-09-20 11:58:20 -04:00
Ryan Cramer
8973a9de49 Bump version to 3.0.113 2018-09-14 15:29:00 -04:00
Ryan Cramer
d7a84f223c Update CKEditor version from 4.8.0 to 4.10.1 2018-09-14 15:26:48 -04:00
Ryan Cramer
42b46152eb Refactoring of SessionLoginThrottle. Prevents it from being too aggressive when TFA is in use, improves clarity of message to user, and adds the ability to log failures. 2018-09-14 12:03:16 -04:00
Ryan Cramer
64680df68f Refactoring of ProcessController and add support for access controlled methods that works with the existing moduleInfo['nav'][]['permission'] setting. Previously that permission was only used to determine whether to show the item in the nav, and you had to do your own separate permission check in the actual method implementation. This commit also moves all the WireException definitions to the main core/Exceptions file. 2018-09-14 11:02:30 -04:00
Ryan Cramer
590a69502c Update $mail (WireMailTools) API var with some fluent interface shortcuts, enabling you to start with $mail->to(mail), $mail->from(email), or $mail->subject(email), all of which return a WireMail instance, rather than having to start with a $mail->new() to get the WireMail instance. While minor, this makes for a little bit shorter API calls when using WireMail fluent interface. 2018-09-14 10:36:14 -04:00
Ryan Cramer
c1c705ab59 Refactoring of WireShutdown class and related minor updates to some other classes 2018-09-14 10:34:56 -04:00
Ryan Cramer
89caff1bdc Fix issue processwire/processwire-issues#697 pages with $template->noMove setting could not be restored/moved out of the trash 2018-09-13 12:11:43 -04:00
Ryan Cramer
0284bd28a1 Fix issue processwire/processwire-issues#695 change order of listable() call in ProcessPageList::renderNavJSON so that one could make an viewable/editable page non-listable in the tree nav dropdown 2018-09-13 11:17:21 -04:00
Ryan Cramer
84e5fd6b51 Fix issue processwire/processwire-issues#690 where isMoveable() error messages were not yet multi-language translatable 2018-09-13 10:33:40 -04:00
Ryan Cramer
2c9b25fdfd Fix issue processwire/processwire-issues#686 where FieldtypeCheckbox::markupValue() result ended up blank after a PHP strip_tags() 2018-09-13 10:07:29 -04:00
Ryan Cramer
272077b1cf Fix issue processwire/processwire-issues#685 where templates with noParents=-1 setting (only one allowed) were not shown as selectable in Template field in page editor 2018-09-13 09:08:44 -04:00
Ryan Cramer
818d9f50a6 Fix issue processwire/processwire-issues#683 where $pages->findOne() didn't have exclusion/filter enabled if finding unpublished or hidden page that is editable & viewable to current user when the selector includes only reference to the page's id. 2018-09-13 08:50:49 -04:00
Ryan Cramer
efb7a8ace2 Fix issue processwire/processwire-issues#682 fix typo about what tab to change the unpublished setting 2018-09-13 08:34:27 -04:00
Ryan Cramer
844946b706 Fix issue processwire/processwire-issues#681 where Fieldtype exceptions getting caught when saving page, which could interfere with InnoDB transactions when Fieldtype throws an Exception 2018-09-13 08:29:49 -04:00
Ryan Cramer
dfc0c4da52 Fix issue processwire/processwire-issues#680 where ProcessLanguageTranslator does not skip over /site/assets/ on Windows servers 2018-09-13 06:21:31 -04:00
Ryan Cramer
adca762ee1 Fix issue processwire/processwire-issues#679 capture Exception in ProcessLanguageTranslator when it throws due to too many directories 2018-09-13 06:12:13 -04:00
Ryan Cramer
468ecb6b29 Make ProcessPageSearchLive::execute() hookable per processwire/processwire-issues#675 2018-09-12 13:20:26 -04:00
Ryan Cramer
7a89f4f188 Fix issue processwire/processwire-issues#671 where LiveSearch pages list could also show users if custom configured field name present on user template 2018-09-12 12:57:11 -04:00
Ryan Cramer
313fee873c Fix issue processwire/processwire-issues#669 admin liveSearch noSearchTypes option not working for 'pages', 'trash' or 'modules'. 2018-09-12 12:35:49 -04:00
Ryan Cramer
8315776960 Module download/install process label updates per processwire/processwire-issues#641 2018-09-12 11:58:52 -04:00
Ryan Cramer
6c9b475559 Add @horst-n PR #118 which fixes processwire/processwire-issues#628 in ImageSizerEngine 2018-09-12 11:16:33 -04:00
Ryan Cramer
b07d7341eb Improvements and refactorings to inputfields.js, plus add processwire/processwire-requests#224, and addition of new public API methods: InputfieldFocus($inputfield) which finds, opens and focuses an Inputfield (making it visible) regardless of where it is in the interface; InputfieldOpen($inputfield) which opens a collapsed Inputfield; and InputfieldClose($inputfield) which does the opposite. Thanks to @Toutouwai for the feature request and example code that got this started. 2018-09-11 11:31:34 -04:00
Ryan Cramer
e2478aa401 Add new string case conversion methods to Sanitizer: hyphenCase(), snakeCase(), camelCase(), and pascalCase() 2018-09-10 11:15:59 -04:00
Ryan Cramer
d260b269c5 Bump version to 3.0.112 2018-09-07 14:45:21 -04:00
Ryan Cramer
77cf1b9c71 Typo fix in ProcessField 2018-09-07 14:43:15 -04:00
Ryan Cramer
af423e5406 Update WireHttp to support more HTTP response codes (the success ones, in addition to the existing error codes) 2018-09-07 14:42:04 -04:00
Ryan Cramer
7e5f79e9b3 Fix issue where drag/drop image into non-default language CKEditor field was showing error message 2018-09-04 12:01:46 -04:00
Ryan Cramer
4c48e1d941 Refactoring in ProcessPageClone.module and related hooks and multi-language support, plus some improvements to new PagesNames class. 2018-08-30 06:22:02 -04:00
Ryan Cramer
aa34e8a0f7 Bump version to 3.0.111 2018-08-24 16:41:05 -04:00
Ryan Cramer
cc53b835b6 Refactoring of the PagesTrash class and related, plus some minor additions to PagesNames class 2018-08-24 10:41:47 -04:00
Ryan Cramer
dfa8cc7b74 Refactoring of the PagesEditor class, primarily addition of a new PagesNames class for handling page names, duplicate names, untitled pages, incrementing page names, etc. 2018-08-23 10:30:12 -04:00
Ryan Cramer
088938454b Move random generation functions from Password to new WireRandom class, and add several new methods for generating random strings, numbers, arrays, etc. 2018-08-21 11:23:20 -04:00
Ryan Cramer
ff123065eb Update the clone/copy repeater item function to also account for InputfieldTable rows that may be present in the repeater 2018-08-19 07:01:54 -04:00
Ryan Cramer
5554e87b47 Fix issue processwire/processwire-issues#667 where cloning a FieldtypeOptions field was only cloning the field, and not the options. 2018-08-13 05:56:57 -04:00
Ryan Cramer
563f4d237b Bump version to 3.0.110 2018-08-10 15:32:08 -04:00
Ryan Cramer
3c923dfb93 Some updates and improvements to the Password::randomAlnum() function 2018-08-10 11:36:51 -04:00
Ryan Cramer
acd42bbfc9 Update ProcessPageView to support optional single subdir for page files when $config->pagefileSecure mode is active. This enables it to work when pagefileSecure mode is active and a module (like ProDrafts) uses a subdirectory of of a page's file/asset directory 2018-08-09 11:15:06 -04:00
Ryan Cramer
2e672428a4 Fix issue processwire/processwire-issues#659 where PageFinder "_custom" option didn't yet have support for "field.owner.property=value" selectors 2018-08-08 15:05:21 -04:00
Ryan Cramer
82641a5521 Add request processwire/processwire-issues#614 to make InputfieldPassword use version of id attribute for confirm input rather than name attribute, for cases where someone might want to have multiple password fields in the same document. 2018-08-08 14:48:10 -04:00
Ryan Cramer
9fc4ff9e21 Add request processwire/processwire-issues#657 to clarify what characters are allowed when creating templates, plus some other minor improvements in ProcessTemplate 2018-08-08 09:17:46 -04:00
Ryan Cramer
fc8f6ad51c Fix issue processwire/processwire-issues#658 where non-specified, non-default-language pageNumUrlPrefix settings weren't properly falling back to $config default setting 2018-08-08 08:05:37 -04:00
Ryan Cramer
cee8504db9 Fix issue processwire/processwire-issues#656 where recently added Inputfield::setParent() update was causing issues (ListerPro config tab as one example) 2018-08-08 06:21:01 -04:00
Ryan Cramer
6d1dc83a45 Fix issue processwire/processwire-issues#639 where template configured to unpublish when missing required field was not applying to missing required fields within repeater items. 2018-08-08 06:16:04 -04:00
Ryan Cramer
69424df158 Fix issue processwire/processwire-issues#642 where new ProcessPageSearch feature was not using correct edit URL when searching for users 2018-08-07 06:17:32 -04:00
Ryan Cramer
6cc5a01cf5 Fix issue processwire/processwire-issues#646 where repeater trash icon click not triggering InputfieldStateChanged on InputfieldRepeater on case one forgets to save their changes 2018-08-07 06:02:58 -04:00
Ryan Cramer
683e24e010 Add an update to accommodate request in processwire/processwire-issues#648 for wording of warning message when a 3rd party hook has changed a page's name behind the scenes after it was saved 2018-08-06 11:31:35 -04:00
Ryan Cramer
a1676b0adb Fix issue processwire/processwire-issues#650 cropped images in image fields with overwrite option enabled not showing 2018-08-06 10:33:17 -04:00
Ryan Cramer
6882abe40a Fix issue processwire/processwire-issues#653 Inputfield parent issue causing out of memory errors when two buttons lacking name attributes 2018-08-06 08:21:21 -04:00
Ryan Cramer
c9a210ec35 Bump version to 3.0.109 2018-08-03 12:45:52 -04:00
Ryan Cramer
f61bca5d9f A couple of other minor updates to Session and FieldtypeModule 2018-08-03 12:21:00 -04:00
Ryan Cramer
95adb8039c Updates to Tfa base class, plus updates to ProcessLogin for TFA support 2018-08-03 12:17:12 -04:00
Ryan Cramer
5af6e63358 Add two-factor authentication module base class. Two modules that implmement it coming shortly. 2018-08-02 12:33:06 -04:00
Ryan Cramer
730b61a3c3 Various minor module updates 2018-08-02 12:31:41 -04:00
Ryan Cramer
fb9d1458a8 Update FieldtypeModule to support a couple of new config options for blank value and showing a "none" option when used with radios. 2018-08-02 12:23:18 -04:00
Ryan Cramer
fa2c8e30b9 Fix PSR-4 stacked namespace loading issue in WireClassLoader 2018-08-02 12:21:58 -04:00
Ryan Cramer
75889d3ac9 Update Password class with new methods for random string generation 2018-08-02 12:19:38 -04:00
Ryan Cramer
5e389ff65a Upgrade Inputfield and InputfieldWrapper base classes to support new getting, setting, and traversal methods. Plus a new $inputfields->new() method that lets you create a new Inputfield and add it to the wrapper in one step. 2018-08-02 12:17:59 -04:00
Ryan Cramer
c01289edb7 Attempt fix for issue processwire/processwire-issues#635 with FileCompiler and PHP touch() when PW run under different user accounts 2018-07-19 06:58:21 -04:00
Ryan Cramer
22fe5794d0 Fix issue processwire/processwire-issues#634 where single-use tokens in SessionCSRF needed an automatic reset to be consistent with behavior described the phpdoc 2018-07-18 07:45:38 -04:00
Ryan Cramer
b439a4488f Fix issue processwire/processwire-issues#631 where page-edit-lang-[name] permissions could prevent intended "addable" permission on templates. 2018-07-18 06:48:59 -04:00
Ryan Cramer
b6ec6cd679 Fix issue processwire/processwire-issues#629 where $config->pagefileSecure combined with non-superuser editing user profile didn't show images in image fields 2018-07-17 11:41:33 -04:00
Ryan Cramer
411fedf785 Fix issue processwire/processwire-issues#627 InputfieldCKEditor when extraAllowedContent empty 2018-07-16 11:37:56 -04:00
Ryan Cramer
2fb5ebbc96 Fix issue processwire/processwire-issues#625 where nested repeater with image field when combined with $config->pagefileSecure and access control without guest page-view permission was not appearing for logged-in non-superuser 2018-07-16 11:31:45 -04:00
Ryan Cramer
1404d1ef0f A couple of minor adjustments for 3.0.108 2018-07-13 13:25:18 -04:00
Ryan Cramer
1f4ca1b8f4 Some additional adjustments to the live search, including the addition of a "help" option, which you can access just by typing "help" into the search box. Bumped version to 3.0.108 2018-07-13 11:19:26 -04:00
Ryan Cramer
9741fbb818 Add 'language' option to $page->editUrl() method 2018-07-12 14:54:34 -04:00
Ryan Cramer
e038cb3d89 Update 3 admin themes for live search code, plus some other minor adjustments 2018-07-12 14:53:46 -04:00
Ryan Cramer
484d183054 Add a $modules->findByInfo() method that enables finding modules by factors in their module info, without instantiating the modules. Also some optimizations to the $modules->getModuleInfo() method. 2018-07-12 10:48:22 -04:00
Ryan Cramer
f8c41366c5 Update wireInstanceOf($instance, $className)) function to support interfaces for $className argument, regardless of whether the $instance argument is an object or a string (class name). Previous versions did not fully support interfaces for the $className argument. 2018-07-12 06:22:35 -04:00
Ryan Cramer
cd7a684b85 Add "searchable" module support to ProcessPageType and ProcessUser 2018-07-11 16:13:42 -04:00
Ryan Cramer
6b2f2243da Add "searchable" module support to ProcessTemplate and ProcessField 2018-07-11 16:13:00 -04:00
Ryan Cramer
14fce7e11a Add "searchable" module support to ProcessCommentsManager 2018-07-11 16:11:34 -04:00
Ryan Cramer
be9d07c57b Add new "addable" and "deletable" options to AsmSelect, enabling you to have AsmSelects where the ability to add and/or delete items is suppressed. With both options disabled, it's useful for sorting fixed groups of items. 2018-07-11 16:09:38 -04:00
Ryan Cramer
2e095c9cf4 Add url(), httpUrl() and editUrl() methods to Comment class. 2018-07-11 16:08:08 -04:00
Ryan Cramer
0dc2f17b5a Add a couple of new static methods to Selectors class for examining selector operators 2018-07-11 16:06:52 -04:00
Ryan Cramer
04187ed19f Update PaginatedArray/PageArray::getPaginationString() method to support new options (documented in phpdoc) 2018-07-11 16:05:48 -04:00
Ryan Cramer
756c9298a5 Add SearchableModule interface to Module definition, add getProcessPage() to Process module base class (which returns the Page object the Process page lives on), plus a couple of minor phpdoc improvements in core classes 2018-07-11 16:02:40 -04:00
Ryan Cramer
c3e0bbec86 Bump version to 3.0.107 2018-06-29 15:11:41 -04:00
Ryan Cramer
30bc99cfd7 Update ProcessPageList to have module configuration setting as to whether or not Trash and Restore is available to non-superusers. Plus add new Restore tab to ProcessPageEdit that appears when editing a page in the trash. Previously you could only restore by using the PageList "restore" action. 2018-06-29 12:27:01 -04:00
Ryan Cramer
bf62fbb897 Upgrade ProcessPageList to support showing and use of trash to non-superusers, for pages user is allowed to edit 2018-06-28 12:59:59 -04:00
Ryan Cramer
8e084a1ba0 Add new collapsed “What pages point to this one?” field on page editor Settings tab. This also demonstrates use of the $page->references() and $page->links(); methods. 2018-06-28 12:56:29 -04:00
Ryan Cramer
2883d2adb5 Add new $page->restorable() method to accompany the existing $page->trashable() method. This method returns true/false as to whether or not the page can be restored from the trash to its original location by the current user. Also add a PagePermissions::trashListable() method for determining whether the trash is viewable by the current user. 2018-06-28 12:53:56 -04:00
Ryan Cramer
54fad20ffd Add new Page methods: $page->links() which returns PageArray of other pages linking to it; $page->references() which returns PageArray of other pages referencing it in Page fields; $page->urls() which returns array of all URLs that can refer to the page, in all languages, and including both previous and current URLs. Plus add these properties: $page->references; $page->links; $page->numLinks; $page->numReferences; $page->hasLinks; $page->hasReferences; $page->referencing; $page->urls; see phpdoc for details on these properties. 2018-06-28 12:49:27 -04:00
Ryan Cramer
94524a8776 Add new find methods to FieldtypePage, FieldtypeTextarea and MarkupQA for finding page references and links specific to a given page. The public interfaces to these will be in the Page class (coming in next commit). 2018-06-28 12:47:05 -04:00
Ryan Cramer
5a9976f7a7 Improvements to User::hasPermission() method to support new permission detection options in context argument. 2018-06-28 12:43:09 -04:00
Ryan Cramer
8ef358eac7 Support option of forcing use of core WireMail even when other WireMail modules installed (primarily for testing/debugging purposes) 2018-06-28 12:42:03 -04:00
Ryan Cramer
bd325ba123 Fix issue where InputfieldImage could potentially detect ajax request mode when not applicable 2018-06-28 12:39:43 -04:00
Ryan Cramer
67bbd9641d Improvements to PagePathHistory module to support new options for getPathHistory() method 2018-06-28 12:38:24 -04:00
Ryan Cramer
fb65040836 Improvements to ProcessPageType and some phpdoc improvements to ProcessRole and ProcessPageLister 2018-06-28 12:35:18 -04:00
Ryan Cramer
cccc2d1161 Add lazy-loading option to WireInput, specified by $config->wireInputLazy=true; 2018-06-28 12:29:50 -04:00
Ryan Cramer
b54b3bace1 Add $modules->getModuleInfoProperty($module, 'name'); to retrieve just one property of module info, when useful. 2018-06-27 11:07:59 -04:00
Ryan Cramer
e5e13c1904 Fix issue processwire/processwire-issues#624 where WireHttp was producing error on the occasion when HTTP code present without text (which seems to be rare as far as I can tell). 2018-06-27 10:39:19 -04:00
Ryan Cramer
be1a203247 Fix issue processwire/processwire-issues#622 in $sanitizer->url() to workaround that PHP’s FILTER_VALIDATE_URL does not accept underscores in hostnames, despite their use being fairly common (even if not technically valid). 2018-06-27 10:24:59 -04:00
Ryan Cramer
1e912c4a4d Fix issue processwire/processwire-issues#623 where WireHttp::download() method was not working with URLs having encoded spaces when downloading with the "fopen" option (the "curl" option was not affected). Added a couple new $options to $sanitizer->url() method for dictating how encoded characters should be handled. Added WireHttp::setValidateURLOptions() method for cases where you want to manually specify different options for validating the URL in WireHttp. 2018-06-27 08:30:31 -04:00
Ryan Cramer
637f81579e Remove debug message() calls in ProcessPageEdit.module 2018-06-20 11:10:45 -04:00
Ryan Cramer
643c9d3a87 Bump version to 3.0.106 2018-06-15 13:19:15 -04:00
Ryan Cramer
afb4c4dbfd Add support for users with user-admin-all permission to be able to assign roles with user-admin permission per processwire/processwire-issues#607 2018-06-15 11:17:25 -04:00
Ryan Cramer
c82dba8835 Some upgrades and code consolidation in MarkupPagerNav, plus make it populate a $config->pagerHeadTags that one can output in a document head after rendering pagination, containing the <link rel='next|prev'.../> tags when desired. 2018-06-15 11:06:05 -04:00
Ryan Cramer
e60b79bd78 Upgrade $input->urlSegmentStr() method with new arguments, add an $input->pageNumStr() method, and add an $input->httpHostUrl() method that includes current scheme and hostname without path. 2018-06-15 11:03:38 -04:00
Ryan Cramer
28d275fe4d Fix issue processwire/processwire-issues#616 where PHP 7.2 is throwing deprecation notices for default arguments of idn_to_utf8() and idn_to_ascii(). The deprecation change and new defaults for next major PHP version does not appear to affect our usage after several tests, so opted to suppress these notices. 2018-06-15 07:58:50 -04:00
Ryan Cramer
828a80f1f6 Fix issue processwire/processwire-issues#621 where PageFrontEdit module config modal in AdminThemeDefault was showing extra/unnecessary buttons on modal dialog 2018-06-15 06:38:04 -04:00
Ryan Cramer
325ee3da15 Add a couple of new traversal methods to Inputfield: $inputfield->getRootParent(); and $inputfield->getForm();. While it was possible to get this info before, this makes it more obvious and straightforward. 2018-06-13 15:34:41 -04:00
Ryan Cramer
bd35c02e81 Improve support for Field tags by adding a new "Manage Tags" button at the bottom of the fields list screen, enabling you to add or remove fields to/from tags. In addition tags can now be used in $pages->find() searches, i.e. $pages->find("my_tag%=something"); would search all fields in the "my_tag" collection. 2018-06-13 15:31:55 -04:00
Ryan Cramer
a465fab672 Several fixes and impovements to PageFrontEdit per processwire/processwire-issues#609. Plus, this commit also improves PageFrontEdit usage with AdminThemeUikit, and improves behavior of multi-language fields. 2018-06-12 07:43:55 -04:00
Ryan Cramer
738c2db3c5 Correct issue in JqueryUI/modal.js where it used $() rather than jQuery() in one spot, and also fix issue where modal would stay open in some cases when we didn't want it to 2018-06-12 07:12:04 -04:00
Ryan Cramer
9ac3bfb5ad Various small code improvements and optimizations to ProcessPageEdit.module 2018-06-12 07:09:35 -04:00
Ryan Cramer
fff1250211 Various minor updates: add a $config->version($minVersion) function, add @since tags in Sanitizer, update count() => wireCount() in FieldtypePageTable and add PHPdoc hints where phpstorm is asking for them, update InputfieldImage to track change for the field when an action manipulates the image file, plus some other minor odds and ends. 2018-06-12 07:06:40 -04:00
Ryan Cramer
9ef1ac1486 Fix issue where drag-to-upload to CKE field within a repeater wasn't working quite right. Also updated it to default to drag-upload to image field in repeater item, if one is present (and supports more than one item), and use the owning page as a fallback for upload. 2018-06-10 08:08:53 -04:00
Ryan Cramer
d57b3ffcfc Bump version to 3.0.105 2018-06-01 15:39:56 -04:00
Ryan Cramer
6ef23f5c75 A couple of minor updates to AdminThemeUikit 2018-06-01 14:08:19 -04:00
Ryan Cramer
f6d8d3510f Add the “Regular” site profile to the core as another installation profile option 2018-06-01 13:26:07 -04:00
Ryan Cramer
ecf812224f Add $mail->sendHTML() and $mail->mailHTML() methods which are the same as their send() and mail() counterparts, but assume the given message body is HTML, and text-body is auto-generated. 2018-06-01 09:17:37 -04:00
Ryan Cramer
c0437702ce Fix issue processwire/processwire-issues#610 where .sort-date spans were showing in MarkupAdminDataTable columns at mobile width 2018-06-01 06:55:50 -04:00
Ryan Cramer
2abc454ce1 Add @rolandtoth PR #110 and PR #111 that fix issues with LanguageTabs in AdminThemeDefault/AdminThemeReno themes. 2018-05-31 14:55:33 -04:00
Ryan Cramer
07ab8ef9fc Add support for multi-language options support to InputfieldSelect, and modules that descend from it (radios, checkboxes, asmSelect, selectMultiple, etc). This enables you to use selectable options in multiple languages when used in FormBuilder or any other tool that uses InputfieldSelect without a Fieldtype. 2018-05-31 14:27:39 -04:00
Ryan Cramer
d2da3adcf7 Add new $mail->mail() method, which is the same as the existing $mail->send() method except that its arguments duplicate those of PHP mail(), making it possible to use as a drop-in replacement for PHP mail(). 2018-05-31 13:17:22 -04:00
Ryan Cramer
7c159f9c1a Some reworking of and improvements to the WireMail class, though no change to external interface (and should be no changes required for classes extending it). 2018-05-31 13:15:32 -04:00
Ryan Cramer
863abee8bb mprovements and new options for $sanitizer methods: text(), textarea(), unentities(). Plus, add new $sanitizer->removeWhitespace() method that can remove 99 different types of whitespace that can appear in a string (which includes the obvious whitespace, but also various UTF-8 and html-entity based whitespace). 2018-05-31 11:07:36 -04:00
Ryan Cramer
46e774ba38 Fix issue in FieldtypeComments::find() where "sort=something" selector wasn't always working if DatabaseQuerySelectFulltext modified the query 2018-05-30 07:29:00 -04:00
Ryan Cramer
bdaf8810cb Various minor AdminThemeUikit updates, mostly styles adjustments. Bump version to 3.0.104 and a couple other minor tweaks. 2018-05-25 11:10:41 -04:00
Ryan Cramer
5b38c26806 Update InputfieldCKEditor figure/figcaption styles to bind width of caption to width of image 2018-05-25 10:40:15 -04:00
Ryan Cramer
81ceb184b2 Update FieldtypeComments CommentList class to support a footer option 2018-05-25 10:38:40 -04:00
Ryan Cramer
ad9973ca33 Additional fix for processwire/processwire-issues#602 add z-index to InputfieldRepeater to prevent lost outline at bottom of field when repeater depth enabled 2018-05-24 05:47:56 -04:00
Ryan Cramer
da73572b95 Fix issue in Firefox where repeaters with depth in AdminThemeUikit had strange outline on containing Inputfield element per suggestion in processwire/processwire-issues#602 2018-05-24 05:45:21 -04:00
Ryan Cramer
c1dc6a9b9f Add @Notanotherdotcom PR #96 to allow use of field names with brackets (array) in field dependencies 2018-05-23 08:34:33 -04:00
Ryan Cramer
d06fd64171 Fix issue processwire/processwire-issues#603 where toggling repeater item open/closed makes outline disappear the second time you do it 2018-05-23 08:28:54 -04:00
Ryan Cramer
c512fd68e3 Fix issue processwire/processwire-issues#598 in FieldtypeOptions where manually assigned option IDs were not working 2018-05-23 07:45:33 -04:00
Ryan Cramer
91930953b6 Additional update for processwire/processwire-issues#596 2018-05-23 05:43:08 -04:00
Ryan Cramer
af3db8b5c1 Fix issue processwire/processwire-issues#600 where label saved to log was showing wrong path 2018-05-21 09:45:24 -04:00
Ryan Cramer
771c275432 Fix issue processwire/processwire-issues#597 allow radio button labels to wrap when needed 2018-05-21 09:40:35 -04:00
Ryan Cramer
79fa81d8a8 Add enhancement processwire/processwire-issues#596 show field labels rather than field names in Inputfield error messages 2018-05-21 09:23:47 -04:00
Ryan Cramer
2ba8d29ce2 Fix issue processwire/processwire-issues#595 where horizontal separator in dropdown nav wasn't quite right for the Page Add > Bookmarks option 2018-05-21 08:47:04 -04:00
Ryan Cramer
76a15a2539 Some other tweaks to Fieldtype and FieldtypeComments and bump version to 3.0.103 2018-05-18 15:07:24 -04:00
Ryan Cramer
9c38d29402 Improve support for fields that can be placed in custom header of CommentList class 2018-05-18 10:15:04 -04:00
Ryan Cramer
0fbd5a5882 Significant improvements to FieldtypeComments::find() method, adding support for selectors containing multiple sort values, OR values (for most properties), fulltext index searching of comment text, and matching comments based on field values present on the page that contains the comment field. 2018-05-18 10:12:04 -04:00
Ryan Cramer
aa34db127e Add WireLog option for specifying which user should be recorded with the log entry (if different from current user). 2018-05-18 09:21:13 -04:00
Ryan Cramer
7b2692f0cd Add new removeAllFor($namespace) method to Session, and improve the multi-IP option in the getIP() method 2018-05-18 09:19:01 -04:00
Ryan Cramer
ac480032c6 Major improvements and configuration options added to ProcessForgotPassword module. Also improves it for non-admin uses such as with the LoginRegister module. Additional added hooks also support more customization. 2018-05-18 09:17:38 -04:00
Ryan Cramer
8dda4368ea Bump version to 3.0.102 2018-05-11 12:00:35 -04:00
Ryan Cramer
8b285cfb17 Add @kixe PR #94 which adds a "disallow parallel sessions" option to SessionHandlerDB. When enabled, only one login can be maintained at a time per user. 2018-05-11 09:52:46 -04:00
Ryan Cramer
63cba339e4 Add "Cancel" JS callback to ProcessWire.confirm(message, funcOk, funcCancel) per PR #108 2018-05-11 08:20:26 -04:00
Ryan Cramer
717c1d2961 Add PR #107 so that panel.js triggers pw-panel-closed and pw-panel-opened events in JS 2018-05-11 06:33:33 -04:00
Ryan Cramer
8ef088f625 Some minor updates to WireTextTools::truncate() method to improve sentence type matches 2018-05-11 06:14:15 -04:00
Ryan Cramer
d7e0cccecb Apply the LanguageSupportPageNames duplicate checking feature without debug/advanced modes per processwire/processwire-issues#322 2018-05-10 06:25:30 -04:00
Ryan Cramer
7458d0504c Make InputfieldSelect show blank option even when selection is required per processwire/processwire-issues#585 2018-05-10 06:02:57 -04:00
Ryan Cramer
04123e305a Fix issue processwire/processwire-issues#588 where InputfieldFieldsetOpen lacked class InputfieldFieldset, which prevented it from having background color in AdminThemeUikit 2018-05-10 05:52:40 -04:00
Ryan Cramer
2d8dc82010 Fix issue processwire/processwire-issues#590 dropdown hover ajax menus using fix suggested in issue report 2018-05-10 05:37:29 -04:00
Ryan Cramer
e520c09f7a Update InputfieldPageAutocomplete.js to initialize the autocomplete on focus event, rather than on document ready. This resolves a render time issue in AdminThemeUikit when there are a lot of autocomplete inputs present, per @Toutouwai 2018-05-08 09:25:07 -04:00
Ryan Cramer
2f20fe402c Some code cleanup in ProcessPageSearch, plus add a hook for processwire/processwire-issues#584 2018-05-08 08:48:52 -04:00
Ryan Cramer
5127be3b35 Fix issue processwire/processwire-issues#587 where "Pages > Recent > Created" dropdown was showing pages created by user rather than all users. 2018-05-08 08:09:13 -04:00
Ryan Cramer
211cf78403 Fix issue processwire/processwire-issues#586 unnecessary "clone_field" property in field settings after a field is cloned. Unrelated: this commit also converts InputfieldPageAutocomplete to use an scss file. 2018-05-08 08:03:41 -04:00
Ryan Cramer
a3d2a447a9 Add fix for issue processwire/processwire-issues#584 allow quotes in InputfieldPageAutocomplete selectors 2018-05-08 05:43:24 -04:00
Ryan Cramer
ebe163452d Bump version to 3.0.101 2018-05-04 15:35:01 -04:00
Ryan Cramer
8d38ad7c44 Add $sanitizer->truncate() function per processwire/processwire-requests#163 2018-05-04 15:32:54 -04:00
Ryan Cramer
589d745caa Some small CSS fixes in ProcessModule 2018-05-02 06:24:49 -04:00
Ryan Cramer
c2bc83bc40 Attempt fix for processwire/processwire-issues#582 requiredIf and ProcessPageEdit behaviors 2018-05-02 06:14:47 -04:00
Ryan Cramer
027275ec40 Fix issue processwire/processwire-issues#583 $pages->clone() was failing when page had files and output formatting state was true. 2018-05-02 05:53:26 -04:00
Ryan Cramer
3296b699e2 Fix issue in InputfieldImage where negative degree rotate image actions were getting converted to positive degree rotates due to sanitization. 2018-05-01 06:09:21 -04:00
Ryan Cramer
8c18e3bf0d Fix issue processwire/processwire-issues#580 2018-04-30 05:56:36 -04:00
Ryan Cramer
ecdd3a3895 Bump version to 3.0.100 2018-04-27 16:34:17 -04:00
Ryan Cramer
cfed957df2 Update $pages->newPage() method to support option of accepting array of fields/properties to set when creating the page object 2018-04-27 11:21:13 -04:00
Ryan Cramer
b0abd3b9de Additional update for processwire/processwire-issues#575 2018-04-27 10:28:13 -04:00
Ryan Cramer
43c3bb52c3 Additional adjustment for processwire/processwire-requests#183 to correct enter-key detection that was using wrong element id. 2018-04-27 10:13:50 -04:00
Ryan Cramer
904db74f1a Add custom __debugInfo() methods to Pagefile, Pageimage, and Pagefiles per processwire/processwire-issues#575 plus some other minor updates to Pagefile/Pageimage 2018-04-27 09:55:31 -04:00
Ryan Cramer
c2324f0a8a Fix issue processwire/processwire-issues#576 adjustment in InputfieldPassword.js for EmailNewUser module 2018-04-27 06:09:07 -04:00
Ryan Cramer
2a49fe1087 Attempt to fix issue processwire/processwire-issues#560 add support for viewable permission with User pages outside admin structure when developer intends it 2018-04-26 10:04:49 -04:00
Ryan Cramer
105ba2b8eb Add __debugInfo() method to WireInput class per processwire/processwire-issues#575 2018-04-26 08:48:09 -04:00
Ryan Cramer
e53e4b4bd0 Add processwire/processwire-requests#183 focus URL input field when ProcessPageEditLink opens 2018-04-26 08:32:42 -04:00
Ryan Cramer
2cc69a1f0b Fix issue processwire/processwire-issues#571 where configuring a FieldtypeFieldsetClose in Setup > Fields would result in error in AdminThemeUikit 2018-04-25 11:16:05 -04:00
Ryan Cramer
c7dfb37a7e Fix issue processwire/processwire-issues#570 where InputfieldImage in a Repeater item, crop/focus/variations buttons didn't show to non-superusers 2018-04-25 11:09:59 -04:00
Ryan Cramer
d691b743b0 Fix issue processwire/processwire-issues#569 when Inputfield::textFormatMarkdown constant used it wasn't replacing default wrapping markup 2018-04-25 09:07:18 -04:00
Ryan Cramer
ba400767ab Fix issue processwire/processwire-issues#568 for ProcessModule table.AdminDataList tables, plus convert CSS to SCSS 2018-04-25 08:21:47 -04:00
Ryan Cramer
ace36fb415 Fix issue processwire/processwire-issues#567 w/AdminThemeUikit search box ctrl/cmd-click did not open new tab 2018-04-25 06:12:50 -04:00
Ryan Cramer
a9051a252f Fix issue processwire/processwire-issues#565 with InputfieldSelector preview count and _custom selector option was not working before 2018-04-25 05:55:22 -04:00
Ryan Cramer
f2912bcfd4 A couple of minor adjustments in AdminThemeUikit 2018-04-23 05:42:12 -04:00
Ryan Cramer
184059b5d6 Additional improvements to $config->noHTTPS option for support of hostnames 2018-04-20 10:33:43 -04:00
Ryan Cramer
bc037f840e Minor adjustment to InputfieldRepeater and publish status to simplify a ProDrafts hook 2018-04-19 10:52:08 -04:00
Ryan Cramer
16c20439f1 Add support for $config->noHTTPS. When boolean true, it cancels any HTTPS requirements set per-template, simplifying cases where you copy a production site to dev site that may not have the same HTTPS capabilities. So for that case, the dev site would want to have $config->noHTTPS=true; in /site/config.php 2018-04-19 10:49:58 -04:00
Ryan Cramer
a6bd85f6b2 Some additional updates for AdminThemeUikit to improve on irregular Inputfield column widths and global uk-form-large or uk-form-small settings 2018-04-19 10:47:13 -04:00
Ryan Cramer
19f557b487 Several updates to AdminThemeUikit including additional config settings, new input appearance adjustments, option to use percentage-based widths rather than uk-width classes, and more. processwire/processwire-issues#564 processwire/processwire-issues#480 2018-04-18 09:05:29 -04:00
Ryan Cramer
c550c6678b Some minor adjustments in preparation for a couple AdminThemeUikit additions 2018-04-18 08:50:53 -04:00
Ryan Cramer
d2ebb4cd56 Update base AdminTheme class for more flexible class management functions 2018-04-18 08:47:20 -04:00
Ryan Cramer
566d60b152 Small optimization for for __('text') translation function when used in PHP 5.4.0 or newer and no textdomain specified. 2018-04-13 09:17:09 -04:00
Ryan Cramer
5884f05b9f Fix issue in MarkupFieldtype when rendering file/image fields within repeater items 2018-04-13 08:50:02 -04:00
Ryan Cramer
6b3b0b7782 Further updates to AdminThemeUikit Inputfield width adjustments per processwire/processwire-issues#480 2018-04-13 08:31:17 -04:00
Ryan Cramer
ae5b955ec8 Fix issue in FieldtypeFieldsetOpen.module where hooks might not get called depending on module load order 2018-04-08 08:17:09 -04:00
Ryan Cramer
341342dc5b Bump version to 3.0.98 2018-04-06 14:25:49 -04:00
Ryan Cramer
9d11d87e9b Update InputfieldSelect::addOptionsString() to allow for user-defined disabled option if line in option string is prefixed with "disabled:" 2018-04-06 07:14:48 -04:00
Ryan Cramer
c5147a5279 Make Inputfield requiredLabel customizable per field from API via $inputfield->requiredLabel property, to provide option to override default "missing requird value" label. 2018-04-06 06:56:54 -04:00
Ryan Cramer
f27feb2bbf Followup fix for issue processwire/processwire-issues#322 check for duplicate non-default language page name when creating new page 2018-04-06 06:46:42 -04:00
Ryan Cramer
eaa2cb4d89 Fix issue processwire/processwire-issues#558 remove unused filemtime() call in ProcessLanguage.module 2018-04-06 05:46:30 -04:00
Ryan Cramer
215348b797 Fix issue processwire/processwire-issues#556 fix issue when cloning Pagefile that has name ending with "-0". 2018-04-06 05:43:25 -04:00
Ryan Cramer
6dc7a122a6 Fix issue processwire/proceswire-issues#555 in ProcessProfile where 50% width Inputfield followed by submit button resulted in submit button in wrong position 2018-04-06 05:37:22 -04:00
Ryan Cramer
c5c7ae4232 Bump to 3.0.97 and update to add a Pagefile::HTTPURL to accompany the existing Pagefile::URL property (uppercase implies cache busting URLs). 2018-03-30 10:48:40 -04:00
Ryan Cramer
2722345aac Attempt to fix issue processwire/processwire-issues#554 isue with $files->include() and Windows paths 2018-03-30 10:45:35 -04:00
Ryan Cramer
b1214fa0c4 Fix issue processwire/processwire-issues#553 where a note in AdminThemeUikit config screen needed to be updated for new module location 2018-03-30 09:53:46 -04:00
Ryan Cramer
8cbe05d1e7 Fix issue processwire/processwire-issues#277 where Inputfield::collapsedNever constant didn't work quite right in some cases 2018-03-30 09:41:32 -04:00
Ryan Cramer
deb448851b Fix issue processwire/processwire-issues#544 where FieldsetTab in User editor was not viewable to non-superuser with user-admin permission 2018-03-28 09:43:44 -04:00
Ryan Cramer
b7e90f896c Fix issue processwire/processwire-issues#551 Pages::trashed and Pages::restored hooks were getting called twice 2018-03-28 06:31:36 -04:00
Ryan Cramer
164d132be8 Fix issue processwire/processwire-issues#548 WireLog::getLines method wasn't passing options array to FileLog::find 2018-03-28 05:59:40 -04:00
Ryan Cramer
bd85c025e2 Fix issue in ProcessCommentsManager where the tabs were not showing active state properly in AdminThemeUikit. Plus some minor CSS updates in InputfieldPage and ProcessPageLister. 2018-03-22 07:59:16 -04:00
Ryan Cramer
a5e5ae7b47 Update FieldtypePage to support a runtime option to create Page references that don't exist when value is set by title. This is to accommodate a feature in ImportPagesCSV module. 2018-03-22 07:48:26 -04:00
Ryan Cramer
3e6840748b Update FieldtypeInteger so that it will still convert to integer even if number prefixed with USD or EUR symbol. 2018-03-22 07:46:02 -04:00
Ryan Cramer
89ed62a6d8 Prevent stars input from showing in InputfieldCommentsAdmin.module when stars feature is not enabled 2018-03-22 07:43:47 -04:00
Ryan Cramer
941ef5cb67 Update to 2018 2018-03-16 10:44:38 -04:00
Ryan Cramer
48fe0769a4 Remove console.log from ProcessProfile.js 2018-03-16 10:35:56 -04:00
Ryan Cramer
4551748659 Bump version to 3.0.96 2018-03-16 10:30:08 -04:00
Ryan Cramer
f52c3f54c9 Fix issue processwire/processwire-issues#322 where certain scenario could allow collision of language-specific page names 2018-03-16 10:24:46 -04:00
Ryan Cramer
6c4f4103d2 Fix issue processwire/processwire-issues#542 in InputfieldRepeater.module repeater item titles, where numbered HTML entities (like the one for apostrophe) were getting their "#" character removed, making it look like double encoding 2018-03-16 07:40:36 -04:00
Ryan Cramer
19a55c6d37 Enhancement for issue processwire/processwire-issues#539 show "Edit Profile" button after login when user has profile-edit permission but not page-edit permission 2018-03-15 11:02:37 -04:00
Ryan Cramer
ba21b28b4e Attempt fix for issue processwire/processwire-issues#537 update code that corrects Firefox issue where it populates autocomplete password field even when autocomplete is disabled (Firefox ignores disabled autocomplete here for some reason) 2018-03-15 09:39:03 -04:00
Ryan Cramer
bd72c59e41 Fix issue processwire/processwire-issues#535 missing null check for return value from getInputfield() in ProcessField 2018-03-15 08:55:27 -04:00
Ryan Cramer
eb95498183 Fix issue processwire/processwire-issues#534 PageList narrow mode didn't show 'New' action for home, or 'Empty' action for trash 2018-03-15 08:48:21 -04:00
Ryan Cramer
d9b30167c7 Typo fixes per processwire/processwire-issues#533 2018-03-14 11:50:50 -04:00
Ryan Cramer
bbfb4d78d2 Update for additional suggestions in processwire/processwire-issues#518 2018-03-14 10:37:24 -04:00
Ryan Cramer
27b6141a1b Fix issue processwire/processwire-issues#531 repeater within a fieldset depth changes unexpectedly when repeater item dragged up/down. 2018-03-14 10:02:15 -04:00
Ryan Cramer
1045acc057 Make PageTable buttons have better margins per processwire/processwire-issues#521 2018-03-14 08:43:56 -04:00
Ryan Cramer
11398f00bf Update for AdminThemeUikit main.js per processwire/processwire-issues#480 2018-03-14 08:13:46 -04:00
Ryan Cramer
c1ca1fde3b Fix minor JS issue where ajax-loaded inputfields didn't have the toggle arrow icon properly adjusting for open/closed state 2018-03-13 09:40:04 -04:00
Ryan Cramer
0a98458d5b Fix issue where ajax-loaded Inputfield render() didn't have before(render) hooks called when rendering the non-ajax placeholder, which was identified in processwire/processwire-issues#480 2018-03-13 09:38:32 -04:00
Ryan Cramer
3a76db94f9 Some minor optimizations in WireHooks class, plus enable before/after option for $type argument in runHooks method that was mentioned in the phpdoc but wasn't fully supported yet 2018-03-13 09:37:05 -04:00
Ryan Cramer
d82d011eca Change ksort to sort in minColumnWidth for AdminThemeUikit 2018-03-12 13:33:56 -04:00
Ryan Cramer
d99c5ec731 Fix processwire/processwire-issues#526 where part of longclick for LanguageTabs was still active (was converted to double click awhile back) 2018-03-12 10:37:29 -04:00
Ryan Cramer
ceff2e47a8 Fix issue processwire/processwire-issues#523 prevent user from uninstalling admin themes defined by $config->defaultAdminTheme 2018-03-12 10:16:11 -04:00
Ryan Cramer
afe41773d8 Fix issue processwire/processwire-issues#519 where front-edit editor w/CKEditor inline image action was not working 2018-03-12 09:45:03 -04:00
Ryan Cramer
20f02cddf9 Some AdminThemeUikit updates, mostly related to processwire/processwire-issues#480 2018-03-12 09:02:00 -04:00
Ryan Cramer
0c760171bd Bump version to 3.0.95 2018-03-09 12:26:29 -05:00
Ryan Cramer
36bceb6ed2 Fix issue in Selectize.js processwire/processwire-issues#520 where Selectize.js had bottleneck in measureString() function that caused a slowdown because of DOM manipulation (was noticed when Selectize paired with Uikit 3) 2018-03-09 09:17:04 -05:00
Ryan Cramer
5da4e17a27 Update for processwire/processwire-issues#517 and ryancramerdesign/AdminThemeUikit#46 correct issue where InputfieldImage defined buttonClass wasn't getting used in repeaters, plus update the Uikit custom button class to not be applied when InputfieldImage::renderButtons() is hooked, in order to avoid two different button styes appearing when other modules add buttons to it. 2018-03-08 10:56:32 -05:00
Ryan Cramer
bd3be5e315 Fix issue processwire/processwire-issues#518 where multi-instance + multi-site had boot error in Windows 2018-03-08 08:58:15 -05:00
Ryan Cramer
786a4b8309 Update InputfieldAsmSelect to use font-awesome based edit icon rather than a jQuery UI one 2018-03-07 13:07:38 -05:00
Ryan Cramer
3f62968392 Resolve various minor AdminThemeUikit issues and update Uikit 3 version to beta 40 2018-03-07 13:06:08 -05:00
Ryan Cramer
787c589151 Fix issue processwire/processwire-issues#516 where the WireDateTime conversion table from date to strftime incorrectly had '$' rather than '%' for some of the conversions. 2018-03-07 08:26:43 -05:00
Ryan Cramer
b1454b31a4 Add ProcessField detection for invalid Page field showIf/requiredIf dependencies per processwire/processwire-issues#509 2018-03-07 08:11:42 -05:00
Ryan Cramer
a166c22e71 Update for processwire/processwire-issues#480 2018-03-07 07:22:44 -05:00
Ryan Cramer
16f614f5a0 Fix issue processwire/processwire-issues#513 datepicker z-index when near image field 2018-03-06 11:04:15 -05:00
Ryan Cramer
0aefc2baa3 Fix issue processwire/processwire-issues#512 correct unnecessary error message when performing file field schema update 2018-03-06 10:53:54 -05:00
Ryan Cramer
aada758d14 Update AdminThemeUikit JS for Inputfield widths show/hide calculations per processwire/processwire-issues#480 2018-03-06 09:01:56 -05:00
Ryan Cramer
e20562b5c1 Add @horst-n improvements to ImageSizerEngine and InputfieldImage focus+zoom features, plus add some other adjustments and improvements in InputfieldImage.js 2018-03-05 08:45:39 -05:00
Ryan Cramer
a9ec5a640a Bump version to 3.0.94 2018-03-02 12:01:16 -05:00
Ryan Cramer
76f3dac6fd Update for processwire/processwire-issues#408 plus some phpdoc updates in ProcessModule to appease phpstorm 2018-03-01 09:30:45 -05:00
Ryan Cramer
cd0f76d32c Fix issue processwire/processwire-issues#505 add support for detection of missing closing tags in WireMarkupRegions in order to prevent timeout 2018-02-28 08:40:58 -05:00
Ryan Cramer
8acd06646f Fix issue processwire/processwire-issues#349 InputfieldOptions with InputfieldSelect required field not having blank option 2018-02-27 10:36:05 -05:00
Ryan Cramer
5674af8b99 Minor count() update per processwire/processwire-issues#408 plus some minor phpdoc updates to appease phpstorm 2018-02-27 08:33:17 -05:00
Ryan Cramer
7c0012fbd9 Additional wireCount updates per processwire/processwire-issues#408 2018-02-26 10:16:12 -05:00
Ryan Cramer
ac779c79a8 Fix issue processwire/processwire-issues#302 where markup <region> tags didn't get removed if template file produced no output 2018-02-26 09:58:29 -05:00
Ryan Cramer
9d2a986048 Add @Toutouwai request related to processwire/processwire-issues#84 for improving touch input detection in ProcessLogin via method like InputDetect module with whatinput JS. 2018-02-26 09:19:28 -05:00
Ryan Cramer
37f95199f4 Add descriptive error message when trying to use OR values with path/URL and core PathPaths module is not installed, per processwire/processwire-issues#504 2018-02-26 06:27:29 -05:00
Ryan Cramer
869040bb30 Attempt to fix issue processwire/processwire-issues#503 incorrect label index warning/notice in ProcessRecentPages 2018-02-26 06:00:52 -05:00
Ryan Cramer
bd90821d40 Minor adjustment to account for possibility that CRLF may get counted as two bytes in InputfieldTextarea::getTextLength method (for maxlength detection). 2018-02-25 07:40:03 -05:00
Ryan Cramer
f3c188254d Bump version to 3.0.93 2018-02-23 11:51:54 -05:00
Ryan Cramer
72e00189b5 Some adjustments to InputfieldCKEditor for future support of external asset pages. Not currently enabled, just getting some foundational stuff in place for future use. 2018-02-23 10:56:32 -05:00
Ryan Cramer
dd9f215247 Update LanguageSupport module config screen to recommend next steps during module installation 2018-02-23 10:36:38 -05:00
Ryan Cramer
919c475631 Various minor adjustments 2018-02-23 10:36:15 -05:00
Ryan Cramer
053ef62970 Fix issue processwire/processwire-issues#500 where trackChanges state of cloned page was off 2018-02-21 06:12:39 -05:00
Ryan Cramer
b2281d6e93 Optimize WireMail module detection, add support for replyTo() method, add $config->wireMail default settings for WireMail module(s). processwire/processwire-issues#498 2018-02-20 10:12:57 -05:00
Ryan Cramer
fc1c6e5ea1 Fix issue processwire/processwire-issues#499 as well as expand upon phpdoc in ProcessPageEditImageSelect.module 2018-02-20 06:37:35 -05:00
Ryan Cramer
8fe1eb13f4 Add support for interactive selection of zoom, combined with focus, in InputfieldImage. To enable zoom, go to your image field settings, and on the Input tab locate the "Focus point selection" field and choose "Focus point and zoom". Bump version to 3.0.92. 2018-02-16 11:28:25 -05:00
Ryan Cramer
5c6e54e24d Fix issue processwire/processwire-issues#495 update word counter to display max length even when field is blank 2018-02-14 09:49:40 -05:00
Ryan Cramer
5493d5fea6 Add @horst-n support for focus zoom setting in the ImageSizerEngines 2018-02-13 06:02:30 -05:00
Ryan Cramer
e73ec872da Fix issue processwire/processwire-issues#491 add support for automatically cloning fieldset _END field when fieldset is cloned 2018-02-12 09:20:38 -05:00
Ryan Cramer
ef6a9d56e4 Fix issue processwire/processwire-issues#492 where FieldtypeTextareaLanguage fields on multi-language install with only default language present could get caught in a loop during load of field 2018-02-12 09:12:43 -05:00
Ryan Cramer
1f6ae656dc Bump version to 3.0.91 2018-02-09 10:10:09 -05:00
Ryan Cramer
0630b3a3c3 Add support for PageFinder selectors where field name is the name of a Fieldtype module. During search, it translates to all fields having that Fieldtype. This enables you to perform a search on all text/textarea fields at once, for example, "FieldtypeTextarea%=something". You can also do "FieldtypeText.extends%=something", and that will include all fields that use or extend the given Fieldtype. Or you can do "FieldtypeText.fields%=something" and the resulting PageArray will contain a "fields" data property that contains an array of matched fields and the quantity of pages that matched each. You can also do "FieldtypeText.extends.fields%=something" and it will include both behaviors mentioned here. Finally, you can use OR expressions here too, like "FieldtypeText|FieldtypeTextarea%=something". Note that these searches are not particularly fast and may have limits on scale. The "FieldtypeName.fields" option is slower than without the "fields" option, BUT it enables it to scale further. In my testing so far, the %= operator actually performs quite a bit better here for text-based searches than the *= or ~= operators, likely because the resulting query probably really limits use of the fulltext indexes. 2018-02-09 09:58:49 -05:00
Ryan Cramer
bafe3d4a12 Fix issue processwire/processwire-issues#490 where ProcessPageEditLink didn't allow for external link rel attributes with space separated values 2018-02-08 09:22:05 -05:00
Ryan Cramer
6dbb244654 Fix issue processwire/processwire-issues#488 where UTF-8 mode urlSegments that contained only ascii would get converted to lowercase 2018-02-08 08:51:33 -05:00
Ryan Cramer
3ade2c3269 Fix issue processwire/processwire-issues#489 where file/image field with limit=1, multi-language description was not retained on when file was replaced 2018-02-08 08:07:50 -05:00
Ryan Cramer
cf292363ca Enhance wireBytesStr() function with more options per processwire/processwire-requests#133 2018-02-06 10:03:13 -05:00
Ryan Cramer
3918bba0ab Upgrade htmlpurifier version to 4.9.3 per processwire/processwire-issues#483 2018-02-06 05:44:43 -05:00
Ryan Cramer
eb80f52efe Fix issue processwire/processwire-issues#487 where svg files in $config->pageFileSecure mode needed entry in $config->fileContentTypes 2018-02-06 05:39:19 -05:00
Ryan Cramer
5c708797f9 Update for processwire/processwire-issues#408 PHP 7.2 and changed behavior of count() 2018-02-05 06:25:05 -05:00
Ryan Cramer
f89544ea04 Fix issue processwire/processwire-issues#485 where attempting to use numeric index with WireData could halt request/memory error 2018-02-05 06:09:54 -05:00
Ryan Cramer
e21c965f7b Fix issue processwire/processwire-issues#484 where PageFrontEdit with <edit field="pageID.fieldName"> tag was not working property. 2018-02-05 05:57:15 -05:00
Ryan Cramer
8e52ee8097 Bump version to 3.0.90 2018-02-02 15:00:29 -05:00
Ryan Cramer
461152a8cd This update to AdminThemeUikit fixes various minor reported issues with Inputfields and some other small details 2018-02-02 10:36:11 -05:00
Ryan Cramer
cb2683208f Various minor adjustments 2018-02-02 10:16:15 -05:00
Ryan Cramer
3eba1b58aa Fix issues processwire/processwire-issues#173 and processwire/processwire-issues#393 nested repeater labels 2018-02-02 10:10:49 -05:00
Ryan Cramer
1d82b530ff Fix issue processwire/processwire-issues#347 where field export that included roles was using IDs rather than role names. Also same issue with PageTable that was using template IDs rather than template names. 2018-02-02 10:05:40 -05:00
Ryan Cramer
f7ff7eab8c Fix issue processwire/processwire-issues#352 where deleted role could interfere with the "who can access this page" field in the page editor 2018-02-01 11:39:30 -05:00
Ryan Cramer
bbca8f5669 Add new module ImageSizerEngineAnimatedGif by @horst-n for animated GIF support in image resizes 2018-01-31 09:13:21 -05:00
Ryan Cramer
03660974ee Some additional helpful logic and messages added in ProcessRole, plus an update to resolve processwire/processwire-issues#474 2018-01-31 08:10:32 -05:00
Ryan Cramer
e917c93d76 Fix issue processwire/processwire-issues where clone of page having FieldsetPage field was not cloning the FieldsetPage portion of it 2018-01-29 09:16:03 -05:00
Ryan Cramer
90efe9b14a Bump version to 3.0.89 and some other adjustments 2018-01-26 11:43:22 -05:00
Ryan Cramer
2cebe89e57 Fix issue processwire/processwire-issues#475 adjustment to argument type hint in ProcessUser 2018-01-25 09:50:11 -05:00
Ryan Cramer
02f1eadacc Fix issue processwire/processwire-issues#474 where ProcessRole wasn't working correctly with page-view permission assignment 2018-01-25 09:43:16 -05:00
Ryan Cramer
2a946c09de Fix issue processwire/processwire-issues#473 where SelectableOptionManager.php:addOptions() method had incorrect "array" type hint in method argument 2018-01-25 06:26:52 -05:00
Ryan Cramer
b72e609cc4 Fix issue processwire/processwire-issues#468 where ProcessPageSearch wasn't working with ^= (starts with) search operator 2018-01-25 06:22:51 -05:00
Ryan Cramer
a021d3b54f Fix issue processwire/processwire-issues#467 correct typo in PaginatedArray sprintf string 2018-01-25 06:15:23 -05:00
Ryan Cramer
7b1913b6ed Add support for image focus area / focus point / focal point (or whatever the best term is) to InputfieldImage per processwire/processwire-requests#150 2018-01-24 11:48:50 -05:00
Ryan Cramer
3fb3294f5a Add support for filedata in FieldtypeFile/FieldtypeImage, which provides a way to store other general purpose data with files/images. 2018-01-24 11:31:03 -05:00
Ryan Cramer
6241fd6730 Fix issue with InputfieldSelector not recognizing template_ids property on Page reference fields that use autocomplete 2018-01-14 11:55:54 -05:00
Ryan Cramer
b3ea132d81 Fix ProcessTemplate and asmSelect issue processwire/processwire-issues#472 2018-01-11 09:51:47 -05:00
Ryan Cramer
ad6f6bd283 Fix JqueryUI modal.js issue processwire/processwire-issues#471 2018-01-11 09:39:06 -05:00
Ryan Cramer
f7b49055cd Fix PageFrontEdit issue processwire/processwire-issues#470 2018-01-11 09:36:46 -05:00
Ryan Cramer
f3749d241a Change an sanitizer entities() call to entities1() since it's getting double encoded for some reason in @adrianbj case 2018-01-05 12:37:40 -05:00
Ryan Cramer
4f7981cf75 Bump version to 3.0.88 2018-01-05 12:05:35 -05:00
Ryan Cramer
de5c241b6a Fix issue and PR #54 with similar solution though also remove predefined min/max image settings from ProcessPageEditImageSelect because they seem to be causing various issues, and the predefined settings are more about uploaded files. 2018-01-05 10:56:05 -05:00
Ryan Cramer
fb39ded94d Add support for image file actions to InputfieldImage and update ImageSizer engines to support new rotate, flip and color actions 2018-01-05 10:54:21 -05:00
Ryan Cramer
9bdad6fc86 Detect NullPage in InputfieldCKEditor::getImagesFieldName() to fix issue that appeared for someone when editing page with no images fields. 2017-12-31 11:02:42 -05:00
Ryan Cramer
a31d4a84e2 Upgrade CKEditor to 4.8.0, add support for direct image upload/paste in CKEditor. Bump version to 3.0.87. 2017-12-29 08:42:49 -05:00
Ryan Cramer
5bba89b043 Fix issue processwire/processwire-issues#462 solution for files using arabic filenames 2017-12-26 07:27:50 -05:00
Ryan Cramer
f573473066 Add support for custom configuration of what Fieldtype/Inputfield settings may be overridden for field/template context. Appears only in $config->advanced mode. You can see it when editing a field (ProcessField) on the "Overrides" tab. Related to processwire/processwire-requests#145 2017-12-22 11:11:02 -05:00
Ryan Cramer
63229872a0 Fix typo in phpdoc processwire/processwire-issues#458 2017-12-22 07:47:44 -05:00
Ryan Cramer
5b53a41c12 Fix issue processwire/processwire-issues#456 2017-12-21 09:36:17 -05:00
Ryan Cramer
87dc586c8c Add PageFinder selector support for field.owner.subfield=value where "field" is a PageTable, Page or Repeater field, and "owner" is a literal reserved word, and "subfield" is any field or field.subfield combination. It makes the selector match Repeater, PageTable or Page reference pages that have an "owner" that matches. This "owner" would be defined as the page that has a Repeater or PageTable item being matched, as one of its fields. Ping @apeisa 2017-12-20 12:21:14 -05:00
Ryan Cramer
362c0a0af5 Bump version to 3.0.86 2017-12-15 15:28:40 -05:00
Ryan Cramer
aa1f7dbd47 Fix issue processwire/processwire-issues#455 2017-12-15 09:19:18 -05:00
Ryan Cramer
ded6476832 Fix issue processwire/processwire-issues#454 for Pageimage::maxSize() 2017-12-15 06:30:36 -05:00
Ryan Cramer
77ad382bcd Attempt fix for issue processwire/processwire-issues#452 2017-12-15 06:05:52 -05:00
Ryan Cramer
04b7dd0404 Fix autoload_noget change tracking issue in Page per @apeisa 2017-12-14 06:38:08 -05:00
Ryan Cramer
9b10232b73 Add ability to specify roles that aren't allowed to login, related to processwire/processwire-requests#140 plus while I was in there, did some re-working of login related code in Session class and user management code in ProcessUser.module. 2017-12-13 10:37:39 -05:00
Ryan Cramer
bac60dc340 Add support for interlaced jpeg images per @horst-n and processwire/processwire-requests#134 2017-12-11 11:09:57 -05:00
Ryan Cramer
67cdf1d456 Add variation of @adrianbj PR#92 which adds Imagick as another method or determining SVG dimensions 2017-12-11 10:49:59 -05:00
Ryan Cramer
b4b40f19f4 Fix issue processwire/processwire-issues#449 2017-12-11 05:50:48 -05:00
Ryan Cramer
763c713f4c wireCount updates per processwire/processwire-issues#408 2017-12-11 05:42:48 -05:00
Ryan Cramer
927f66cc61 Bump version to 3.0.85 2017-12-08 11:30:53 -05:00
Ryan Cramer
7a3da093d9 Update InputfieldCheckbox to support a separately configured checkbox label, distinct from the field label (for the cases where you might want both). 2017-12-08 10:20:20 -05:00
Ryan Cramer
93a5cc20b1 Update random string generation method in Password class (randomBase64String) to support new PHP7 random_bytes function, plus add a test mode for testing all generation techniques at once. processwire/processwire-issues#447 2017-12-08 09:36:25 -05:00
Ryan Cramer
dfb5f740d0 Add usage example to FieldtypeOptions config screen 2017-12-08 09:32:46 -05:00
Ryan Cramer
1ba1323e81 count() to wireCount() update per processwire/processwire-issues#408 2017-12-08 07:55:34 -05:00
Ryan Cramer
76e956ad61 Additional updates for autojoin related issue, delay autojoin field sanitization until first access of field 2017-12-06 06:33:28 -05:00
Ryan Cramer
00a8bf03d3 Fix isSaveable() unnecessary parent load + findMany() issue per @apeisa 2017-12-06 05:58:08 -05:00
Ryan Cramer
45eadd971b Fix Page field autojoin issue per @apeisa 2017-12-06 05:45:07 -05:00
Ryan Cramer
fb21af434d Fix issue processwire/processwire-issues#443 2017-12-05 12:15:23 -05:00
Ryan Cramer
5b8761454a Fix issue processwire/processwire-issues#439 2017-12-05 10:51:39 -05:00
Ryan Cramer
3b21a3e9a8 Add @teppokoivula suggested fix for processwire/processwire-issues#435 PageFrontEnd when editing 404 page 2017-12-04 11:17:16 -05:00
Ryan Cramer
5bbd342570 Fix issue processwire/processwire-issues#433 2017-12-04 10:57:04 -05:00
Ryan Cramer
06c1a46ba5 Fix issue processwire/processwire-issues#428 2017-12-04 10:28:51 -05:00
Ryan Cramer
a865e0a053 Some cleanup in ProcessPageAdd plus add support for disabling template suggestions when adding pages per processwire/processwire-issues#424 via $config->pageAdd('noSuggestTemplates', true); or specify space-separated list of template names (string) for the "true" value. 2017-12-04 09:51:11 -05:00
Ryan Cramer
337e59663b Add support for group notifications in AdminThemeFramework (AdminThemeUikit), along with a config option in the AdminThemeUikit module. Also fix an issue in ProcessProfile that was interfering with ajax requests. 2017-11-20 09:45:16 -05:00
Ryan Cramer
32b3da7b0d Fix issue where collapsed repeater in fieldset (where repeater is only field in fieldset), when clicking to un-collapse repeater, it would jump to top of page 2017-11-19 06:33:22 -05:00
Ryan Cramer
81a07fde34 Attempt to fix issue processwire/processwire-issues#432 for disabling autocomplete and password field 2017-11-18 07:00:48 -05:00
Ryan Cramer
22c69ec599 Add latest version of AdminThemeUikit and bump version to 3.0.84 2017-11-17 10:02:49 -05:00
Ryan Cramer
2c4c9de61d Fixes to ryancramerdesign/AdminThemeUikit#55 and ryancramerdesign/AdminThemeUikit#61 (when combined with latest AdminThemeUikit version, to be added shortly) 2017-11-17 09:44:45 -05:00
Ryan Cramer
60572a4cd2 Fix issue with PageTable not having correct table styles when using AdminThemeUikit 2017-11-17 08:29:22 -05:00
Ryan Cramer
7d16590f07 Update ProcessProfile to support the ability to edit user name (if configured to do so). Also update it so that it requires you to enter your password before you can commit changes to email or user name. 2017-11-16 10:54:41 -05:00
Ryan Cramer
6c3eb6a460 Update ProcessTemplate so that it lets you specify when fields user can edit in their profile, when editing the "user" template. Meaning, it's no longer necessary to jump back and forth between ProcessProfile and the ProcessProfile module config. 2017-11-16 10:53:16 -05:00
Ryan Cramer
ac59da3427 Upate JqueryWireTabs module to support more customization of its tooltips (so admin theme can adjust) 2017-11-16 10:52:13 -05:00
Ryan Cramer
ebc150a3a7 Update PW installer to use Uikit 3 theme, plus add support for detecting and dropping existing tables (if present) during installation. 2017-11-16 10:50:36 -05:00
Ryan Cramer
58eb0c3159 Various minor adjustments 2017-11-16 10:49:26 -05:00
Ryan Cramer
221a15a653 Add AdminThemeUikit module to core and bump version to 3.0.83 2017-11-10 11:10:40 -05:00
Ryan Cramer
e0d04a4626 Upgrade InputfieldPassword to support requirement of entering your old password before it'll let you set a new one. 2017-11-10 09:13:10 -05:00
Ryan Cramer
840ab68625 Minor cosmetic adjustments 2017-11-10 09:11:00 -05:00
Ryan Cramer
e2a42381ac Some optimizations to LanguageTabs and add support for Uikit3 beta34+ 2017-11-10 09:08:30 -05:00
Ryan Cramer
7398267be1 Bump version to 3.0.82 2017-11-03 10:51:44 -04:00
Ryan Cramer
8e27f859c0 Fix issue ryancramerdesign/AdminThemeUikit#47 where AdminThemeFramework was not running the headline through the language translation function 2017-11-03 08:10:47 -04:00
Ryan Cramer
297a64f9d4 Fix issue processwire/processwire-issues#421 fixing useless redirects from manually deleted session cookies 2017-11-02 09:50:26 -04:00
Ryan Cramer
ece560daa1 Fix issue processwire/processwire-issues#418 to fix file/image drag-to-upload in IE11 using fix provided by @Toutouwai 2017-11-02 09:37:23 -04:00
Ryan Cramer
34d15dadae Fix issue processwire/processwire-issues#420 to disable autocomplete via custom attribute for InputfieldPageName using suggestion by @Toutouwai 2017-11-02 09:29:40 -04:00
Ryan Cramer
7b19df0175 Add Setup>Fields>Export/Import support for Repeater fields. processwire/processwire-issues#416 2017-11-02 06:33:46 -04:00
Ryan Cramer
ab6d158cf9 Fix issue processwire/processwire-issues#419 which corrects InputfieldImage.js sizing of list mode using fix provided by @Toutouwai 2017-11-02 06:22:50 -04:00
Ryan Cramer
7bf27cc5e5 Fix issue processwire/processwire-issues#415 where ProcessPageEditImageSelect was not showing variations for image when it was specified as one of the skipFields in module configuration. 2017-10-31 06:25:24 -04:00
Ryan Cramer
5c09f03e8b Fix issue processwire/processwire-issues#414 language tabs and language used for placeholder attribute, though not positive this one should stay...previous behavior may be preferable depending on the case. 2017-10-30 06:22:29 -04:00
Ryan Cramer
c778056991 Fix issue processwire/processwire-issues#410 by making ProcessLanguageTranslator give preference to GET var (rather than session) with language ID, by adding part of PR #93 2017-10-30 05:48:31 -04:00
Ryan Cramer
0f4cf01ec2 Update Config.php note for imageSizerOptions per processwire/processwire-issues#409 2017-10-30 05:43:20 -04:00
Ryan Cramer
066a54fb6f Fix issue processwire/processwire-issues#411 where multiple PageListSelect in same page editor weren't working properly (thanks to @Toutouwai for the fix) 2017-10-30 05:36:26 -04:00
Ryan Cramer
c90fc3f872 Add improved Roles editor that now lets you manage permissions by template 2017-10-27 11:21:13 -04:00
Ryan Cramer
e3fc776c53 Bump version to 3.0.80 2017-10-20 09:58:29 -04:00
Ryan Cramer
b2381002be Improve error reporting ability of $modules->get(), $modules->getModule(), primarily for debugging purposes. 2017-10-20 09:45:53 -04:00
Ryan Cramer
97935d156a Add wireCount() function to duplicate behavior of count() function in PHP versions prior to 7.2 2017-10-20 09:44:21 -04:00
Ryan Cramer
56e35b55ea Add support for using repeaters in user profile editor per issue processwire/processwire-issues#407 2017-10-20 09:43:08 -04:00
Ryan Cramer
b4f977c36a Fix issue processwire/processwire-issues#396 plus update ProcessCommentsManager to be compatible with AdminThemeUikit 2017-10-19 11:15:08 -04:00
Ryan Cramer
a48f4038c8 Enhancement processwire/processwire-issues#405 add support for hooks to be used in classes that extend Wire-derived classes that aren't in the ProcessWire namespace. 2017-10-17 10:00:55 -04:00
Ryan Cramer
b0f37a306e Fix issue processwire/processwire-issues#406 correction of error message in ProcessPageSort 2017-10-17 05:44:30 -04:00
Ryan Cramer
fff501cac3 Correct an issue in Repeater that interfered with some file-upload situations in draft page versions 2017-10-17 05:33:32 -04:00
Ryan Cramer
2771c353ee Fix issue processwire/processwire-issues#403 which caused problems with file uploads in some kinds of repeaters. Also bump version to 3.0.79 2017-10-13 12:59:33 -04:00
Ryan Cramer
00c183e70f Some code adjustments in ProcessPageList to support a separate renderReady() method, as well as some tweaks per @EntitySelf evernote document 2017-10-13 08:17:32 -04:00
Ryan Cramer
5dc690af9e Fix issue processwire/processwire-issues#381 where Selectors class could get confused by a quoted value with comma when contained in an OR value 2017-10-12 11:57:26 -04:00
Ryan Cramer
58bde80a81 Fix issue processwire/processwire-issues#384 update 2016 copyright date to 2017 2017-10-12 11:15:37 -04:00
Ryan Cramer
47b87d283b Per PR #91 fix issue processwire/processwire-issues#385 where ProcessPageEditImageSelect nosize option wasn't working with InputfieldCKEditor pwimage plugin 2017-10-12 11:12:42 -04:00
Ryan Cramer
f4971ae5d4 Fix issue processwire/processwire-issues#388 where repeater templates could show as selectable options in page editor template field, when they didn't need to 2017-10-12 10:52:47 -04:00
Ryan Cramer
e0a2abea07 Fix issue processwire/processwire-issues#391 remove stray console.log() in InputfieldRepeater.js 2017-10-12 10:41:54 -04:00
Ryan Cramer
7d3dc80798 Fix issue processwire/processwire-issues#395 where renaming a repeater field didn't rename the related template and fieldgroup 2017-10-12 10:36:53 -04:00
Ryan Cramer
0a790cfb43 Fix issue processwire/processwire-issues#399 where InputfieldInteger wasn't supporting defaultValue 2017-10-12 09:44:25 -04:00
Ryan Cramer
90da2e9936 Remove old CKEditor version 2017-10-12 09:43:57 -04:00
Ryan Cramer
df17b337fd Version 3.0.78 has updates primarily specific to enabling more customization from admin themes. In this case, AdminThemeUikit, but applies to any admin theme. 2017-10-06 09:33:39 -04:00
Ryan Cramer
5a39c9efc9 Just some small updates in 3.0.77, mostly related to AdminThemeUikit updates 2017-09-29 12:18:34 -04:00
Ryan Cramer
73d7e997fe Bump version to 3.0.76 2017-09-22 12:30:28 -04:00
Ryan Cramer
f822292eb3 Some updates to ProcessPageView and additions for processwire/processwire-issues#366 to better handle URLs with unrecognized characters 2017-09-21 11:37:46 -04:00
Ryan Cramer
8bcb31359f Fix issue processwire/processwire-issues#363 where repeater with min count and unpublished min item wouldn't get saved if new non-min item was added before save 2017-09-21 06:53:13 -04:00
Ryan Cramer
d765c0d589 Fix issue processwire/processwire-issues#367 where nested FieldsetPage with Repeater/Matrix combinations could cause same non-nested FieldsetPage to not get saved 2017-09-21 06:32:02 -04:00
Ryan Cramer
7943aa1064 Update for processwire/processwire-issues#371 clarify that superuser permissions are not editable (since superuser implies user with all permissions) 2017-09-20 07:50:53 -04:00
Ryan Cramer
78e07bc6cf Enhance Page::localName() method per processwire/processwire-issues#373 2017-09-20 07:15:37 -04:00
Ryan Cramer
327576ff7a Fix issue processwire/processwire-issues#374 where save+next in page editor should not proceed to next page when there were errors on editing page 2017-09-20 07:03:02 -04:00
Ryan Cramer
fffebd9214 Update CKEditor version to 4.7.3 2017-09-20 06:52:56 -04:00
Ryan Cramer
4560ed2997 Fix issue processwire/processwire-issues#377 where install of non-native site profile with 3rd party Fieldtype modules could produce error messages at install time 2017-09-20 06:43:38 -04:00
Ryan Cramer
2a3be7209c Bump version to 3.0.75 2017-09-15 10:49:48 -04:00
Ryan Cramer
064da79bae Update InputfieldFile non-ajax upload mode to warn about file uploads that exceed the max upload size. 2017-09-15 10:48:01 -04:00
Ryan Cramer
b1048297e9 Add nocache URL to image variations modal per processwire/processwire-issues#369 2017-09-11 14:17:22 -04:00
Ryan Cramer
5a9c8fea58 Attempt fix issue processwire/processwire-issues#359 where certain repeater/repeater-matrix combinations weren't initializing propertly 2017-09-11 10:44:32 -04:00
Ryan Cramer
009a2d9342 Fix issue processwire/processwire-issues#353 remove limit on max selector quantity 2017-09-11 09:05:52 -04:00
Ryan Cramer
0bdfdeeb78 Fix issue processwire/processwire-issues#354 where a.b.c='' selector was not working right 2017-09-11 08:30:08 -04:00
Ryan Cramer
8bc3e6ad2e Improve an error message on ProcessPageEditImageSelect.module 2017-09-08 10:10:27 -04:00
Ryan Cramer
41089806e1 Some minor updates for ProcessPageLister 2017-09-08 10:09:45 -04:00
Ryan Cramer
6fef531b1a Some optimizations to FieldtypeRepeater 2017-09-08 10:09:14 -04:00
Ryan Cramer
cc34ec8752 Add support to Pages/PagesLoader for populating directly to existing PageArray 2017-09-08 10:07:25 -04:00
Ryan Cramer
d427b7f563 Fix issue in ProcessPageEditLink when a FieldtypeFieldsetPage field is on the page 2017-09-03 05:58:21 -04:00
Ryan Cramer
34519c4ad3 Minor adjustment to InputfieldRepeater 2017-09-01 11:24:19 -04:00
Ryan Cramer
70289c05a8 Typo fix 2017-09-01 11:02:42 -04:00
Ryan Cramer
b544d0a50f Bump version to 3.0.74 2017-09-01 10:59:47 -04:00
Ryan Cramer
c0e8485a77 Add FieldtypeFielsetPage module to core as part of the repeaters package 2017-09-01 10:58:52 -04:00
Ryan Cramer
3cb9c46e7d Some additional updates for repeater single mode 2017-09-01 10:58:21 -04:00
Ryan Cramer
27ee1fae15 Some adjustments to FieldtypeRepeater for single page mode and update PagesExportImport for support 2017-08-31 11:19:35 -04:00
Ryan Cramer
d9fb9cd026 Updates to FieldtypeRepeater and InputfieldRepeater to support single mode, as used by FieldtypeFieldsetPage 2017-08-30 09:37:19 -04:00
Ryan Cramer
718baae573 Various small fixes and tweaks, and improvements to code documentation in several spots 2017-08-30 09:24:57 -04:00
Ryan Cramer
aa321c6b7b Bump version to 3.0.73 2017-08-25 12:23:41 -04:00
Ryan Cramer
4a26d7626f Improve extendability of FieldtypeFieldsetOpen for FieldtypeFieldsetGroup 2017-08-25 10:33:43 -04:00
Ryan Cramer
055aa6297d Remove fieldset management from ProcessTemplate since it is now native to asmSelect 2017-08-25 10:31:10 -04:00
Ryan Cramer
eaf346df12 Upgrade ProcessField with some additional hooks that can be monitored, along with a few other tweaks 2017-08-25 10:29:04 -04:00
Ryan Cramer
e68e3be9ff Upgrade asmSelect to support fieldsets natively, rather than having it bolted on separately by ProcessTemplate 2017-08-25 10:27:50 -04:00
Ryan Cramer
e96d740586 Some additional updates and fixes for pages export/import 2017-08-25 10:18:09 -04:00
Ryan Cramer
fc2b7944a2 Some minor repeater adjustments to support derived fieldtype in progress 2017-08-25 10:15:10 -04:00
Ryan Cramer
c9fca29283 Fix issue processwire/processwire-issues#344 using page name with not-equals operator in FieldtypePage selector 2017-08-22 11:22:27 -04:00
Ryan Cramer
d841a44955 Fix issue processwire/processwire-issues#293 fix missing repeater labels in nested repeater 2017-08-22 08:51:59 -04:00
Ryan Cramer
950706935e Fix issue processwire/processwire-issues#351 Fix Process.php setViewFile() method typo, and behavior improvements 2017-08-22 06:15:27 -04:00
Ryan Cramer
e38c603ce4 Continued updates to PagesExportImport, add ZIP export/import option to ProcessPagesExportImport (with page files support), and bump version to 3.0.72 2017-08-18 11:33:06 -04:00
Ryan Cramer
74a2b001cc Add support for export/import of comments fields (FieldtypeComments) 2017-08-17 09:38:06 -04:00
Ryan Cramer
b30c6a4ec8 Fix issue processwire/processwire-issues#335 error was thrown by InputfieldImage for too large file, even if client-side resize enabled 2017-08-16 10:36:17 -04:00
Ryan Cramer
56345aff26 Fix issue processwire/processwire-issues#336 where markup regions run on SVG output caused problem 2017-08-16 10:18:40 -04:00
Ryan Cramer
c496c3b859 Fix issue processwire/processwire-issues#340 - image/file tags disappear on existing files after uploading a new file 2017-08-16 09:54:12 -04:00
Ryan Cramer
74dcd51837 Additional updates per issue processwire/processwire-issues#330 - make image replacement keep the same filename (so long as files have same extension). Also upgraded it to retain description and tags during replacement. 2017-08-16 09:02:54 -04:00
Ryan Cramer
b5b2636e01 Add Repeater field support to new Page Export/Import features. 2017-08-15 12:13:25 -04:00
Ryan Cramer
e89235b757 Add $notices->move($from, $to) method for moving notices from one Wire instance to another. 2017-08-15 12:12:03 -04:00
Ryan Cramer
7873422f9e Add ProcessPagesExportImport module to core, and bump version to 3.0.71. To install this new module, go to Modules > Refresh, then Modules > Core > Process > Pages Export/Import > Install 2017-08-11 11:06:31 -04:00
Ryan Cramer
af6ab99d87 Continued major updates to the main PagesExportImport class 2017-08-11 10:45:46 -04:00
Ryan Cramer
d000117e4b Fix issue processwire/processwire-issues#338 where WireTempDir had incorrect order of arguments to explode function 2017-08-11 10:45:06 -04:00
Ryan Cramer
a216b561d1 Upgrade the multi-language text Fieldtypes to support pages export/import 2017-08-11 10:43:49 -04:00
Ryan Cramer
edd71401ed Upgrade LanguagesPageFieldValue to support population by language-name indexed array 2017-08-11 10:42:44 -04:00
Ryan Cramer
a038debd82 Upgrade FieldtypeTextarea to support conversion of file/asset URLs when exporting pages and importing elsewhere 2017-08-11 10:40:49 -04:00
Ryan Cramer
143e42722d Upgrade FieldtypeText to support importing from multi-language to non-multi-language 2017-08-11 10:39:54 -04:00
Ryan Cramer
9e76a2b770 Update FieldtypePage to implement the Fieldtype::__importValue() method for page import support 2017-08-11 10:37:57 -04:00
Ryan Cramer
7ff278a9ff Upgrade Pagefile, FieldtypeFile and FieldtypeImage for improved Page export (exportValue) support 2017-08-11 10:36:31 -04:00
Ryan Cramer
c1f7e96185 Bump version to 3.0.70 2017-08-04 12:33:52 -04:00
Ryan Cramer
3e7b2d0273 Further updates to PagesExportImport class. 2017-08-04 10:58:25 -04:00
Ryan Cramer
dc88104277 Merge branch 'horst-n-patch-1' into dev 2017-08-02 11:29:04 -04:00
Ryan Cramer
84c033a758 Merge branch 'patch-1' of https://github.com/horst-n/processwire into horst-n-patch-1 2017-08-02 11:28:28 -04:00
Ryan Cramer
cc7005192a Update InputfieldCKEditor settings screen Toolbar field to show standard toolbar names included with CKEditor 2017-08-02 11:18:33 -04:00
Ryan Cramer
502774a65f Significant refactoring of WireTempDir class, and update PagefilesManager's use of WireTempDir 2017-08-02 11:17:34 -04:00
Ryan Cramer
a07855c9f6 Minor adjustments, mostly phpdoc related 2017-08-02 11:16:32 -04:00
Ryan Cramer
fa1ff60b97 Add 2nd pass support to Markup Regions, so that a region added in 1st pass can be manipulated by commands in the 2nd pass. Also, some optimizations to region hints, and debug mode improvements. 2017-08-02 10:56:26 -04:00
Ryan Cramer
a1e1f25dff Bump version to 3.0.69 2017-07-28 15:05:27 -04:00
Ryan Cramer
629220ffc8 Add support for closing tag hints in WireMarkupRegions. These increase speed/efficiency for large documents. 2017-07-28 13:49:49 -04:00
Ryan Cramer
4a3f09d34c Fix issue processwire/processwire-issues#330 where drag-n-drop image replacement wasn't working properly 2017-07-28 11:15:00 -04:00
Ryan Cramer
abce877f02 Fix issue processwire/processwire-issues#329 Pagefile::install() method when used with URL argument that contains a query string was screwing up the file extension in the basename 2017-07-28 10:42:20 -04:00
Ryan Cramer
8169594e79 Fix issue processwire/processwire-issues#328 typo fix 2017-07-28 10:29:32 -04:00
Ryan Cramer
277674db23 Fix issue processwire/processwire-issues#325 panel.js and URL fragments fix 2017-07-28 10:10:16 -04:00
Ryan Cramer
71896f463e Fix issue processwire/processwire-issues#320 - MarkupCache, path() method, and WireArray::debugInfo() 2017-07-28 07:10:00 -04:00
Ryan Cramer
52b8389215 Fix issue processwire/processwire-issues#319 where Page::setAndSave() was only working with custom fields and not with DB native properties 2017-07-28 06:52:23 -04:00
Ryan Cramer
0c42013c45 Fix issue processwire/processwire-issues#314 - Lister bookmarks when used with parent.title and %= operator was not working correctly 2017-07-28 06:35:53 -04:00
Ryan Cramer
f5d8633590 Adjustment in Wire class and bump version to 3.0.68 2017-07-21 13:08:05 -04:00
Ryan Cramer
506f66b64a Updates to PagesExportImport class 2017-07-21 13:06:29 -04:00
Ryan Cramer
59549a01be Some optimizations in the PagesEditor class 2017-07-21 13:04:52 -04:00
Ryan Cramer
651e8bd20c CSS adjustment in AdminThemeDefault to improve appearance of language tabs in file/image fields. Also, remove an extra unnecessary margin on language-tabbed text Inputfields. 2017-07-17 06:08:47 -04:00
Ryan Cramer
b7cb72cac6 Fix issue with file/image tags where they weren't initiating properly after an AjaxUploadDone JS event 2017-07-17 05:47:43 -04:00
Ryan Cramer
faf1efc049 Adjust to WireFileTools::find() method to correct potential confusion between windows vs. unix directory separator 2017-07-16 09:57:31 -04:00
Ryan Cramer
0a06c12b82 A couple other tweaks to tags updates and bump version to 3.0.67 2017-07-14 14:09:46 -04:00
Ryan Cramer
659391a4c3 Fix issue processwire/processwire-issues#316 - legacy db API variable class and debug timers in PHP7 producing unnecessary notice 2017-07-14 10:39:54 -04:00
Ryan Cramer
0fe94521fe Fix issue processwire/processwire-issues#315 - remove some extra code that's no longer necessary for image fields in a PageTable 2017-07-14 10:15:31 -04:00
Ryan Cramer
bf351573b0 Fix issue processwire/processwire-issues#310 - update ProcessForgotPassword to use the same requirements as when editing the pass field in the page editor 2017-07-14 09:47:24 -04:00
Ryan Cramer
cab20d518d Add improved tags support for InputfieldFile and InputfieldImage. Now the UI is more tags oriented, and there are more config options for how the tags are input. This commit also adds a modified version of the Selectize js library to provide improved tag inputs. 2017-07-13 15:20:53 -04:00
Ryan Cramer
650fec9a37 Bump version to 3.0.66 2017-07-07 14:07:39 -04:00
Ryan Cramer
786995f85e Add new WireFileTools API var $files->find() method and update the include/compile methods in WireFileTools to use the TemplateFile render stack. 2017-07-07 14:06:28 -04:00
Ryan Cramer
b8d01e88d6 Add render stack support to TemplateFile so that a rendered php file can know what other file(s) are rendering it, when applicable. 2017-07-07 14:05:00 -04:00
Ryan Cramer
ae4761180f Some minor fixes to enable deleting a field that is missing its Fieldtype module. Plus addition of hasPage property to Inputfield modules. And a couple other minor things. 2017-07-07 14:03:39 -04:00
Ryan Cramer
5ec3e48de6 Fix issue processwire/processwire-issues#299 2017-06-30 10:49:31 -04:00
Ryan Cramer
bcd40cdf03 Bump version to 3.0.65 2017-06-23 15:50:18 -04:00
Ryan Cramer
5b0eb5ea81 Fix issue processwire/processwire-issues#264 2017-06-23 09:04:14 -04:00
Ryan Cramer
8107d5e90d Fix issue processwire/processwire-issues#276 (typo in translation text) 2017-06-23 07:34:17 -04:00
Ryan Cramer
651e0e8763 Fix issue processwire/processwire-issues#274 2017-06-23 07:32:03 -04:00
Ryan Cramer
0cccb763a5 Fix issue processwire/processwire-issues#281 (typo in phpdoc example) 2017-06-23 07:11:38 -04:00
Ryan Cramer
438a2944cb Upgrade CKEditor version from 4.6.2 to 4.7.0, plus fix issue processwire/processwire-issues#279 where image placement wasn't working correctly in CKE inline mode. 2017-06-23 06:51:53 -04:00
Ryan Cramer
da3c80d0ca Fix issue processwire/processwire-issues#275 - part 2, fix for exif.js 2017-06-23 06:22:10 -04:00
Ryan Cramer
7ad84bf1c3 Fix issue processwire/processwire-issues#275 2017-06-23 06:15:21 -04:00
Ryan Cramer
dbfb3e2c7e Fix issue processwire/processwire-issues#285 2017-06-22 11:29:35 -04:00
Ryan Cramer
763963ec39 Update WireMarkupRegions to remove class attribute-based action support, since our final spec uses action attributes instead. It only does this for PW installations installed today or later, just in case anyone is using some older examples on an existing site. If you want to force use of the newer version (which is more efficient) set $config->useMarkupRegions=2; The updates made in this commit might also fix processwire/processwire-issues#294 but have not yet confirmed. 2017-06-22 08:45:16 -04:00
Ryan Cramer
3348f3f13a Fix issue processwire/processwire-issues#286 2017-06-22 07:03:28 -04:00
Ryan Cramer
571266c6c1 Fix issue processwire/processwire-issues#288 2017-06-22 06:38:40 -04:00
Ryan Cramer
68a4ddee88 Fix issue processwire/processwire-issues#289 2017-06-21 10:12:09 -04:00
Ryan Cramer
b32a2f0058 Bump version to 3.0.64 2017-06-16 14:34:53 -04:00
Ryan Cramer
05b210766c Continued work on the PagesExportImport class. Not yet ready for use, but much further along and it works in testing on the API side for the basics. Wait before using it though, as there's much more to come here. 2017-06-16 12:50:07 -04:00
Ryan Cramer
0f55f41831 Correct minor issue in Lister for when both the view and edit links for individual pages are disabled 2017-06-16 12:49:19 -04:00
Ryan Cramer
e378acc7bc Correct issue with InputfieldText placeholder attribute sometimes not working for multi-language (like outside the page editor). 2017-06-16 05:56:58 -04:00
Ryan Cramer
1900675bbb Various minor fixes and bump version number to 3.0.63 2017-05-26 14:35:16 -04:00
Ryan Cramer
76fc6dada1 Fix issue processwire/processwire-issues#261 2017-05-26 09:44:14 -04:00
Ryan Cramer
1a590c586f Fix issue processwire/processwire-issues#267 2017-05-26 09:27:19 -04:00
Ryan Cramer
507555e907 Fix issue processwire/processwire-issues#268 2017-05-26 09:18:35 -04:00
Ryan Cramer
5f3827ecba Fix issue processwire/processwire-issues#272 2017-05-26 08:27:48 -04:00
Ryan Cramer
76097ea1ee Add client-side image resize support to InputfieldImage 2017-05-25 11:37:10 -04:00
Ryan Cramer
57b297fd1d Fix issue with InputfieldFile non-ajax mode not working for some cases and bump version to 3.0.62 2017-05-05 13:43:17 -04:00
Ryan Cramer
be99669203 Fix issue processwire/processwire-issues#255 2017-05-02 09:59:06 -04:00
Ryan Cramer
84a39c0667 Fix issue processwire/processwire-issues#256 where the image field modal upload function was not updating the parent window images field, thus preventing the image having the temporary status removed (and not being saved). 2017-05-02 09:50:45 -04:00
Ryan Cramer
b17c9eaed9 Fix InputfieldSelector issue identified by @renobird where checked OR-checkboxes in larger groups of same-field selectors could result in matching the OR to the wrong selector row. 2017-04-30 07:50:35 -04:00
Ryan Cramer
347240acd9 Minor adjustments to repeater and asmSelect 2017-04-28 14:54:26 -04:00
Ryan Cramer
7adf09e305 Small .htacess update for HTTPS redirect support on AWS ELB 2017-04-28 14:53:07 -04:00
Ryan Cramer
0cd8a7a276 Add WireHttp::getResponseHeaderValues() per processwire/processwire-issues#253 2017-04-28 09:46:08 -04:00
horst
64e9ca214d stripped out tab_width 2017-04-24 10:35:20 +02:00
horst
3337aff4d5 create .editorconfig in wire directory
This is very helpful for contributors who uses different editor settings, for example indentation with spaces, not tabs. Most of all common editor programms or IDEs nowadays support to preserve the defined coding style rules automatically. Read more here: http://editorconfig.org
2017-04-23 12:00:33 +02:00
horst
188d0e150d correct syntax highlighting of *.module files
on github. 
see: https://processwire.com/talk/topic/16124-github-now-support-custom-highlight-config/
2017-04-23 11:51:08 +02:00
Ryan Cramer
3fc9f69da7 Some minor adjustments and bump version to 3.0.61 2017-04-21 11:27:47 -04:00
Ryan Cramer
8d02e72320 Update font-awesome to 4.7 per processwire/processwire-requests#72 2017-04-20 05:59:28 -04:00
Ryan Cramer
1c46d0d44c Fix issue with repeaters in renderValue mode 2017-04-19 10:04:16 -04:00
Ryan Cramer
e83ed750c9 Add option for InputfieldHidden to still render as an input in renderValue mode 2017-04-19 10:03:30 -04:00
Ryan Cramer
c259ce8103 Fix minor issue with CommentForm front-end output 2017-04-19 10:02:00 -04:00
Ryan Cramer
a8febefa70 Fix issue processwire/processwire-issues#248 2017-04-19 09:33:43 -04:00
Ryan Cramer
42de2e7bda Fix issue processwire/processwire-issues#245 2017-04-19 09:29:50 -04:00
Ryan Cramer
bb12873a19 Fix issue with multi-template settings not always updating the selectable columns on columns tab 2017-04-16 06:46:31 -04:00
Ryan Cramer
93d1be8453 Bump version to 3.0.60 dev 2017-04-14 14:19:10 -04:00
Ryan Cramer
223b80d685 Update/add documentation to a few classes (like WireCache), correct issue with HTTPS detection on AWS load balancer, plus some other minor tweaks. 2017-04-14 06:32:23 -04:00
Ryan Cramer
93779e2017 Update MarkupHTMLPurifier version per processwire/processwire-issues#243 2017-04-14 06:29:11 -04:00
Ryan Cramer
fb1cc857f2 Fix issue processwire/processwire-issues#244 2017-04-14 06:12:44 -04:00
Ryan Cramer
b914586f6c Fix issue processwire/processwire-issues#227 update Sanitizer::selectorValue() allow exclamation point "!" at beginning of selector value 2017-04-13 08:54:49 -04:00
Ryan Cramer
fd2f14445c Fix issue processwire/processwire-issues#233 2017-04-13 08:36:23 -04:00
Ryan Cramer
267a368034 Fix issue processwire/processwire-issues#241 2017-04-13 06:14:45 -04:00
Ryan Cramer
ae50a0563b Attempt to fix issue processwire/processwire-issues#242 for FieldtypeComments + utf8mb4 charset combo 2017-04-13 06:06:46 -04:00
Ryan Cramer
81c8d4eb2f Fix issue processwire/processwire-issues#236 with InputfieldSelector, plus update the "None" selection option to fix another issue reported in ListerPro board 2017-04-10 08:23:45 -04:00
Ryan Cramer
168a4ffa58 Bump version to 3.0.59 plus a couple other minor updates 2017-04-07 10:14:40 -04:00
Ryan Cramer
00e7a46434 Fix issue processwire/processwire-issues#222 with InputfieldImage thumbnail logic 2017-04-07 06:08:50 -04:00
Ryan Cramer
0714279ba9 Update phpdoc for $pages->sort() per processwire/processwire-issues#225 2017-04-07 06:00:11 -04:00
Ryan Cramer
60989f97b4 Fix issue processwire/processwire-issues#229 2017-04-07 05:44:08 -04:00
Ryan Cramer
75a969bafb Various minor tweaks/updates 2017-04-07 05:11:06 -04:00
Ryan Cramer
98594eb024 Bump version to 3.0.58 2017-03-31 15:43:56 -04:00
Ryan Cramer
ef2fd54e68 Add spelling correction per @mestaritonttu PR #62 2017-03-30 07:01:51 -04:00
Ryan Cramer
2d1864c80d Add @jofalk PR #59 that fixes the issue with </edit> tags not being stripped from front-end editor. 2017-03-30 06:57:21 -04:00
Ryan Cramer
c5033e1e42 Add @derixithy PR #58 for additional sessionCookieDomain call 2017-03-30 06:48:02 -04:00
Ryan Cramer
3f67722294 Add @LostKobrakai PR #50 plus some other tweaks to FieldtypeDatetime module 2017-03-30 06:27:52 -04:00
Ryan Cramer
94876a7bde Merge branch 'Notanotherdotcom-master' into dev 2017-03-30 06:03:39 -04:00
Ryan Cramer
dd777ea9d8 Merge branch 'master' of https://github.com/Notanotherdotcom/processwire-1 into Notanotherdotcom-master 2017-03-30 06:03:16 -04:00
Ryan Cramer
2b51c75cee Various minor tweaks related to AdminThemeFramework 2017-03-30 05:53:46 -04:00
Ryan Cramer
4ca684df83 Fix 2 Lister related issues with 'parent' filters/columns identified by @somatonic in ListerPro board. 2017-03-26 06:40:35 -04:00
Ryan Cramer
ec4726b3df A few updates in support of new AdminThemeFramework and bump version to 3.0.57 2017-03-24 13:23:36 -04:00
Notanotherdotcom
04c87f1954 Branding Upgrade
Replaces the logo in the Default and Reno admin themes with the new
version for branding consistency.
2017-03-23 22:57:18 +00:00
Ryan Cramer
722b504273 Additional updates to $languages setLocale/getLocale methods per processwire/processwire-issues#215 2017-03-20 08:26:21 -04:00
Ryan Cramer
8e241f0132 Add @horst-n fix for ImageSizerEngine rounding issue processwire/processwire-issues#191 2017-03-20 05:49:34 -04:00
Ryan Cramer
a410bf8236 Some fixes to InputfieldSelector parent.subfield properties, plus fix issue where datetime field didn't always recognize when the date picker should include a time picker as well. 2017-03-19 07:07:52 -04:00
Ryan Cramer
1f23b54f45 Fix issue where non-default date format could get lost in session values of InputfieldSelector.module 2017-03-19 06:27:20 -04:00
Ryan Cramer
4414a2db2b Bump version to 3.0.56 plus some other minor tweaks 2017-03-17 13:25:29 -04:00
Ryan Cramer
9c92ce5305 Updates for issue processwire/processwire-issues#215 to better support locale settings on front-end, plus add $languages->setLocale() and $languages->getLocale() methods, and freshen up code and docs in related classes. 2017-03-16 09:49:47 -04:00
Ryan Cramer
e1928c9e3c Some cleanup in ProcessPageList.module, plus attempt compromise fix identified for ProcessPageEdit breadcrumb starting from issue #22. 2017-03-15 14:12:27 -04:00
Ryan Cramer
770c717baa Updates related to WireMarkupRegions discussion in issue processwire/processwire-issues#195 - fix behavior of boolean pw-before/pw-after attributes, and add support for <pw-region> or <region> tags. 2017-03-15 14:04:49 -04:00
Ryan Cramer
11ebcfb456 Fix issue where some non-text based FieldtypeTable subfields had selection issues in InputfieldSelector 2017-03-12 11:16:26 -04:00
Ryan Cramer
11c49f2bb1 Fix issue with page edit dropdown menu actions appearing in buttons they aren't intended to 2017-03-12 07:15:40 -04:00
Ryan Cramer
8018ecc7af Bump version to 3.0.55 and a couple other minor adjustments 2017-03-10 12:46:01 -05:00
Ryan Cramer
8b96e6b060 Attempt fix for issue processwire/processwire-issues#206 fix for multi-site config not working 2017-03-10 09:05:08 -05:00
Ryan Cramer
b899bc42e7 Fix issue processwire/processwire-issues#202 where the leave confirm box was appearing when it shouldn't, after saving after a file had been uploaded. Also added drag/drop protection so that if you accidentially drag/drop a file outside of the specified dropzone, it gets ignored, rather than loading the file in the browser. 2017-03-10 08:38:16 -05:00
Ryan Cramer
6fe703f699 Fix issue processwire/processwire-issues#203 update to make API-created users always have guest role 2017-03-10 08:13:56 -05:00
Ryan Cramer
12767e284d Fix issue processwire/processwire-issues#201 where InputfieldPassword wasn't honoring the minlength setting and was always requiring at least 8 characters 2017-03-10 07:55:18 -05:00
Ryan Cramer
9df4469314 Fix issue processwire/processwire-issues#200 where the WireMarkupRegions self-closing tags list needed <link> and a few others 2017-03-10 06:08:12 -05:00
Ryan Cramer
ed0ba504bc Fix issue processwire/processwire-issues#199 add clarification notes to InputfieldFile "Inputfield Type" option, and prevent selection of "Image" type when in a FieldtypeFile 2017-03-10 05:50:18 -05:00
Ryan Cramer
020c1beca5 Fix issue processwire/processwire-issues#197 correcting an issue with repeater item label contained string "{images.count}" 2017-03-09 15:12:26 -05:00
Ryan Cramer
97f33e02ed Fix issue processwire/processwire-issues#198 (typo misspelling in phpdoc) 2017-03-09 10:16:05 -05:00
Ryan Cramer
95f5cba2ed Fix issue processwire/processwire-issues#196 (cosmetics) 2017-03-09 10:08:54 -05:00
Ryan Cramer
a3ba477346 Fix issue processwire/processwire-issues#194 2017-03-09 09:48:32 -05:00
Ryan Cramer
e87dcd5985 Fix issue processwire/processwire-issues#192 where inserted emoji could cause text to be truncated on systems using dbEngine "utf8" (as opposed to "utf8mb4"). Because the emoji/MB4 detection and replacement has some overhead, it's not enabled by default. To enable, set $config->dbStripMB4=true; in your /site/config.php file. 2017-03-09 09:11:30 -05:00
Ryan Cramer
25bfb8a5a6 Fix issue processwire/processwire-issues#208 where JqueryWireTabs was not properly remembering current tab between requests 2017-03-09 05:44:14 -05:00
Ryan Cramer
bbb8e987c9 Add support for Pages > Tree navigation: drill down through the page tree within the top navigation dropdowns. This works in AdminThemeDefault and AdminThemeUikit but not in AdminThemeReno (which just shows the first level, per its nav setup). 2017-03-08 11:43:58 -05:00
Ryan Cramer
c4ea3c3356 Fix issue processwire/processwire-issues#189 FieldtypeOptions and "!=" operators 2017-03-07 10:31:13 -05:00
Ryan Cramer
f32593adfa Fix issue processwire/processwire-issues#188 to provide more detailed warning message when installing module that doesn't meet requirements 2017-03-07 09:03:58 -05:00
Ryan Cramer
d7d392fe95 Fix issue #187 where $page->httpUrl didn't respect Template::slashUrls==0 setting when used in non-multi-language. 2017-03-07 08:49:28 -05:00
Ryan Cramer
bc032e1ce3 Minor optimization to a few Page hook method calls in Fieldtype 2017-03-07 08:32:32 -05:00
Ryan Cramer
4d6ce48252 Update login locale detection to suggest different actions depending on whether multi-language support is installed, per processwire/processwire-issues#184 2017-03-07 08:24:04 -05:00
Ryan Cramer
c7d4e5f662 Fix issue processwire/processwire-issues#185 by adding a strict mode to the date sanitizer and updating the method notes a bit. 2017-03-07 07:54:37 -05:00
Ryan Cramer
d5971733f2 Tweak to InputfieldDatetime datepicker button option, plus adjustment to ProcessPageEdit when in modal field edit 2017-03-06 08:43:25 -05:00
Ryan Cramer
a12cb029bb Various updates supporting core admin theme customization and bump version to 3.0.54 2017-03-03 10:37:33 -05:00
Ryan Cramer
f137dd2966 Updated several modules and classes to better support admin theme customization, consistent with needs in AdminThemeUikit 2017-02-24 12:23:57 -05:00
Ryan Cramer
ad90996c0b Update CKEditor version to 4.6.2 2017-02-13 06:05:42 -05:00
Ryan Cramer
ceae45cda7 Add problematic basenaem/locale detection and warning per issue processwire/processwire-issues#157 2017-02-06 06:38:59 -05:00
Ryan Cramer
310ea9d281 Bump version to 3.0.52 2017-02-03 11:26:05 -05:00
Ryan Cramer
4fa5641256 Fix issue processwire/processwire-issues#182 2017-02-03 06:02:36 -05:00
Ryan Cramer
60d418fe12 Fix issue processwire/processwire-issues#117 2017-02-02 10:07:01 -05:00
Ryan Cramer
21f48868a1 Fix issue processwire/processwire-issues#178 2017-02-02 09:40:48 -05:00
Ryan Cramer
8305215407 Fix issue processwire/processwire-issues#175 2017-02-02 09:13:22 -05:00
Ryan Cramer
666ddc528b Fix issue processwire/processwire-issues#181 2017-02-02 05:56:44 -05:00
Ryan Cramer
e518650515 Fix issue processwire/processwire-issues#171 which updates our reserved field names list 2017-02-01 09:37:24 -05:00
Ryan Cramer
1f021206d1 Attempt to fix issue processwire/processwire-issues#168 2017-02-01 09:21:42 -05:00
Ryan Cramer
8e08e0d5ac Fix issue processwire/processwire-issues#163 2017-02-01 08:28:24 -05:00
Ryan Cramer
144bd01619 Fix issue processwire/processwire-issues#153 where selector fails when there is leading comma in quoted value 2017-02-01 06:36:40 -05:00
Ryan Cramer
81adf00b8f Some optimizations to WireMarkupRegions class, and add a hasField property to Inputfield modules when paired with a Fieldtype (similar to existing hasFieldtype property, except is a Field object rather than Fieldtype object). 2017-02-01 06:11:15 -05:00
Ryan Cramer
1886f132b5 Fix for debug mode warning in WireMarkupRegions plus other minor tweaks 2017-01-29 07:08:18 -05:00
Ryan Cramer
933d9de724 Minor admin css adjustment and bump version number to 3.0.51 2017-01-27 14:08:27 -05:00
Ryan Cramer
4a3812efe0 Add a "Publish and Add Another" option to the Submit button options in ProcessPageAdd.module (for when template contains only a title) 2017-01-27 14:05:44 -05:00
Ryan Cramer
01f4419113 Some adjustments to WireMarkupRegions 2017-01-27 14:04:31 -05:00
Ryan Cramer
2becc0cbba Add an val() method to the Inputfield class for consistency with jQuery. This method is a shortcut for getting or setting the value attribute of Inputfield objects 2017-01-27 14:03:28 -05:00
Ryan Cramer
279f65ec65 Small adjustment to InputfieldSelector to prevent access control scenario that hides selectable options for FieldtypePage/InputfieldPage fields. 2017-01-22 08:10:48 -05:00
Ryan Cramer
b50b3aa3c7 Update MarkupFieldtype class to detect FieldtypeLanguageInterface objects 2017-01-22 07:18:10 -05:00
Ryan Cramer
f01ad1fe25 Bump version to 3.0.50 2017-01-20 15:25:45 -05:00
Ryan Cramer
31efae4d16 Fix issue processwire/processwire-issues#166 2017-01-20 11:22:29 -05:00
Ryan Cramer
5a8a4af23f Updates to WireMarkupRegions class to support use of "pw-id" and "data-pw-id" as an alternative to "id", and support of "pw-[action]=id" and "data-pw-[action]=id" attributes as an alternative to the "pw-" class names introduced last week. The [action] can be: append, prepend, replace, remove, before, after. 2017-01-20 11:08:38 -05:00
Ryan Cramer
65eb8da0ff Various minor updates 2017-01-20 10:53:57 -05:00
Ryan Cramer
0d82cee465 Fix issue processwire/processwire-issues#164 2017-01-20 10:16:36 -05:00
Ryan Cramer
07dbc4e6f6 Fix issue #161 where deleted page using default file/image fallback could delete fallback file if deleting page without outputFormatting enabled. 2017-01-20 09:49:19 -05:00
Ryan Cramer
a434f6193d Bump version to 3.0.49 and adjustment to debug info generated in WireMarkupRegions.php 2017-01-13 16:52:26 -05:00
Ryan Cramer
062ebd6663 Move some code from PageRender.module to WireMarkupRegions.php and make additional updates to WireMarkupRegions, adding support for nested/recursive regions. 2017-01-13 12:23:16 -05:00
Ryan Cramer
12a45994bf Add support for markup regions, a simple new template file output strategy that bridges the gap between direct and delayed output. See comments in the PageRender.module file for more details. 2017-01-12 15:45:07 -05:00
Ryan Cramer
6bfe4e65e2 Minor corrections to phpdoc in Page, WireArray and PageArray 2017-01-12 15:09:22 -05:00
Ryan Cramer
501a097e29 Minor improvements to installer, mostly phpdoc related 2017-01-12 15:08:28 -05:00
Ryan Cramer
307d7c9e7d Adjustment to Page::isChanged() method to reduce Pages::save() overhead 2017-01-09 08:12:42 -05:00
Ryan Cramer
43986173fb Fix issue #151, minor correction to FileCompiler affecting single lines that contain both className and function from ProcessWire namespace at the same time 2017-01-08 06:52:13 -05:00
Ryan Cramer
5fbbd9e2c6 Bump version to 3.0.48 2017-01-06 16:25:29 -05:00
Ryan Cramer
cde8b0a3e3 Add some initial stuff to support pages export/import. Nothing to use here yet, just building the foundation. 2017-01-06 12:25:24 -05:00
Ryan Cramer
afd0e8bc95 Adjustment to FileCompiler to improve detection of false positive end-of-PHP blocks 2017-01-06 11:16:26 -05:00
Ryan Cramer
8d97815110 Minor correction to previous commit 2017-01-05 15:36:15 -05:00
Ryan Cramer
d4d90a53fe Add @rolandtoth PR #16 that triggers showInputfield or hideInputfield events when showing/hiding Inputfields due to dependencies. 2017-01-05 14:46:42 -05:00
Ryan Cramer
d785fd7523 Minor adjustments to comment blocks: Add @LostKobrakai PR #42, PR #44, PR #45 and add @owzim PR #43 2017-01-05 14:32:09 -05:00
Ryan Cramer
928a399b8a Add @gmclelland PR #47 that fixes "search autocomplete keyboard focus errors". This also adds a compromise of accommodates PR #48 which retains support of up-arrow-to-close only if down arrow hasn't already been pressed. See also processwire/processwire-issues#125 and processwire/processwire-issues#141 2017-01-05 14:14:29 -05:00
Ryan Cramer
9ed0e415ed Adjustment to yesterday's FileCompiler updates that corrects a case where wire() function calls are used within include() statements 2017-01-05 13:02:12 -05:00
Ryan Cramer
c0ef1aea92 Fix issue processwire/processwire-issues#98 and also make some upgrades/optimizations to FileCompiler 2017-01-04 13:06:03 -05:00
Ryan Cramer
506187d871 Merge branch 'adrianbj-fix-input-url-path' into dev 2017-01-03 05:17:04 -05:00
Ryan Cramer
34ee4e051c Adjustment to PR #46 2017-01-03 05:16:32 -05:00
Ryan Cramer
5238b00250 Merge branch 'fix-input-url-path' of https://github.com/adrianbj/processwire into adrianbj-fix-input-url-path 2017-01-03 05:14:16 -05:00
Ryan Cramer
0d8cce638c Minor adjustments 2017-01-03 05:13:21 -05:00
Ryan Cramer
51bda526eb Bump version to 3.0.47, fix issue #139, issue #123, and issue #115 2016-12-30 13:09:07 -05:00
Ryan Cramer
8cb944cf52 Some updates to the template_ids support for FieldtypePage/InputfieldPage 2016-12-30 07:22:51 -05:00
Ryan Cramer
7d57fc5784 Fix issue processwire/processwire-issues#135 2016-12-29 10:48:07 -05:00
Ryan Cramer
c2cf538317 Minor CKEditor README.md update related to last commit 2016-12-29 10:06:24 -05:00
Ryan Cramer
264c5b0b54 Fix issue processwire/processwire-issues#133 updating a few links pointing to old GitHub repo to point to new one 2016-12-29 10:03:28 -05:00
Ryan Cramer
4402932ee6 Add request issue processwire/processwire-issues#137 2016-12-29 09:55:43 -05:00
Ryan Cramer
88e143126f Fix issue processwire/processwire-issues#138 where PageFinder had a pagination issue introduced in 3.0.46 2016-12-28 13:55:52 -05:00
Ryan Cramer
e12095e622 Attempt to fix issue processwire/processwire-issues#134 2016-12-28 07:03:17 -05:00
Ryan Cramer
20b6ebb81f Fix issue #128 2016-12-26 11:10:09 -05:00
Ryan Cramer
b6ba7049a0 Fix issue #127 2016-12-26 10:35:04 -05:00
Ryan Cramer
4e2a3c1bac Fix issue that was preventing non-local property hooks from working in WireHooks class 2016-12-26 09:31:42 -05:00
Ryan Cramer
622896e028 Fix issues #130 and #131 (dup) 2016-12-25 07:44:55 -05:00
Ryan Cramer
e8b9f12943 Bump version to 3.0.46 2016-12-23 15:13:13 -05:00
Ryan Cramer
ba9688af65 Improvements to dropdown submit buttons so that the menu doesn't disappear immediately when accidentally outside of it. 2016-12-23 12:24:52 -05:00
Ryan Cramer
e14d52f722 Add new $pages API methods: findIDs(), sort(), insertAfter, insertBefore(). Add support for negative "limit" and "start" values in selectors, where negative values reference the end of the set rather than the beginning. Add support for "eq=n" (or alias "index=n") selectors (both WireArray and PageFinder) for pulling a specific n'th item, can also specify "first" or "last" for "n". Update $page->editUrl(true) method to force return of edit URL with scheme and hostname. 2016-12-23 12:18:58 -05:00
adrianbj
137cbbd186 Fix for incorrect path returned from $input->url() when called before page is available.
It used to return: 
//admin/page/edit/-id-1111/?id=1111
Instead of the correct:
/admin/page/edit/?id=1111

This appears to fix everything correctly.
2016-12-23 08:38:20 -08:00
Ryan Cramer
0844bf2e47 Fix issue processwire/processwire-issues#119 2016-12-20 06:27:26 -05:00
Ryan Cramer
381293e0c7 Add a new $pages->findIDs() method, plus some experimental options in PageFinder 2016-12-19 13:38:07 -05:00
Ryan Cramer
e34918c73a Attempt to fix processwire/processwire-issues#122 2016-12-19 13:00:20 -05:00
Ryan Cramer
2570d6c86d Fix issue processwire/processwire-issues#121 2016-12-19 12:34:59 -05:00
Ryan Cramer
315251fa04 Some optimizations to PagesLoader::find() and findShortcut() methods. 2016-12-19 07:00:25 -05:00
Ryan Cramer
aab97a0b00 Fix inadvertent debug mode memory leak issue when using $pages->findMany(), plus optimize PageTable for faster load when sortfield(s) are in use. Per @apeisa / Avoine. 2016-12-19 06:57:37 -05:00
Ryan Cramer
3f758312a6 Bump version to 3.0.45 2016-12-16 15:19:38 -05:00
Ryan Cramer
8d8d9dfe3d Update the PageArrayIterator::$chunkSize setting to be configurable via $config->lazyPageChunkSize per @apeisa 2016-12-16 11:12:23 -05:00
Ryan Cramer
ffde966920 Upgrades to configuration of FieldtypePage and InputfieldPage (as seen in field editor), Upgrades to Page and WireArray __invoke() support, Rewrite Selectors::stringHasSelector() method to make it more accurate, Upgrades to Sanitizer::minArray(), add support for load-time filtering to FieldtypePage (Page fields), plus some other small tweaks. 2016-12-16 10:45:32 -05:00
Ryan Cramer
83846d1fe2 Minor tweaks 2016-12-14 12:26:26 -05:00
Ryan Cramer
7e262fa60d Some updates to the wireRegion()/region() function to support locking prepend or append 2016-12-14 11:36:26 -05:00
Ryan Cramer
2b9a7adbcb Update to allow for Page objects to load pages (children, parent, etc.) that don't get cached when their load option specified cache=false. Also makes the 'parent' page lazy loading for Page objects, so that it doesn't load the parent Page until a $page->parent() call is requested. Updates for @apeisa / Avoine request. 2016-12-14 11:02:32 -05:00
Ryan Cramer
3821dc2be3 Add add inline script support to Inputfield elements rendered with Lister per processwire/processwire-issues#112 2016-12-14 06:00:38 -05:00
Ryan Cramer
d077dfa32b Update InputfieldRepeater to process inline scripts from Inputfields per processwire/processwire-issues#112 2016-12-14 05:47:16 -05:00
Ryan Cramer
953ca72014 Various updates including optimizations to WireHooks, support for a hookable renderReadyHook method in Inputfield, cache prevention measures for Pages::findMany() per @apeisa, and some work in progress on InputfieldSelector support for improved "custom (field=value)" searches. 2016-12-13 14:24:01 -05:00
Ryan Cramer
e2e8c35c2c Fix issue processwire/processwire-issues#110 2016-12-12 09:26:09 -05:00
Ryan Cramer
f0212dcc9c Updates to support multiple template selections for Page fields per @apeisa and @niklam PR #22, plus bump version to 3.0.44 2016-12-09 14:16:04 -05:00
Ryan Cramer
880810c6bb Additional repeater updates including addition of a "minimum items" option, and support for an accordion mode. 2016-12-09 10:08:55 -05:00
Ryan Cramer
6027e87a5e Repeater updates continued with a complete refactoring of the InputfieldRepeater.js file. This update also corrects the depth-dragging issues that were present in the last commit (issues were especially noticable dragging when using AdminThemeReno). Also adds a repeater open all/close all function per processwire/processwire-requests#33 - to use it, double click the "on/off" toggle icon that appears next to the trash icon. 2016-12-08 14:02:43 -05:00
Ryan Cramer
d92674fd4a Minor adjustment to improve repeater depth drag behavior in AdminThemeReno. Still not there yet, but this adjustment improves it a little bit. Works much better in AdminThemeDefault for some reason. 2016-12-07 15:24:42 -05:00
Ryan Cramer
c44329817c Correct issue where AdminThemeReno was missing the extra "head" output, which prevented the vex dialogs from being styled 2016-12-07 12:29:05 -05:00
Ryan Cramer
d1dcebe002 Update cachebuster version code in admin themes, just in case the previous commits updates to main.js are cached and not immediately visible 2016-12-07 12:21:47 -05:00
Ryan Cramer
e2f9597c5a Update Repeaters to add these features: 1) Support for repeater depth per @jlahijani, 2) Add new repeater item "clone" feature, 3) Add support for max items, 4) Add double-click trash icon to "delete all" feature. This commit also adds the Vex library for nicer looking Javascript alert and confirm boxes, which we use to confirm "clone" and "delete all" operations in repeaters, and will use elsewhere. 2016-12-07 11:24:49 -05:00
Ryan Cramer
423fbe6f57 Fix issue in Page.php formatFieldValue function that occurs when formatting a single image|null field. 2016-12-03 07:55:08 -05:00
Ryan Cramer
ac9487e967 Bump version to 3.0.43 2016-12-02 13:31:09 -05:00
Ryan Cramer
af5cda1d6d Minor adjustment per remaining button in issue in processwire/processwire-issues#84 2016-12-02 11:23:24 -05:00
Ryan Cramer
707cd9e735 Expand upon the Page::url() method to include an $options argument that adds many new capabilities. 2016-12-02 10:38:49 -05:00
Ryan Cramer
7eddb51bc9 Some documentation adjustments to WireArray 2016-12-01 12:36:53 -05:00
Ryan Cramer
015725191b Fix issue processwire/processwire-issues#100 where WordPress creating a fake mb_strlen() function made PW think that multibyte support was installed. 2016-12-01 12:07:25 -05:00
Ryan Cramer
9cdf68e6b8 Fix processwire/processwire-issues#99 where multi-instance URLs that had hostname specified were missing a leading slash 2016-12-01 12:00:41 -05:00
Ryan Cramer
cbdf8e9063 Fix issue processwire/processwire-issues#97 where using {images.count} in repeater label caused issue with loading images in the repeater 2016-12-01 11:22:40 -05:00
Ryan Cramer
ef42513831 Add PageFieldValueInterface as an implementation option for objects supporting page field values. Basically a helper for certain object-based values like Pagefiles/Pageimages, and likely others. 2016-12-01 10:56:04 -05:00
Ryan Cramer
30f96593bb Fix issue processwire/processwire-issues#94 for integer field not adding min attribute to HTML5 number type when 0 specified as minimum and no maximum specified. 2016-12-01 05:40:24 -05:00
Ryan Cramer
b8e51db176 Some updates to touch support per issue #84, plus a few other minor things that got bundled in 2016-11-30 13:18:14 -05:00
Ryan Cramer
6a3789d892 Merge branch 'marcus-herrmann-fix/improve-a11y-markup-pager-nav' into dev 2016-11-29 10:39:50 -05:00
Ryan Cramer
c4958461f1 Some changes to Marcus's PR, plus additional updates to MarkupPagerNav module, including expanded documentation 2016-11-29 10:38:26 -05:00
Ryan Cramer
103642ef6d Merge branch 'fix/improve-a11y-markup-pager-nav' of https://github.com/marcus-herrmann/processwire-1 into marcus-herrmann-fix/improve-a11y-markup-pager-nav 2016-11-29 06:06:06 -05:00
Ryan Cramer
76cb88815a Merge branch 'teppokoivula-feature-comments-manager-tweaks' into dev 2016-11-29 06:00:50 -05:00
Ryan Cramer
d3f9d4fd5b Merge branch 'feature-comments-manager-tweaks' of https://github.com/teppokoivula/processwire-1 into teppokoivula-feature-comments-manager-tweaks 2016-11-29 06:00:34 -05:00
Ryan Cramer
53612e9489 Update ProcessField to use more verbose field type names for additional clarity. Also a couple minor cosmetic adjustments in LanguageSupport 2016-11-28 11:04:02 -05:00
Ryan Cramer
bba6e3b00f Add option to exclude FileCompiler at include() time by specifying include(/*NoCompile*/'filename.php'); or include('filename.php'/*NoCompile*/);. The include() can also be include_once(), require(), require_once(), wireRenderFile(), wireIncludeFile() or new TemplateFile(). Relevent to PR #13 2016-11-28 10:59:33 -05:00
mhe
bf2ff863dd Update/simplify MarkupPagerNav default markup 2016-11-27 17:12:01 +01:00
mhe
016290192f Use aria-current also in built in site profiles 2016-11-27 17:10:22 +01:00
mhe
7038b787f0 Add defaults, labels, markers for accessibility to MarkupPagerNav 2016-11-27 10:47:25 +01:00
teppokoivula
a8efd4c04b Fix typo and make tab navigation always visible
This commit fixes a typo that prevented using the module and also makes
tab navigation visible even when active tab is empty.

Fixes processwire/processwire-issues#95
2016-11-26 20:42:45 +02:00
Ryan Cramer
332dbb317e Merge branch 'marcus-herrmann-fix/a11y-core-site-profiles' into dev 2016-11-25 15:04:41 -05:00
Ryan Cramer
cfb6823d49 Merge branch 'fix/a11y-core-site-profiles' of https://github.com/marcus-herrmann/processwire-1 into marcus-herrmann-fix/a11y-core-site-profiles 2016-11-25 15:03:34 -05:00
Ryan Cramer
35df716082 Additional documentation updates and bump version to 3.0.42 2016-11-25 14:58:02 -05:00
Ryan Cramer
942cac2707 Update $sanitizer->testAll() method for more methods per processwire/processwire-issues#85 2016-11-23 07:15:17 -05:00
Ryan Cramer
2a66b67174 Fix issue processwire/processwire-issues#92 where $sanitizer->markupToText() left useless trailing character when replacing br tags 2016-11-23 06:43:14 -05:00
Ryan Cramer
c1f4693ca0 Documentation updates to several core classes 2016-11-22 14:26:41 -05:00
Ryan Cramer
3647a47b86 Adjustment to ProcessPageEditLink.module to correct debug mode warnings that appear when called from inside a CKEditor field that's in a repeater 2016-11-20 06:48:52 -05:00
mhe
28d119c273 Improve accessibility in site profiles 2016-11-20 09:47:25 +01:00
Ryan Cramer
d013ef8550 Merge branch 'derixithy-dev' into dev 2016-11-18 12:29:48 -05:00
Ryan Cramer
6fbfdbab95 Merge branch 'dev' of https://github.com/derixithy/processwire into derixithy-dev 2016-11-18 12:28:39 -05:00
Ryan Cramer
d935e9b699 Some minor doc tweaks, update $modules->getModuleInfo() method to support returning info for all modules, bump version to 3.0.41 2016-11-18 12:23:27 -05:00
Ryan Cramer
a1219e38e9 Some phpdoc updates in a few classes, plus fix a bug in the $config->paths() method 2016-11-17 15:20:09 -05:00
Ryan Cramer
ab7b7a6380 Fix issue processwire/processwire-issues#80 where missing site/templates/admin.php file rendered blank output rather than throwing exception 2016-11-15 08:03:55 -05:00
Ryan Cramer
a98cd1b351 Merge branch 'borantula-patch-1' into dev 2016-11-14 08:12:37 -05:00
Ryan Cramer
8b1e5a3c41 Fix issue processwire/processwire-issues#66 where deleting item from 2 different repeater fields at same time resulted in only item from first repeater being deleted 2016-11-14 07:57:29 -05:00
Ryan Cramer
facc671e8d Fix issue processwire/processwire-issues#81 where 2 translation phrases were on 1 line when they should have been on 2 2016-11-14 06:42:35 -05:00
Ryan Cramer
4b800adb5a Fix issue #78 with PagePathHistory where a cloned child of a renamed parent page could redirect to wrong item when accessed at old URL 2016-11-14 06:38:11 -05:00
Ryan Cramer
e0af12d7ab Fix issue #76 with typo in Pages.php 2016-11-14 06:21:36 -05:00
Ryan Cramer
58c659b288 Add @adrianbj PR #34 removing duplicate line in ProcessTemplate.module 2016-11-14 06:13:56 -05:00
Ryan Cramer
7700627d33 Merge branch 'pine3ree-patch-1' into dev 2016-11-14 05:57:57 -05:00
Ryan Cramer
0d81ff542b Merge branch 'patch-1' of https://github.com/pine3ree/processwire-1 into pine3ree-patch-1 2016-11-14 05:57:39 -05:00
Ryan Cramer
25c703b924 Merge branch 'teppokoivula-feature-querystring-overrides' into dev 2016-11-14 05:51:05 -05:00
Ryan Cramer
a35d00baab Merge branch 'feature-querystring-overrides' of https://github.com/teppokoivula/processwire-1 into teppokoivula-feature-querystring-overrides 2016-11-14 05:50:41 -05:00
Ryan Cramer
654c18fbe9 Various minor tweaks and documentation improvements 2016-11-14 05:37:50 -05:00
Bora Yalçın
1bb92f1941 turkish chars added to defaultReplacements array
Special letters in Turkish alphabet (see below) added to default replacements array as they were becoming - in page names.

ı=i
ğ=g
İ=i
Ç=c
ş=s
Ş=s
2016-11-09 09:37:56 +01:00
Derixithy
3897df4436 Added support for cookie domain 2016-11-07 20:14:52 +01:00
maks feltrin
78acf909d3 Sanitizer::string small refactoring
Since we are doing type-checking we are dealing with mutually excluding conditions.

So far the code blocks for non-string type matching will transform `$value` into a string anyway so if we have an object, the null value or a bool we would get a string and any further test would be not needed for it. W only need to test the unprocessed value, i.e. other conditions on the input value.

Now the code reads like this:
object? => get a string
or null? => get an (empty) string
or bool? => get a (numeric|empty) string
or array? => build a string
or if anything else but a string => cast to string

Off-Topic:
shouldn't be better to use `null === $var` / `null !== $var` instead of calling `is_null`. Inside a function that can be called many times it can make a diifference in processing time since calling a function is more expensive, even though i agree to be more consistent with other type-checking.

kind regards
2016-11-07 18:17:43 +01:00
Ryan Cramer
2d9e959bf2 Update InputfieldSelector to support sorted by field labels (rather than names) and support for better multi-language subfield names. 2016-11-06 08:13:34 -05:00
Ryan Cramer
21bf57eb79 Add support for OR-groups in "custom (field=value)" selections in InputfieldSelector. Simply use the existing "or" checkbox to apply the custom selector as an OR group. 2016-11-06 07:23:36 -05:00
teppokoivula
0a12932951 Add support for optional overrides array to $input->queryString() 2016-11-05 14:53:09 +02:00
Ryan Cramer
8d0c8de3ee Bump version to 3.0.40 2016-11-04 11:45:14 -04:00
Ryan Cramer
8bf7b0dc41 Add @pine3ree PR #24 which adds a break statement to a for() loop in WireFileTools.php 2016-11-03 12:18:53 -04:00
Ryan Cramer
29023269a4 Merge branch 'pine3ree-patch-2' into dev 2016-11-03 12:16:05 -04:00
Ryan Cramer
a9a2ff35ac Merge branch 'patch-2' of https://github.com/pine3ree/processwire-1 into pine3ree-patch-2 2016-11-03 12:15:55 -04:00
Ryan Cramer
35639b0f11 Merge branch 'LostKobrakai-feature/min-size-by-aspect' into dev 2016-11-03 12:13:04 -04:00
Ryan Cramer
99008366ba Merge branch 'feature/min-size-by-aspect' of https://github.com/LostKobrakai/processwire-1 into LostKobrakai-feature/min-size-by-aspect 2016-11-03 12:12:44 -04:00
Ryan Cramer
8405c586f0 Add @iamwebrocker PR #18 which adds honeypot option to comments form 2016-11-03 11:58:06 -04:00
Ryan Cramer
9b385168d0 Spelling correction in MarkupRSS.module per @lesaff PR #26 2016-11-03 11:33:17 -04:00
Ryan Cramer
243998039f Add @clsource PR #23 to correct a phpdoc typo in Functions.php 2016-11-03 11:30:48 -04:00
Ryan Cramer
2f66734a6b Add @clsource PR #23 to correct a phpdoc typo in Functions.php 2016-11-03 11:30:03 -04:00
Ryan Cramer
c26095e9e3 Merge branch 'gmclelland-fix-issue-69' into dev 2016-11-03 11:20:25 -04:00
Ryan Cramer
8bc1b51e03 Merge branch 'fix-issue-69' of https://github.com/gmclelland/processwire into gmclelland-fix-issue-69 2016-11-03 11:19:23 -04:00
Ryan Cramer
0fb25668c4 Update SystemUpdater module to remove any session data associated with admin theme when a core update occurs 2016-11-03 11:11:40 -04:00
Ryan Cramer
2135f2ca2e Update AdminThemeReno to remove an extra pixel appearing underneath selected tab in WireTabs 2016-11-03 11:10:46 -04:00
Ryan Cramer
1633b990ca Update several Inputfield modules to remove unnecessary newlines in markup and add phpdocs where appropriate 2016-11-03 11:10:04 -04:00
Ryan Cramer
1c3ea5ce51 Fix issue processwire/processwire-issues#65 modal window on InputfieldForm with HTML5 required inputs 2016-11-03 09:54:58 -04:00
Ryan Cramer
8116a5ba77 Merge branch 'dev-css' into dev-dev 2016-11-02 13:09:31 -04:00
Ryan Cramer
36984e4a05 Updates to legacy code in ProcessTemplate, cleaning things up a bit 2016-11-02 12:27:01 -04:00
Glenn McLelland
2ddb3928bd Used the css function to display the element inline 2016-11-02 11:23:24 -05:00
Glenn McLelland
4eca3fd458 Revert "Added a css fix for issue #69" going to use a different technique.
This reverts commit 66149758d9.
2016-11-02 11:07:27 -05:00
Ryan Cramer
b2a6857561 Update WireTabs module to support automatic tab selection by querystring (i.e. url?WireTab=tab_id). This is in addition to the existing hash url#tab_id option, which seems to have issues in certain cases like that reported in processwire/processwire-issues#71 2016-11-02 10:35:39 -04:00
Benjamin Milde
dc860bc929 Allow min and max dimensions for images to be swapped for portrait images 2016-11-02 12:39:30 +01:00
Ryan Cramer
a864d6a4a1 Fix processwire/processwire-issues#67 where Inputfield::collapsedBlank behavior only applied to default language in multi-language fields 2016-11-02 06:26:39 -04:00
Glenn McLelland
66149758d9 Added a css fix for issue #69 2016-11-01 14:59:06 -05:00
Ryan Cramer
87ea1685b2 Update to convert several css selectors to be "pw-" selectors, as part of longer term goal to work nicely with other css frameworks. 2016-11-01 15:03:41 -04:00
Ryan Cramer
e374bee288 Fix issue processwire/processwire-issues#68 to workaround apparent PHP 7.1 mail() bug 2016-11-01 08:39:09 -04:00
Ryan Cramer
fc8fa08275 Fix processwire/processwire-issues#49 where locked InputfieldPage still loaded all selectable pages when not necessary to do so 2016-11-01 08:29:26 -04:00
Ryan Cramer
2d99624ca7 Fix issue processwire/processwire-issues#52 where InputfieldPageListSelectMultiple module didn't work on "user" template. 2016-11-01 06:56:29 -04:00
Ryan Cramer
78e9b4748d Fix $sanitizer->selectorValue() issue where it filtered out some UTF-8 symbols it doesn't need to per processwire/processwire-issues#54 2016-11-01 06:29:02 -04:00
maks feltrin
83d4788764 allow custom th classes
this allows customization of th elements and when using relative column sizing classes it also allows to avoid repeating those classes in every tbody row.
2016-11-01 02:32:08 +01:00
Ryan Cramer
b50b1f9a28 Update to processwire/processwire-issues#60 for multi-instance support with multi-language page names 2016-10-31 13:49:42 -04:00
Ryan Cramer
c435081050 Add JS events for supporting pw-button-dropdown enabled and disabled states per processwire/processwire-issues#62 2016-10-31 10:36:18 -04:00
Ryan Cramer
6567896a3e Add a since phpdoc to WireInput::requestMethod() per processwire/processwire-issues#64 2016-10-31 10:07:17 -04:00
Ryan Cramer
3705d55177 Depreciate TemplateFile globals options per processwire/processwire-issues#61 2016-10-31 10:03:21 -04:00
Ryan Cramer
ce037f06da Fix issue processwire/processwire-issues#60 where multi-language fields weren't working correctly with multi-instance 2016-10-31 09:30:26 -04:00
Ryan Cramer
0529bcc495 Fix issue processwire/processwire-issues#59 replacing non-multi-instance compatible static vars in WireDatabaseBackup 2016-10-31 06:29:27 -04:00
Ryan Cramer
ca5870b70c Attempt fix for issue #58 where saving user with different parent than default could result in unnecessary pages_parents table updates. 2016-10-31 06:16:46 -04:00
Ryan Cramer
0c84379255 Fix issue with PageRender and cache clearing of single page 2016-10-30 08:00:54 -04:00
Ryan Cramer
f7c613d39d Bump version to 3.0.39 2016-10-28 16:16:13 -04:00
Ryan Cramer
a5b1110c1f Merge working branch to dev 2016-10-28 05:43:08 -04:00
Ryan Cramer
b4f2dda5fa Various updates primarily related to functions API and profiler support. 2016-10-28 05:41:45 -04:00
Ryan Cramer
fa1b58c6a2 Minor adjustments 2016-10-24 04:52:58 -04:00
Ryan Cramer
57fd741232 BUmp version to 3.0.38 2016-10-21 14:14:00 -04:00
Ryan Cramer
c4972556ea Merge branch 'BitPoet-dev-wiremail-nest' into dev 2016-10-21 13:56:06 -04:00
Ryan Cramer
6d99771e71 A few minor changes to the WireMail PR 2016-10-21 13:55:34 -04:00
Ryan Cramer
da5e769e60 Merge branch 'dev-wiremail-nest' of https://github.com/BitPoet/processwire into BitPoet-dev-wiremail-nest 2016-10-21 13:44:55 -04:00
Ryan Cramer
5005dfa259 Add support for common multi-language translations 2016-10-21 13:31:18 -04:00
Chris
264fa2264f Remove leftover test switch that circumvents mb_encode_mimeheader 2016-10-21 13:31:31 +02:00
Ryan Cramer
913201788c Additional updates to TextformatterSmartypants per processwire/processwire-issues#17 2016-10-21 06:28:38 -04:00
Ryan Cramer
131c0c8b4a Update to processwire/processwire-issues#40 ImageInspector per @horst-n 2016-10-21 06:05:02 -04:00
Ryan Cramer
6b96702c14 Fix issue processwire/processwire-issues#57 where Pageimage::maxSize() method was not always working correctly. 2016-10-21 06:01:33 -04:00
Ryan Cramer
2778829df6 Fix issue processwire/processwire-issues#47 2016-10-20 11:46:14 -04:00
Ryan Cramer
efd2289e2a Fix issue processwire/processwire-issues#41 where uploading image in CKEditor image dialog to page other than the one being edited resulted in an uploaded image that had "temp" status 2016-10-20 11:28:06 -04:00
Ryan Cramer
873ec1a06d Upgrade SmartyPants version for TextformatterSmartypants module, plus updates per processwire/processwire-issues#17 2016-10-20 10:28:40 -04:00
Chris
6ae4a5829d Subject wrapping, mbstring support, nested content parts
Support wrapping of long subject headers, using mb_encode_mimeheader if
available, otherwise fall back to an custom encoding/wrapping function
that uses PHP builtins.
Nest plaintext+html bodies in their own multipart-alternative header if
attachments are present so that the display of the html part still has
precedence over the plaintext part.
Put quotes around name parts after quoted-printable encoding of the name
in name+address combinations.
2016-10-20 13:40:53 +02:00
Ryan Cramer
5542b77440 Add profiler to TemplateFile and PageRender 2016-10-20 07:02:24 -04:00
Ryan Cramer
4060934bae Update Modules/ProcessModules to skip showing configurable modules with no visible configuration fields. Plus a few other small additions. 2016-10-20 06:58:28 -04:00
Ryan Cramer
4a26b774bf Bump version for dev branch to 3.0.37 2016-10-14 13:58:24 -04:00
Ryan Cramer
8896f25d14 Adjustment to reduce changes to fix just issues from standard API log usage 2016-10-13 10:44:17 -04:00
Ryan Cramer
596acaa44b Merge branch 'dev' of https://github.com/adrianbj/processwire into adrianbj-dev 2016-10-13 10:23:51 -04:00
Ryan Cramer
36c7b268e1 Another adjustment to PageImage per issue #31 2016-10-13 10:14:47 -04:00
Ryan Cramer
6336fd4438 Attempt fix for issue #31 when Pageimage::hidpiWidth() called with unsupported string argument 2016-10-13 10:05:39 -04:00
Ryan Cramer
b08f3c082e Update install.php per issue #44 2016-10-13 08:20:13 -04:00
Ryan Cramer
d42a03ca57 Fix issue #40 with ImageInspector having issues with some GIF files 2016-10-13 06:08:35 -04:00
Ryan Cramer
5131edf57c Merge branch 'BitPoet-dev-wiremail-utf8' into dev 2016-10-13 05:54:08 -04:00
Ryan Cramer
e043a2b2f1 Merge branch 'dev-wiremail-utf8' of https://github.com/BitPoet/processwire into BitPoet-dev-wiremail-utf8 2016-10-13 05:52:35 -04:00
Ryan Cramer
e2e4817444 Add Pages::savePageOrFieldReady() and Pages::savedPageOrField() hooks per request 2016-10-13 05:51:34 -04:00
Chris
4d5259b11d Support utf8 characters in WireMail
Use quoted printable encoding for text and html body parts.
Also encode the subject and the name parts of from: and to: headers in
quoted printable.
2016-10-12 14:02:28 +02:00
Ryan Cramer
144872bedb Fix issue processwire/processwire-issues#38 2016-10-11 12:04:03 -04:00
Ryan Cramer
7568093ea7 Remove the "None" option for dateInputFormat per issue processwire/processwire-issues#37 2016-10-11 11:49:19 -04:00
Ryan Cramer
67f504834d Fix issue processwire/processwire-issues#34 2016-10-11 11:34:16 -04:00
Ryan Cramer
7aed0416a9 Fix issue processwire/processwire-issues#30 in PageComparison 2016-10-11 11:19:00 -04:00
Ryan Cramer
6b64a480b8 Update to add removal of incompatible sql_mode settings when used with MySQL 5.7.0 and newer, per issue processwire/processwire-issues#28 2016-10-11 11:07:37 -04:00
Ryan Cramer
cd71d45ef4 Update comments in WireMail 2016-10-09 06:34:14 -04:00
Ryan Cramer
1c2d015e33 Bump version to 3.0.36 2016-10-07 11:48:44 -04:00
Ryan Cramer
f62506c782 Some updates to the inline phpdoc documentation 2016-10-07 11:25:03 -04:00
Ryan Cramer
0d6d2ef6e4 Fix issue processwire/processwire-issues#19 with InputfieldPageAutocomplete and selecting homepage, thanks for fix @BitPoet 2016-10-07 11:06:56 -04:00
adrianbj
504d8b8645 Fix entry parts ending up in the wrong column in Log viewer.
This fixes: https://github.com/ryancramerdesign/ProcessWire/issues/1822

This fix includes changes for the count($parts) == 3 section which are necessary, but also includes changes for the count($parts) == 2 section which may be checking for situations that aren't possible, so see what you think about those changes.

I have included a nested ternary, but I think in this case it reads quite clearly and is ok, but you may want to revisit.

I'll add a comment shortly with a test log file so you can see these changes in action.
2016-10-06 19:38:19 -07:00
Ryan Cramer
ba44b4bf20 Fix issue processwire/processwire-issues#24 findPagesSelector containing "=page.id" when used outside original intended context, fix ensures it continues to work without dependent selects 2016-10-06 10:41:59 -04:00
Ryan Cramer
cb3e8f741b Merge branch 'tmpfix' into dev 2016-10-06 10:14:13 -04:00
Ryan Cramer
16a8232dfe Update the Page::matches() method to make it smarter and more flexible. This also accommodates issue #21. 2016-10-06 10:13:14 -04:00
Ryan Cramer
9f6421d672 Remove TinyMCE mention from ProcessPageEditLink.module 2016-10-05 14:36:11 -04:00
Ryan Cramer
3619a4e958 Remove reference to TinyMCE in TextformatterEntities module description per issue processwire/processwire-issues#18 2016-10-05 11:01:36 -04:00
Ryan Cramer
e4ecc5f7ba Merge branch 'horst-n-patch-1' into dev 2016-10-05 10:44:19 -04:00
Ryan Cramer
7f53fbdda0 Minor adjustment to @horst-n PR #14 2016-10-05 10:43:19 -04:00
Ryan Cramer
637afedade Merge branch 'patch-1' of https://github.com/horst-n/processwire-1 into horst-n-patch-1 2016-10-05 10:41:04 -04:00
Ryan Cramer
3267e6ad68 Add @marcus-herrmann PR #1 which improves keyboard accessibility of ProcessPageList, enabling use of spacebar to toggle actions open/closed. 2016-10-05 10:38:45 -04:00
Ryan Cramer
ad5f79559a Merge branch 'feature/mail-attachments' of https://github.com/LostKobrakai/processwire-1 into LostKobrakai-feature/mail-attachments 2016-10-05 06:21:24 -04:00
Ryan Cramer
37feb6e58c Merge branch 'LostKobrakai-patch/fix-composer-json' into dev 2016-10-05 06:07:23 -04:00
Ryan Cramer
7dd53e4abc Merge branch 'patch/fix-composer-json' of https://github.com/LostKobrakai/processwire-1 into LostKobrakai-patch/fix-composer-json 2016-10-05 06:06:53 -04:00
Ryan Cramer
4bc2efa1f7 Merge branch 'adrianbj-master' into dev 2016-10-04 16:42:56 -04:00
Ryan Cramer
d4090a0610 Add Adrian's PR #11 with some minor changes, plus fix issue with installing FieldtypeOptions when using utf8mb4 dbCharset. 2016-10-04 16:40:36 -04:00
Ryan Cramer
8977302991 Merge branch 'master' of https://github.com/adrianbj/processwire into adrianbj-master 2016-10-04 16:12:36 -04:00
Ryan Cramer
d965cbb0ba Establish dev branch 2016-10-04 11:06:46 -04:00
Ryan Cramer
c5eaf61dfc Fix issue processwire/processwire-issues#15 with InputfieldSelector+PageListSelect+parent_id setting showing select rather than autocomplete 2016-10-04 11:01:20 -04:00
Ryan Cramer
c87f30bae8 Update for issue processwire/processwire-issues#14 to update CSS for long description or notes that have no spaces (like URLs or emails) so that they wrap rather than overflow. 2016-10-04 10:03:23 -04:00
Ryan Cramer
e133309068 Fix issue suggestion processwire/processwire-issues#13 to move Helloworld example module into its own directory 2016-10-04 09:38:08 -04:00
Ryan Cramer
b32857592b Fix issue processwire/processwire-issues#9 with notices close button when in modal dialog window interfering with modal in Chrome 2016-10-04 09:31:29 -04:00
Ryan Cramer
8bb80bd487 Fix issue processwire/processwire-issues#8 so that error message is shown if user mismatches fieldset order in ProcessTemplate 2016-10-04 08:12:43 -04:00
Ryan Cramer
b7983870de Fix issue processwire/processwire-issues#6 with styling of open+hidden pages in PageList no longer showing hidden state 2016-10-04 06:23:15 -04:00
Ryan Cramer
9b4fef4357 Fix issue processwire/processwire-issues#2 to support better handling of unpublished pages in certain Page input types when unpublished pages are not allowed in the field. 2016-10-04 06:15:30 -04:00
horst
6e7421214e added support for CLI
without that, PHP throws notices when try to access the $_SERVER['REMOTE_ADDR'], because it is not set in CLI-Environment.
2016-10-03 15:09:50 +02:00
adrianbj
e64352fbcf Make the additional button just Close & Add. Including Save in the action can be confusing as the user won't see any notices from the fieldtype on save. 2016-10-01 18:59:44 -07:00
adrianbj
4ec21e6f6d Make button label simply Save & Add
I think this looks cleaner and is probably explanatory enough.
2016-10-01 12:27:19 -07:00
Benjamin Milde
2fe4ff02f2 Allow for alternative names to be supplied 2016-10-01 20:35:09 +02:00
Benjamin Milde
ea6f60246d Implement attachment support for core WireMail so we can start using a common interface between different wiremail implementations 2016-10-01 20:28:54 +02:00
adrianbj
35e4f7f6a1 Add a separate Save, Close & Add button
This allows for full configuration of fields that require more than two saves, such as FieldtypeOptions.
2016-10-01 07:40:07 -07:00
Benjamin Milde
199101823d Remove minimum-stability from composer.json 2016-09-23 18:02:04 +02:00
1227 changed files with 165887 additions and 24277 deletions

2
.gitattributes vendored Normal file
View File

@@ -0,0 +1,2 @@
$ cat .gitattributes
*.module linguist-language=PHP

View File

@@ -48,7 +48,7 @@ of contributions in different repositories. Please review the instructions for e
## Pull Requests (PRs)
- Pull requests should be submitted to the [processwire](https://github.com/processwire/processwire/pulls)
repository.
repository, and based on the [dev branch](https://github.com/processwire/processwire/tree/dev).
- Before submitting a PR, read the Contributor License Agreement (CLA) at
<https://processwire.com/about/license/cla/> and indicate your agreement (electronic signature)
@@ -63,8 +63,6 @@ of contributions in different repositories. Please review the instructions for e
before submitting a PR. While it's not required that you adhere to the style guide, it does increase
the odds that we may be able to directly merge your PR.
- Please base pull requests off of the latest ProcessWire 3.x development (dev) branch.
- Please only submit code that you feel confident is stable and you have thoroughly tested.
Verbose code comments are also appreciated when possible.

View File

@@ -410,7 +410,7 @@ https://processwire.com/about/license/mit/
The MIT License (MIT)
Copyright (c) 2015 Ryan Cramer <or other year/person if indicated in file>
Copyright (c) 2020 Ryan Cramer <or other year/person if indicated in file>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

157
README.md
View File

@@ -9,54 +9,139 @@ can be read at: https://github.com/processwire/processwire/blob/master/README.md
1. [About](#about-processwire)
2. [Installation](#installing-processwire)
3. [Upgrading](#upgrading-processwire)
4. [Troubleshooting](https://processwire.com/docs/install/troubleshooting/)
4. [Troubleshooting](https://processwire.com/docs/start/install/troubleshooting/)
5. [Support](#support-and-links)
## About ProcessWire
ProcessWire is an open source content management system (CMS) and web
application framework aimed at the needs of designers, developers and their
clients. ProcessWire gives you more control over your fields, templates and
markup than other platforms, and provides a powerful template system that
works the way you do. Not to mention, ProcessWire's API makes working with
your content easy and enjoyable. Managing and developing a site in
ProcessWire is shockingly simple compared to what you may be used to.
ProcessWire is a friendly and powerful open source CMS with an API that is a
joy to use at any scale. It is both a content management system (CMS) and
framework (CMF) built to save you time and work the way you do. With all custom
fields, a secure foundation, proven scalability and performance, ProcessWire
connects all of your content seamlessly, making your job fast, easy and fun.
* [ProcessWire Home](https://processwire.com)
* [API Reference](https://processwire.com/api/ref/)
* [Download](https://processwire.com/download/)
* [Support](https://processwire.com/talk/)
* [Modules/Plugins](http://modules.processwire.com)
ProcessWire gives you more control over your fields, templates and markup than
other platforms, while ProcessWires API makes working with your content easy and
enjoyable. Managing and developing a site in ProcessWire is shockingly simple
compared to what you may be used to.
ProcessWire is widely trusted by web professionals for its exceptional consistency,
stability and security; revered by web developers for its API that saves time and
makes work fun; valued by web designers for its adaptability and flexibility with
modern website/application content management needs; and loved by clients for its
no-nonsense interface and ease-of-use in adding, updating and maintaining content.
New versions of ProcessWire are released just about every week on the
development branch.
### Background
ProcessWire is a timeless tool for web professionals that has always been
committed to the long term. It started in 2003, gained the name ProcessWire
in 2006, and has been in active development as an open source project since 2010.
Now more than a decade later (2020), we're just getting started, as ProcessWire
continues to grow and develop into the next 10 years and beyond.
While ProcessWire has been around for a long time, dont feel bad if you havent
heard of it till today. We are fundamentally different from other projects in
that we dont make a lot of noise, were not into promotion, we value quality
over quantity, sustainability over growth, and a friendly community over
popularity. ProcessWire is designed to be a silent partner, not easily
identified from the front-end of any website. We dont aim to be big, we are
instead focused on being best-in-class.
Web developers find ProcessWire when the time is right, after theyve tried
some other platforms. And once they start using ProcessWire, they tend to
stay—ProcessWire is addictive, easy to maintain for the long term, and doesnt
have the security and upgrade woes of other platforms. But dont take our word
for it; unless your livelihood depends on some other platform, find out for
yourself.
### Community
ProcessWire is more than just software, it is also a friendly community
of web professionals dedicated to building great sites and applications, and
helping others do so too. Please visit and join our
[friendly community](https://processwire.com/talk/)
in the ProcessWire forums, subscribe to our
[weekly newsletter](https://processwire.com/community/newsletter/subscribe/)
for the latest ProcessWire news, check out our
[website showcase](https://processwire.com/sites/)
to see what others are building with ProcessWire, and read our
[blog](https://processwire.com/blog/)
to stay up-to-date with the latest ProcessWire versions.
### Learn more
* [ProcessWire website](https://processwire.com)
* [About ProcessWire](https://processwire.com/about/)
* [Support forums](https://processwire.com/talk/)
* [Documentation](https://processwire.com/docs/)
* [API reference](https://processwire.com/api/ref/)
* [Downloads](https://processwire.com/download/)
* [Modules/plugins](https://modules.processwire.com)
* [Showcase](https://processwire.com/sites/)
-----------------------------------------------------------------
## Installing ProcessWire
Simply extract the ProcessWire files to an http accessible location and
load the URL in your web browser. This will start the installer. See our
[Installation Guide](https://processwire.com/docs/install/new/) for more
[Installation Guide](https://processwire.com/docs/start/install/new/) for more
details and instructions. If you run into any trouble, please see our
[Troubleshooting Guide](https://processwire.com/docs/install/troubleshooting/).
[Troubleshooting Guide](https://processwire.com/docs/start/install/troubleshooting/).
## Upgrading ProcessWire
Before proceeding with any version upgrade, please read the
[Upgrading ProcessWire](https://processwire.com/docs/install/upgrade/)
Before proceeding with any version upgrade, please see the
[Upgrading ProcessWire](https://processwire.com/docs/start/install/upgrade/)
guide and keep it open during your upgrade in case you need to refer back to it.
If upgrading from one 3.x version to another, please use the
[General Upgrade Process](https://processwire.com/docs/install/upgrade/#general-upgrade-process).
Chances are that you can upgrade simply by replacing the /wire/ directory.
### Upgrading from ProcessWire 3.x (earlier version)
When upgrading from one 3.x version to another, please use the
[General Upgrade Process](https://processwire.com/docs/start/install/upgrade/#general-upgrade-process).
This consists primarily of making sure you've got everything backed up and then
just replacing your `/wire/` directory with the one from the newest version.
In addition, if you are currently running any 3.x version prior to 3.0.135,
you will also want to upgrade your root `.htaccess` file to the newest version:
#### Upgrading your .htaccess file
* If you haven't made any custom modifications to your .htaccess file then you
can simply replace the old one with the new one. The new one is in a file
named `htaccess.txt` so you'll rename it to `.htaccess` after removing
your old one (all in the same directory as this README file).
* If your .htaccess file does have custom modifications, you know what they
are, and are comfortable applying them to the new one — go ahead and
follow the step above and then make those same modifications to the new
.htaccess file.
* If you aren't sure what custom modifications your .htaccess file might
have, or how to apply them to the new one, please see this post which will
quickly guide you through it:
[How to upgrade an existing .htaccess file](https://processwire.com/blog/posts/pw-3.0.135/#how-to-update-an-existing-htaccess-file)
*If you are curious what's new in this latest .htaccess file version,
please see [this post](https://processwire.com/blog/posts/pw-3.0.135/)
for all the details.*
### Upgrading from ProcessWire 2.x
If upgrading from ProcessWire 2.5 or older, we recommend that you upgrade
to ProcessWire [2.8](https://github.com/processwire/processwire-legacy) or
[2.7](https://github.com/ryancramerdesign/processwire) first. Both of those
versions include details in the README file on how to upgrade from these
older versions of ProcessWire. To upgrade from ProcessWire 2.6 (or newer)
to ProcessWire [2.7](https://github.com/ryancramerdesign/processwire) first.
This version includes details in the README file on how to upgrade from that
older version of ProcessWire. To upgrade from ProcessWire 2.6 (or newer)
to ProcessWire 3.x, please follow the instructions below.
1. Login to the admin of your site.
@@ -85,19 +170,19 @@ to ProcessWire 3.x, please follow the instructions below.
`$config->debug` setting back to `false` in your /site/config.php file.
**Troubleshooting a 3.x upgrade**
If you run into any trouble upgrading, please see our troubleshooting guide
located at <https://processwire.com/download/troubleshooting/#upgrades>.
If you run into any trouble upgrading, please see our
[troubleshooting upgrades guide](https://processwire.com/docs/start/install/troubleshooting/#troubleshooting-upgrades).
### Pro module upgrade notes
- If using [FormBuilder](https://processwire.com/api/modules/form-builder/),
we recommend using only v0.3.0 or newer.
- If using [ProCache](https://processwire.com/api/modules/procache/),
- If using [FormBuilder](https://processwire.com/store/form-builder/),
we recommend using only v0.3.0 or newer, but v0.4.0 or newer if possible.
- If using [ProCache](https://processwire.com/store/pro-cache/),
we recommend using only v3.1.4 or newer.
- If using [ListerPro](https://processwire.com/api/modules/lister-pro/),
- If using [ListerPro](https://processwire.com/store/lister-pro/),
we recommend using only v1.0.9 or newer.
- If using [ProFields](https://processwire.com/api/modules/profields/),
- If using [ProFields](https://processwire.com/store/pro-fields/),
we recommend grabbing the latest versions in the ProFields support board.
- If using ProCache and you upgraded your .htaccess file, you should
go to your ProCache settings after the upgrade to have it update
@@ -131,14 +216,16 @@ resolved any issues.
## Support and Links
* [ProcessWire Support](https://processwire.com/talk/)
* [ProcessWire Weekly](https://weekly.pw/)
* [ProcessWire Support Forums](https://processwire.com/talk/)
* [ProcessWire Weekly News](https://weekly.pw/)
* [ProcessWire Blog](https://processwire.com/blog/)
* [Sites running ProcessWire](https://processwire.com/sites/)
* [Subscribe to ProcessWire Weekly email](https://processwire.com/community/newsletter/subscribe/)
* [Submit your site to our directory](https://processwire.com/sites/submit/)
* [Follow @processwire on Twitter](http://twitter.com/processwire/)
* [Contact ProcessWire](https://processwire.com/contact/)
* [Sites running ProcessWire](https://processwire.com/about/sites/)
------
Copyright 2016 by Ryan Cramer / Ryan Cramer Design, LLC
Copyright 2020 by Ryan Cramer / Ryan Cramer Design, LLC

View File

@@ -19,6 +19,5 @@
},
"autoload": {
"files": [ "wire/core/ProcessWire.php" ]
},
"minimum-stability": "dev"
}
}

View File

@@ -1,26 +1,78 @@
#################################################################################################
# START PROCESSWIRE HTACCESS DIRECTIVES
# @version 3.0
# @indexVersion 300
# @htaccessVersion 301
#################################################################################################
#
# Upgrading htaccess (or index) version 300 to 301
# -----------------------------------------------------------------------------------------------
# If you never modified your previous .htaccess file, then you can simply replace it with this
# one. If you have modified your .htaccess file, then you will want to copy/paste some updates
# to the old one instead:
# If your htaccess/index version is 300, upgrade to this version by replacing all of sections #5
# and #15 (Access Restrictions). Also take a look at section #9, which you might also consider
# replacing if using HTTPS, though it is not required. (For instance, HSTS might be worthwhile)
#
# Following that, optionally review the rest of the file to see if there are any other changes
# you also want to apply. Sections tagged "(v301)" are new or have significant changes.
#
# When finished, add a line at the top identical to the "htaccessVersion 301" that you see at
# the top of this file. This tells ProcessWire your .htaccess file is up-to-date.
#
# Resolving 500 errors
# -----------------------------------------------------------------------------------------------
# Depending on your server, some htaccess rules may not be compatible and result in a 500 error.
# If you experience this, find all instances of the term "(500)" in this file for suggestions on
# things you can change to resolve 500 errors.
#
# Optional features
# -----------------------------------------------------------------------------------------------
# Many of the rules in this .htaccess file are optional and commented out by default. While the
# defaults are okay for many, you may want to review each section in this .htaccess file for
# optional rules that you can enable to increase security, speed or best practices. To quickly
# locate all optional rules, search this file for all instances of "(O)".
#
# If using a load balancer
# -----------------------------------------------------------------------------------------------
# If using a load balancer (like those available from AWS) some htaccess rules will need to
# change. Search this file for instances of "(L)" for details.
#
# -----------------------------------------------------------------------------------------------
# 1. Don't show directory indexes, but do follow symbolic links
# 500 NOTE: Some cloud hosting companies don't allow +FollowSymLinks.
# Uncomment +SymLinksifOwnerMatch and comment +FollowSymLinks if you have 500 errors.
# If that doesn't resolve the error, then set it back to +FollowSymLinks.
# 1. Apache Options
#
# Note: If you experience a (500) error, it may indicate your host does not allow setting one or
# more of these options. First try replacing the +FollowSymLinks with +SymLinksifOwnerMatch.
# If that does not work, try commenting them all out, then uncommenting one at a time to
# determine which one is the source of the 500 error.
# -----------------------------------------------------------------------------------------------
# Do not show directory indexes (strongly recommended)
Options -Indexes
# Do not use multiviews (v301)
Options -MultiViews
# Do follow symbolic links
Options +FollowSymLinks
# Options +SymLinksifOwnerMatch
# Character encoding: Serve text/html or text/plain as UTF-8
AddDefaultCharset UTF-8
# -----------------------------------------------------------------------------------------------
# 2. Let ProcessWire handle 404s
# 2. ErrorDocument settings: Have ProcessWire handle 404s
#
# For options and optimizations (O) see:
# https://processwire.com/blog/posts/optimizing-404s-in-processwire/
# -----------------------------------------------------------------------------------------------
ErrorDocument 404 /index.php
# -----------------------------------------------------------------------------------------------
# 3. Handle request for missing favicon.ico/robots.txt files (no ending quote for Apache 1.3)
# -----------------------------------------------------------------------------------------------
@@ -33,6 +85,7 @@ ErrorDocument 404 /index.php
ErrorDocument 404 "The requested file robots.txt was not found.
</Files>
# -----------------------------------------------------------------------------------------------
# 4. Protect from XSS with Apache headers
# -----------------------------------------------------------------------------------------------
@@ -42,18 +95,25 @@ ErrorDocument 404 /index.php
# you will need to remove this one if you want to allow external iframes
Header always append X-Frame-Options SAMEORIGIN
# to prevent cross site scripting (IE8+ proprietary)
# To prevent cross site scripting (IE8+ proprietary)
Header set X-XSS-Protection "1; mode=block"
# prevent mime-based attacks via content sniffing (IE+Chrome)
# Optionally (O) prevent mime-based attacks via content sniffing (IE+Chrome)
# Header set X-Content-Type-Options "nosniff"
</IfModule>
# -----------------------------------------------------------------------------------------------
# 5. Protect ProcessWire system files
# 5. Prevent access to various types of files (v301)
#
# Note that some of these rules are duplicated by RewriteRules or other .htaccess files, as we
# try to maintain two layers of protection when/where possible.
# -----------------------------------------------------------------------------------------------
<FilesMatch "\.(inc|info|info\.json|module|sh|sql)$|^\..*$|composer\.(json|lock)$">
# 5A. Block access to inc, info, info.json/php, module/php, sh, sql and composer files
# -----------------------------------------------------------------------------------------------
<FilesMatch "\.(inc|info|info\.(json|php)|module|module\.php|sh|sql)$|^\..*$|composer\.(json|lock)$">
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
@@ -62,9 +122,23 @@ ErrorDocument 404 /index.php
</IfModule>
</FilesMatch>
# 5B. Block bak, conf, dist, ini, log, orig, sh, sql, swo, swp, ~, and more
# -----------------------------------------------------------------------------------------------
<FilesMatch "(^#.*#|\.(bak|conf|dist|in[ci]|log|orig|sh|sql|sw[op])|~)$">
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order allow,deny
</IfModule>
</FilesMatch>
# -----------------------------------------------------------------------------------------------
# 6. Override a few PHP settings that can't be changed at runtime (not required)
# 500 NOTE: Try commenting out this entire section below if getting Apache 500 errors.
# Note: try commenting out this entire section below if getting Apache (500) errors.
# -----------------------------------------------------------------------------------------------
<IfModule mod_php5.c>
@@ -73,63 +147,137 @@ ErrorDocument 404 /index.php
php_flag register_globals off
</IfModule>
# -----------------------------------------------------------------------------------------------
# 7. Set default directory index files
# -----------------------------------------------------------------------------------------------
DirectoryIndex index.php index.html index.htm
# -----------------------------------------------------------------------------------------------
# 8. ProcessWire requires mod_rewrite
# 8. Enable Apache mod_rewrite (required)
# -----------------------------------------------------------------------------------------------
<IfModule mod_rewrite.c>
RewriteEngine On
AddDefaultCharset UTF-8
# 8A. Optionally (O) set a rewrite base if rewrites are not working properly on your server.
# -----------------------------------------------------------------------------------------------
# In addition, if your site directory starts with a "~" you will most likely have to use this.
# https://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritebase
# Examples of RewriteBase (root and subdirectories):
# RewriteBase /
# RewriteBase /pw/
# RewriteBase /~user/
# 8B. Set an environment variable so the installer can detect that mod_rewrite is active.
# -----------------------------------------------------------------------------------------------
# 9. If you only want to allow HTTPS, uncomment the RewriteCond and RewriteRule lines below.
# -----------------------------------------------------------------------------------------------
# RewriteCond %{HTTPS} off
# RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# -----------------------------------------------------------------------------------------------
# 10. Set an environment variable so the installer can detect that mod_rewrite is active.
# Note that some web hosts don't support this. If you get a 500 error, you might try
# commenting out this SetEnv line below.
# -----------------------------------------------------------------------------------------------
# Note that some web hosts don't support this. If you get a (500) error, try commenting out this
# SetEnv line below.
<IfModule mod_env.c>
SetEnv HTTP_MOD_REWRITE On
</IfModule>
# -----------------------------------------------------------------------------------------------
# 11. OPTIONAL: Set a rewrite base if rewrites aren't working properly on your server.
# And if your site directory starts with a "~" you will most likely have to use this.
# -----------------------------------------------------------------------------------------------
# RewriteBase /
# RewriteBase /pw/
# RewriteBase /~user/
# -----------------------------------------------------------------------------------------------
# 12. Access Restrictions: Keep web users out of dirs that begin with a period,
# 9. Optionally Force HTTPS (O)
# -----------------------------------------------------------------------------------------------
# 9A. To redirect HTTP requests to HTTPS, uncomment the lines below:
# -----------------------------------------------------------------------------------------------
# RewriteCond %{HTTPS} !=on
# RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# 9B. If using load balancer/AWS, use the following rather than 9A above: (L)
# -----------------------------------------------------------------------------------------------
# RewriteCond %{HTTP:X-Forwarded-Proto} =http
# RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# 9C. If using cPanel AutoSSL or Let's Encrypt webroot you may need to MOVE one of the below
# lines after the first RewriteCond in 9A or 9B to allow certificate validation:
# -----------------------------------------------------------------------------------------------
# RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/
# RewriteCond %{REQUEST_URI} !^/\.well-known/cpanel-dcv/[\w-]+$
# RewriteCond %{REQUEST_URI} !^/\.well-known/pki-validation/[A-F0-9]{32}\.txt(?:\ Comodo\ DCV)?$
# 9D. Store current scheme in a 'proto' environment variable for later use in this file
# -----------------------------------------------------------------------------------------------
RewriteCond %{HTTPS} =on
RewriteRule ^ - [env=proto:https]
RewriteCond %{HTTPS} !=on
RewriteRule ^ - [env=proto:http]
# 9E. If using Load balancer/AWS- Use lines below rather than 9D: (L)
# -----------------------------------------------------------------------------------------------
# RewriteCond %{HTTP:X-Forwarded-Proto} =https
# RewriteRule ^ - [env=proto:https]
# RewriteCond %{HTTP:X-Forwarded-Proto} =http
# RewriteRule ^ - [env=proto:http]
# 9F. Tell web browsers to only allow access via HSTS: Strict-Transport-Security (O) (v301)
# -----------------------------------------------------------------------------------------------
# This forces client-side SSL redirection. Before enabling be absolutely certain you can
# always serve via HTTPS because it becomes non-revokable for the duration of your max-age.
# See link below for details and options (note 'max-age=31536000' is 1-year):
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
<IfModule mod_headers.c>
# Uncomment one (1) line below & adjust as needed to enable Strict-Transport-Security (HSTS):
# Header always set Strict-Transport-Security "max-age=31536000;"
# Header always set Strict-Transport-Security "max-age=31536000; includeSubdomains"
# Header always set Strict-Transport-Security "max-age=31536000; preload"
# Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
</IfModule>
# Sections 10 and 11 intentionally omitted
# -----------------------------------------------------------------------------------------------
# 12. Access Restrictions: Keep web users out of dirs or files that begin with a period,
# but let services like Lets Encrypt use the webroot authentication method.
# -----------------------------------------------------------------------------------------------
RewriteRule "(^|/)\.(?!well-known)" - [F]
# -----------------------------------------------------------------------------------------------
# 13. OPTIONAL: Redirect users to the 'www.' version of the site (uncomment to enable).
# For example: http://processwire.com/ would be redirected to http://www.processwire.com/
# 13. Optional domain redirects (O)
#
# Redirect domain.com to www.domain.com redirect (or www to domain.com redirect).
# If using then uncomment either 13A or 13B, do NOT uncomment both of them or nothing will work.
# -----------------------------------------------------------------------------------------------
# 13A. Redirect domain.com and *.domain.com to www.domain.com (do not combine with 13B):
# -----------------------------------------------------------------------------------------------
# RewriteCond %{HTTP_HOST} !^www\. [NC]
# RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# RewriteCond %{SERVER_ADDR} !=127.0.0.1
# RewriteCond %{SERVER_ADDR} !=::1
# RewriteRule ^ %{ENV:PROTO}://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# 13B. Redirect www.domain.com to domain.com (do not combine with 13A):
# -----------------------------------------------------------------------------------------------
# RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
# RewriteRule ^ %{ENV:PROTO}://%1%{REQUEST_URI} [R=301,L]
# -----------------------------------------------------------------------------------------------
# 14. OPTIONAL: Send URLs with non-ASCII name-format characters to 404 page (optimization)
# 14. Optionally send URLs with non-ASCII name-format characters to 404 page (optimization).
#
# This ensures that ProcessWire does not spend time processing URLs that we know ahead of time
# are going to result in 404s. Uncomment lines below to enable. (O)
# -----------------------------------------------------------------------------------------------
# RewriteCond %{REQUEST_URI} "[^-_.a-zA-Z0-9/~]"
@@ -137,49 +285,89 @@ DirectoryIndex index.php index.html index.htm
# RewriteCond %{REQUEST_FILENAME} !-d
# RewriteRule ^(.*)$ index.php?it=/http404/ [L,QSA]
# -----------------------------------------------------------------------------------------------
# 15. Access Restrictions: Protect ProcessWire system files
# 15. Access Restrictions (v301)
# -----------------------------------------------------------------------------------------------
# 15A. Keep http requests out of specific files and directories
# -----------------------------------------------------------------------------------------------
# Allow screenshot files (for install.php only: this 1 line below may be removed after install)
RewriteCond %{REQUEST_URI} !(^|/)site-[^/]+/install/[^/]+\.(jpg|jpeg|png|gif)$
# Prevent all the following rules from blocking images in site install directories
RewriteCond %{REQUEST_URI} !(^|/)site-[^/]+/install/[^/]+\.(jpg|jpeg|png|gif|webp|svg)$
# Block access to any htaccess files
RewriteCond %{REQUEST_URI} (^|/)\.htaccess$ [NC,OR]
# Block access to protected assets directories
RewriteCond %{REQUEST_URI} (^|/)(site|site-[^/]+)/assets/(cache|logs|backups|sessions|config|install|tmp)($|/.*$) [OR]
# Block acceess to the /site/install/ directory
RewriteCond %{REQUEST_URI} (^|/)(site|site-[^/]+)/install($|/.*$) [OR]
# Block dirs in /site/assets/ dirs that start with a hyphen
RewriteCond %{REQUEST_URI} (^|/)(site|site-[^/]+)/assets.*/-.+/.* [OR]
# Block access to /wire/config.php, /site/config.php, /site/config-dev.php, and /wire/index.config.php
RewriteCond %{REQUEST_URI} (^|/)(wire|site|site-[^/]+)/(config|index\.config|config-dev)\.php$ [OR]
# Block access to any PHP-based files in /templates-admin/
RewriteCond %{REQUEST_URI} (^|/)(wire|site|site-[^/]+)/templates-admin($|/|/.*\.(php|html?|tpl|inc))$ [OR]
# Block access to any PHP or markup files in /site/templates/
RewriteCond %{REQUEST_URI} (^|/)(site|site-[^/]+)/templates($|/|/.*\.(php|html?|tpl|inc))$ [OR]
# Block access to any PHP files in /site/assets/
RewriteCond %{REQUEST_URI} (^|/)(site|site-[^/]+)/assets($|/|/.*\.php)$ [OR]
# Block access to any PHP files in core or core module directories
RewriteCond %{REQUEST_URI} (^|/)wire/(core|modules)/.*\.(php|inc|tpl|module|info\.json)$ [OR]
# Block access to any PHP files in /site/modules/
RewriteCond %{REQUEST_URI} (^|/)(site|site-[^/]+)/modules/.*\.(php|inc|tpl|module|info\.json)$ [OR]
# Block access to any software identifying txt files
RewriteCond %{REQUEST_URI} (^|/)(COPYRIGHT|INSTALL|README|htaccess)\.(txt|md|textile)$ [OR]
RewriteCond %{REQUEST_URI} (^|/)(\.htaccess|htaccess\..*)$ [NC,OR]
# Block access to various assets directories
RewriteCond %{REQUEST_URI} (^|/)(site|site-[^/]+)/assets/(cache|logs|backups|sessions|config|install|tmp)($|/.*$) [NC,OR]
# Block access to the /site/install/ directories
RewriteCond %{REQUEST_URI} (^|/)(site|site-[^/]+)/install($|/.*$) [NC,OR]
# Block dirs in /site/assets/dirs that start with a hyphen (see config.pagefileSecure)
RewriteCond %{REQUEST_URI} (^|/)(site|site-[^/]+)/assets.*/-.+/.* [NC,OR]
# Block access to /wire/config.php, /site/config.php, /site/config-dev.php, /wire/index.config.php, etc.
RewriteCond %{REQUEST_URI} (^|/)(wire|site|site-[^/]+)/(config|index\.config|config-dev)\.php$ [NC,OR]
# Block access to any PHP-based files in /site/templates-admin/ or /wire/templates-admin/
RewriteCond %{REQUEST_URI} (^|/)(wire|site|site-[^/]+)/templates-admin($|/|/.*\.(php|html?|tpl|inc))$ [NC,OR]
# Block access to any PHP or markup files in /site/templates/ or /site-*/templates/
RewriteCond %{REQUEST_URI} (^|/)(site|site-[^/]+)/templates($|/|/.*\.(php|html?|tpl|inc))$ [NC,OR]
# Block access to any PHP files within /site/assets/ and further
RewriteCond %{REQUEST_URI} (^|/)(site|site-[^/]+)/assets($|/|/.*\.php)$ [NC,OR]
# Block access to any PHP, module, inc or info files in core or core modules directories
RewriteCond %{REQUEST_URI} (^|/)wire/(core|modules)/.*\.(php|inc|tpl|module|info\.json)$ [NC,OR]
# Block access to any PHP, tpl or info.json files in /site/modules/ or /site-*/modules/
RewriteCond %{REQUEST_URI} (^|/)(site|site-[^/]+)/modules/.*\.(php|inc|tpl|module|info\.json)$ [NC,OR]
# Block access to any software identifying txt, markdown or textile files
RewriteCond %{REQUEST_URI} (^|/)(COPYRIGHT|INSTALL|README|htaccess)\.(txt|md|textile)$ [NC,OR]
# Block potential arbitrary backup files within site directories for things like config
RewriteCond %{REQUEST_URI} (^|/)(site|site-[^/]+)/(config[^/]*/?|[^/]+\.php.*)$ [NC,OR]
# Block access throughout to temporary files ending with tilde created by certain editors
RewriteCond %{REQUEST_URI} \.(html?|inc|json|lock|module|php|py|rb|sh|sql|tpl|tmpl|twig)~$ [NC,OR]
# Block access to names of potential backup file extensions within wire or site directories
RewriteCond %{REQUEST_URI} (^|/)(wire/|site[-/]).+\.(bak|old|sql|sw[op]|(bak|php|sql)[./]+.*)[\d.]*$ [NC,OR]
# Block all http access to the default/uninstalled site-default directory
RewriteCond %{REQUEST_URI} (^|/)site-default/
# If any conditions above match, issue a 403 forbidden
RewriteRule ^.*$ - [F,L]
# 15B. Block archive file types commonly used for backup purposes (O)
# -----------------------------------------------------------------------------------------------
# This blocks requests for zip, rar, tar, gz, and tgz files that are sometimes left on servers
# as backup files, and thus can be problematic for security. This rule blocks those files
# unless they are located within the /site/assets/files/ directory. This is not enabled by
# default since there are many legitimate use cases for these files, so uncomment the lines
# below if you want to enable this.
# RewriteCond %{REQUEST_URI} \.(zip|rar|tar|gz|tgz)$ [NC]
# RewriteCond %{REQUEST_URI} !(^|/)(site|site-[^/]+)/assets/files/\d+/ [NC]
# RewriteRule ^.*$ - [F,L]
# PW-PAGENAME
# -----------------------------------------------------------------------------------------------
# 16a. Ensure that the URL follows the name-format specification required by PW
# 16A. Ensure that the URL follows the name-format specification required by PW
# See also directive 16b below, you should choose and use either 16a or 16b.
# -----------------------------------------------------------------------------------------------
RewriteCond %{REQUEST_URI} "^/~?[-_.a-zA-Z0-9/]*$"
# -----------------------------------------------------------------------------------------------
# 16b. Alternative name-format specification for UTF8 page name support.
# 16B. Alternative name-format specification for UTF8 page name support. (O)
# If used, comment out section 16a above and uncomment the directive below. If you have updated
# your $config->pageNameWhitelist make the characters below consistent with that.
# -----------------------------------------------------------------------------------------------
@@ -197,25 +385,30 @@ DirectoryIndex index.php index.html index.htm
RewriteCond %{REQUEST_FILENAME} !(favicon\.ico|robots\.txt)
# -----------------------------------------------------------------------------------------------
# 18. OPTIONAL: Prevent ProcessWire from attempting to serve images or anything in /site/assets/.
# 18. Optionally (O) prevent PW from attempting to serve images or anything in /site/assets/.
# Both of these lines are optional, but can help to reduce server load. However, they
# are not compatible with the $config->pagefileSecure option (if enabled) and they
# may produce an Apache 404 rather than your regular 404. You may uncomment the two lines
# below if you don't need to use the $config->pagefileSecure option.
# below if you don't need to use the $config->pagefileSecure option. After uncommenting, test
# a URL like domain.com/site/assets/files/test.jpg to make sure you are getting a 404 and not
# your homepage. If getting your homepage, then either: do not use this option, or comment out
# section #2 above that makes ProcessWire the 404 handler.
# -----------------------------------------------------------------------------------------------
# RewriteCond %{REQUEST_FILENAME} !\.(jpg|jpeg|gif|png|ico)$ [NC]
# RewriteCond %{REQUEST_FILENAME} !\.(jpg|jpeg|gif|png|ico|webp|svg)$ [NC]
# RewriteCond %{REQUEST_FILENAME} !(^|/)site/assets/
# -----------------------------------------------------------------------------------------------
# 19. Pass control to ProcessWire if all the above directives allow us to this point.
# For regular VirtualHosts (most installs)
# -----------------------------------------------------------------------------------------------
RewriteRule ^(.*)$ index.php?it=$1 [L,QSA]
# -----------------------------------------------------------------------------------------------
# 20. If using VirtualDocumentRoot (500 NOTE): comment out the one above and use this one instead
# 20. If using VirtualDocumentRoot (500): comment out the one above and use this one instead
# -----------------------------------------------------------------------------------------------
# RewriteRule ^(.*)$ /index.php?it=$1 [L,QSA]
</IfModule>

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,7 @@
* This file is licensed under the MIT license
* https://processwire.com/about/license/mit/
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2019 by Ryan Cramer
* https://processwire.com
*
*/
@@ -26,17 +26,7 @@ if(!defined("PROCESSWIRE")) die();
/*** SITE CONFIG *************************************************************************/
/**
* Enable debug mode?
*
* Debug mode causes additional info to appear for use during dev and debugging.
* This is almost always recommended for sites in development. However, you should
* always have this disabled for live/production sites.
*
* @var bool
*
*/
$config->debug = false;
/** @var Config $config */
/**
* Prepend template file
@@ -49,5 +39,19 @@ $config->debug = false;
*/
$config->prependTemplateFile = '_init.php';
/**
* Allow core API variables to also be accessed as functions?
*
* Recommended. This enables API varibles like $pages to also be accessed as pages(),
* as an example. And so on for most other core variables.
*
* Benefits are better type hinting, always in scope, and potentially shorter API calls.
* See the file /wire/core/FunctionsAPI.php for details on these functions.
*
* @var bool
*
*/
$config->useFunctionsAPI = true;
/*** INSTALLER CONFIG ********************************************************************/

View File

@@ -1,9 +1,9 @@
</div><!--/#main-->
</main>
<!-- footer -->
<footer id='footer'>
<footer id='footer' role="contentinfo">
<p>
Powered by <a href='http://processwire.com'>ProcessWire CMS</a> &nbsp; / &nbsp;
<?php

View File

@@ -22,7 +22,7 @@ function renderNav(PageArray $items) {
if(!$items->count()) return;
echo "<ul class='nav'>";
echo "<ul class='nav' role='navigation'>";
// cycle through all the items
foreach($items as $item) {
@@ -30,7 +30,7 @@ function renderNav(PageArray $items) {
// render markup for each navigation item as an <li>
if($item->id == wire('page')->id) {
// if current item is the same as the page being viewed, add a "current" class to it
echo "<li class='current'>";
echo "<li class='current' aria-current='true'>";
} else {
// otherwise just a regular list item
echo "<li>";
@@ -67,15 +67,16 @@ function renderNavTree($items, $maxDepth = 3) {
// $out is where we store the markup we are creating in this function
// start our <ul> markup
echo "<ul class='nav nav-tree'>";
echo "<ul class='nav nav-tree' role='navigation'>";
// cycle through all the items
foreach($items as $item) {
// markup for the list item...
// if current item is the same as the page being viewed, add a "current" class to it
// if current item is the same as the page being viewed, add a "current" class and
// visually hidden text for screen readers to it
if($item->id == wire('page')->id) {
echo "<li class='current'>";
echo "<li class='current' aria-current='true'><span class='visually-hidden'>Current page: </span>";
} else {
echo "<li>";
}

View File

@@ -11,7 +11,7 @@
<body class='has-sidebar'>
<!-- top navigation -->
<ul class='topnav'><?php
<ul class='topnav' role='navigation'><?php
// top navigation consists of homepage and its visible children
$homepage = $pages->get('/');
@@ -25,7 +25,7 @@
if($child->id == $page->rootParent->id) {
// this $child page is currently being viewed (or one of it's children/descendents)
// so we highlight it as the current page in the navigation
echo "<li class='current'><a href='$child->url'>$child->title</a></li>";
echo "<li class='current' aria-current='true'><span class='visually-hidden'>Current page: </span><a href='$child->url'>$child->title</a></li>";
} else {
echo "<li><a href='$child->url'>$child->title</a></li>";
}
@@ -40,12 +40,13 @@
<!-- search form -->
<form class='search' action='<?php echo $pages->get('template=search')->url; ?>' method='get'>
<input type='text' name='q' placeholder='Search' value='' />
<button type='submit' name='submit'>Search</button>
<label for='search' class='visually-hidden'>Search:</label>
<input type='text' name='q' id='search' placeholder='Search' value='' />
<button type='submit' name='submit' class='visually-hidden'>Search</button>
</form>
<!-- breadcrumbs -->
<div class='breadcrumbs'><?php
<div class='breadcrumbs' role='navigation' aria-label='You are here:'><?php
// breadcrumbs are the current page's parents
foreach($page->parents() as $item) {
@@ -56,5 +57,5 @@
?></div>
<div id='main'>
<main id='main'>

View File

@@ -23,7 +23,7 @@ include('./_head.php'); // include header markup ?>
?></div><!-- end content -->
<div id='sidebar'><?php
<aside id='sidebar'><?php
// rootParent is the parent page closest to the homepage
// you can think of this as the "section" that the user is in
@@ -40,6 +40,6 @@ include('./_head.php'); // include header markup ?>
// output sidebar text if the page has it
echo $page->sidebar;
?></div><!-- end sidebar -->
?></aside><!-- end sidebar -->
<?php include('./_foot.php'); // include footer markup ?>

View File

@@ -6,6 +6,7 @@
* 3. Main content and sidebar
* 4. Footer
* 5. Media queries for responsive layout
* 6. Accessibility helpers
*
*/
@@ -124,9 +125,6 @@ form.search {
border: 1px solid #ccc;
width: 100%;
}
form.search button {
display: none;
}
.breadcrumbs {
clear: both;
@@ -291,3 +289,43 @@ figure figcaption {
font-size: 115%;
}
}
/*********************************************************************
* 6. Accessibility helpers
*
*/
/* Hide visually, but remain approachable for screenreader */
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
white-space: nowrap;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
border: 0;
}
/* Show bypass link on hover */
.element-focusable:focus {
clip: auto;
overflow: visible;
height: auto;
}
/* Sample styling for bypass link */
.bypass-to-main:focus {
top: 0;
left: 0;
width: 100%;
height: 40px;
line-height: 40px;
text-align: center;
background: #333;
color: #fff;
}

View File

@@ -17,7 +17,7 @@
* This file is licensed under the MIT license
* https://processwire.com/about/license/mit/
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2019 by Ryan Cramer
* https://processwire.com
*
*/
@@ -26,17 +26,21 @@ if(!defined("PROCESSWIRE")) die();
/*** SITE CONFIG *************************************************************************/
/** @var Config $config */
/**
* Enable debug mode?
* Allow core API variables to also be accessed as functions?
*
* Debug mode causes additional info to appear for use during dev and debugging.
* This is almost always recommended for sites in development. However, you should
* always have this disabled for live/production sites.
* Recommended. This enables API varibles like $pages to also be accessed as pages(),
* as an example. And so on for most other core variables.
*
* Benefits are better type hinting, always in scope, and potentially shorter API calls.
* See the file /wire/core/FunctionsAPI.php for details on these functions.
*
* @var bool
*
*/
$config->debug = false;
$config->useFunctionsAPI = true;
/*** INSTALLER CONFIG ********************************************************************/

View File

@@ -17,7 +17,7 @@
* This file is licensed under the MIT license
* https://processwire.com/about/license/mit/
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2019 by Ryan Cramer
* https://processwire.com
*
*/
@@ -26,17 +26,21 @@ if(!defined("PROCESSWIRE")) die();
/*** SITE CONFIG *************************************************************************/
/** @var Config $config */
/**
* Enable debug mode?
* Allow core API variables to also be accessed as functions?
*
* Debug mode causes additional info to appear for use during dev and debugging.
* This is almost always recommended for sites in development. However, you should
* always have this disabled for live/production sites.
* Recommended. This enables API varibles like $pages to also be accessed as pages(),
* as an example. And so on for most other core variables.
*
* Benefits are better type hinting, always in scope, and potentially shorter API calls.
* See the file /wire/core/FunctionsAPI.php for details on these functions.
*
* @var bool
*
*/
$config->debug = false;
$config->useFunctionsAPI = true;
/*** INSTALLER CONFIG ********************************************************************/

View File

@@ -6,7 +6,7 @@
if($page->numChildren) {
echo "<ul class='nav'>";
echo "<ul class='nav' role='navigation'>";
foreach($page->children as $child) {
echo "<li><p><a href='{$child->url}'>{$child->title}</a><br />";
@@ -24,7 +24,7 @@ if($page->numChildren) {
</div><!--/content-->
<div id="footer" class="footer">
<div id="footer" class="footer" role="contentinfo">
<div class="container">
<p>Powered by <a href='http://processwire.com'>ProcessWire Open Source CMS/CMF</a></p>
</div>

View File

@@ -32,6 +32,8 @@
</head>
<body>
<a href="#bodycopy" class="visually-hidden element-focusable bypass-to-main">Skip to content</a>
<p id='bgtitle'><?php
// print the section title as big faded text that appears near the top left of the page
@@ -45,11 +47,12 @@
<a href='<?php echo $config->urls->root; ?>'><p id='logo'>ProcessWire</p></a>
<ul id='topnav'><?php
<ul id='topnav' role='navigation'><?php
// Create the top navigation list by listing the children of the homepage.
// If the section we are in is the current (identified by $page->rootParent)
// then note it with <a class='on'> so we can style it differently in our CSS.
// then note it with <a class='on'> so we can style it differently in our CSS
// and add a text that is visually hidden, but available for screen readers.
// In this case we also want the homepage to be part of our top navigation,
// so we prepend it to the pages we cycle through:
@@ -58,19 +61,24 @@
$children->prepend($homepage);
foreach($children as $child) {
if ($child === $page->rootParent) {
$class = " class='on'";
$indicator = "<span class='visually-hidden'>Current page: </span>";
$ariaState = " aria-current='true' ";
}
$class = $child === $page->rootParent ? " class='on'" : '';
echo "<li><a$class href='{$child->url}'>{$child->title}</a></li>";
echo "<li><a$class$ariaState href='{$child->url}'>$indicator{$child->title}</a></li>";
}
?></ul>
<ul id='breadcrumb'><?php
<ul id='breadcrumb' role='navigation' aria-label='You are here:'><?php
// Create breadcrumb navigation by cycling through the current $page's
// parents in order, linking to each:
foreach($page->parents as $parent) {
echo "<li><a href='{$parent->url}'>{$parent->title}</a> &gt; </li>";
echo "<li><a href='{$parent->url}'>{$parent->title}</a> <span class='visually-hidden'>&gt;</span> </li>";
}
?></ul>
@@ -87,6 +95,7 @@
?></h1>
<form id='search_form' action='<?php echo $config->urls->root?>search/' method='get'>
<label for='search_query' class='visually-hidden'>Search:</label>
<input type='text' name='q' id='search_query' value='<?php echo htmlentities($input->whitelist('q'), ENT_QUOTES, 'UTF-8'); ?>' />
<button type='submit' id='search_submit'>Search</button>
</form>
@@ -111,7 +120,7 @@
<div class="container">
<div id="sidebar">
<div id="sidebar" role='complementary'>
<?php
@@ -128,11 +137,12 @@
// We have determined that we're not on the homepage
// and that this section has child pages, so make navigation:
echo "<ul id='subnav' class='nav'>";
echo "<ul id='subnav' class='nav' role='navigation'>";
foreach($page->rootParent->children as $child) {
$class = $page === $child ? " class='on'" : '';
echo "<li><a$class href='{$child->url}'>{$child->title}</a></li>";
$ariaState = $page === $child ? " aria-current='true' " : '';
echo "<li><a$class$ariaState href='{$child->url}'>{$child->title}</a></li>";
}
echo "</ul>";
@@ -156,5 +166,5 @@
</div><!--/sidebar-->
<div id="bodycopy">
<div id="bodycopy" role="main">

View File

@@ -502,3 +502,45 @@ body, input, textarea, table {
z-index: 9999;
}
/*********************************************************************
* 6. Accessibility helpers
*
*/
/* Hide visually, but remain approachable for screenreader */
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
white-space: nowrap;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
border: 0;
}
/* Show bypass link on hover */
.element-focusable:focus {
clip: auto;
overflow: visible;
height: auto;
}
/* Sample styling for bypass link */
.bypass-to-main:focus {
top: 0;
left: 0;
z-index: 10;
width: 100%;
height: 40px;
line-height: 40px;
text-align: center;
background: #333;
color: #fff;
}

View File

@@ -17,7 +17,7 @@
* This file is licensed under the MIT license
* https://processwire.com/about/license/mit/
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2019 by Ryan Cramer
* https://processwire.com
*
*/
@@ -26,17 +26,7 @@ if(!defined("PROCESSWIRE")) die();
/*** SITE CONFIG *************************************************************************/
/**
* Enable debug mode?
*
* Debug mode causes additional info to appear for use during dev and debugging.
* This is almost always recommended for sites in development. However, you should
* always have this disabled for live/production sites.
*
* @var bool
*
*/
$config->debug = false;
/** @var Config $config */
/**
* Prepend template file
@@ -60,7 +50,19 @@ $config->prependTemplateFile = '_init.php';
*/
$config->appendTemplateFile = '_main.php';
/**
* Allow core API variables to also be accessed as functions?
*
* Recommended. This enables API varibles like $pages to also be accessed as pages(),
* as an example. And so on for most other core variables.
*
* Benefits are better type hinting, always in scope, and potentially shorter API calls.
* See the file /wire/core/FunctionsAPI.php for details on these functions.
*
* @var bool
*
*/
$config->useFunctionsAPI = true;
/*** INSTALLER CONFIG ********************************************************************/

View File

@@ -1,5 +1,7 @@
<?php namespace ProcessWire;
if(!defined("PROCESSWIRE")) die();
/**
* ProcessWire Request Finished
* ============================

12
site-default/htaccess.txt Normal file
View File

@@ -0,0 +1,12 @@
# Start ProcessWire:pwbphp (install)
# Block PHP files from direct access (optional fallback if root .htaccess missing)
<FilesMatch "\.(php|module|inc)$">
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order allow,deny
Deny from all
</IfModule>
</FilesMatch>
# End ProcessWire:pwbphp

View File

@@ -1,5 +1,7 @@
<?php namespace ProcessWire;
if(!defined("PROCESSWIRE")) die();
/**
* ProcessWire Bootstrap Initialization
* ====================================

View File

@@ -27,17 +27,17 @@ class Helloworld extends WireData implements Module {
return array(
// The module'ss title, typically a little more descriptive than the class name
// The module's title, typically a little more descriptive than the class name
'title' => 'Hello World',
// version number
'version' => 2,
'version' => 3,
// summary is brief description of what this module is
'summary' => 'An example module used for demonstration purposes. See the /site/modules/Helloworld.module file for details.',
'summary' => 'An example module used for demonstration purposes.',
// Optional URL to more information about the module
'href' => 'http://processwire.com',
'href' => 'https://processwire.com',
// singular=true: indicates that only one instance of the module is allowed.
// This is usually what you want for modules that attach hooks.
@@ -80,9 +80,12 @@ class Helloworld extends WireData implements Module {
/**
* Example1 hooks into the pages->save method and displays a notice every time a page is saved
*
* @param HookEvent $event
*
*/
public function example1($event) {
/** @var Page $page */
$page = $event->arguments[0];
$this->message("Hello World! You saved {$page->path}.");
}
@@ -90,10 +93,13 @@ class Helloworld extends WireData implements Module {
/**
* Example2 hooks into every page after it's rendered and adds "Hello World" text at the bottom
*
* @param HookEvent $event
*
*/
public function example2($event) {
/** @var Page $page */
$page = $event->object;
// don't add this to the admin pages
@@ -105,6 +111,8 @@ class Helloworld extends WireData implements Module {
/**
* Example3 adds a 'hello' method (not property) to every page that simply returns "Hello World"
*
* @param HookEvent $event
*
*/
public function example3($event) {
@@ -113,6 +121,8 @@ class Helloworld extends WireData implements Module {
/**
* Example 4 adds a 'hello_world' property (not method) to every page that returns "Hello [user]"
*
* @param HookEvent $event
*
*/
public function example4($event) {

View File

@@ -69,9 +69,6 @@ learning module development:
There is a module development forum located at:
https://processwire.com/talk/forum/19-moduleplugin-development/
For a tutorial on how to create modules, see:
http://wiki.processwire.com/index.php/Module_Creation
Additional resources
--------------------

View File

@@ -1,5 +1,7 @@
<?php namespace ProcessWire;
if(!defined("PROCESSWIRE")) die();
/**
* ProcessWire Bootstrap API Ready
* ===============================

View File

@@ -41,12 +41,14 @@
</head>
<body class="<?php if($sidebar) echo "has-sidebar "; ?>">
<a href="#main" class="visually-hidden element-focusable bypass-to-main">Skip to content</a>
<!-- top navigation -->
<ul class='topnav'><?php
<ul class='topnav' role='navigation'><?php
// top navigation consists of homepage and its visible children
foreach($homepage->and($homepage->children) as $item) {
if($item->id == $page->rootParent->id) {
echo "<li class='current'>";
echo "<li class='current' aria-current='true'><span class='visually-hidden'>Current page: </span>";
} else {
echo "<li>";
}
@@ -59,12 +61,13 @@
<!-- search form-->
<form class='search' action='<?php echo $pages->get('template=search')->url; ?>' method='get'>
<input type='text' name='q' placeholder='Search' value='<?php echo $sanitizer->entities($input->whitelist('q')); ?>' />
<button type='submit' name='submit'>Search</button>
<label for='search' class='visually-hidden'>Search:</label>
<input type='text' name='q' placeholder='Search' id='search' value='<?php echo $sanitizer->entities($input->whitelist('q')); ?>' />
<button type='submit' name='submit' class='visually-hidden'>Search</button>
</form>
<!-- breadcrumbs -->
<div class='breadcrumbs'><?php
<div class='breadcrumbs' role='navigation' aria-label='You are here:'><?php
// breadcrumbs are the current page's parents
foreach($page->parents() as $item) {
echo "<span><a href='$item->url'>$item->title</a></span> ";
@@ -83,9 +86,9 @@
<!-- sidebar content -->
<?php if($sidebar): ?>
<div id='sidebar'>
<aside id='sidebar'>
<?php echo $sidebar; ?>
</div>
</aside>
<?php endif; ?>
</div>

View File

@@ -6,6 +6,7 @@
* 3. Main content and sidebar
* 4. Footer
* 5. Media queries for responsive layout
* 6. Accessibility helpers
*
*/
@@ -124,9 +125,6 @@ form.search {
border: 1px solid #ccc;
width: 100%;
}
form.search button {
display: none;
}
.breadcrumbs {
clear: both;
@@ -297,3 +295,42 @@ figure figcaption {
font-size: 115%;
}
}
/*********************************************************************
* 6. Accessibility helpers
*
*/
/* Hide visually, but remain approachable for screenreader */
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
white-space: nowrap;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
border: 0;
}
/* Show bypass link on hover */
.element-focusable:focus {
clip: auto;
overflow: visible;
height: auto;
}
/* Sample styling for bypass link */
.bypass-to-main:focus {
top: 0;
left: 0;
width: 100%;
height: 40px;
line-height: 40px;
text-align: center;
background: #333;
color: #fff;
}

View File

@@ -17,7 +17,7 @@
* This file is licensed under the MIT license
* https://processwire.com/about/license/mit/
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2019 by Ryan Cramer
* https://processwire.com
*
*/
@@ -26,17 +26,7 @@ if(!defined("PROCESSWIRE")) die();
/*** SITE CONFIG *************************************************************************/
/**
* Enable debug mode?
*
* Debug mode causes additional info to appear for use during dev and debugging.
* This is almost always recommended for sites in development. However, you should
* always have this disabled for live/production sites.
*
* @var bool
*
*/
$config->debug = false;
/** @var Config $config */
/**
* Prepend template file
@@ -60,6 +50,20 @@ $config->prependTemplateFile = '_init.php';
*/
$config->appendTemplateFile = '_main.php';
/**
* Allow core API variables to also be accessed as functions?
*
* Recommended. This enables API varibles like $pages to also be accessed as pages(),
* as an example. And so on for most other core variables.
*
* Benefits are better type hinting, always in scope, and potentially shorter API calls.
* See the file /wire/core/FunctionsAPI.php for details on these functions.
*
* @var bool
*
*/
$config->useFunctionsAPI = true;
/*** INSTALLER CONFIG ********************************************************************/

View File

@@ -144,10 +144,10 @@
"text": "Pfade sollten relativ zum Root-Verzeichnis ihrer ProcessWire Installation sein (d.h. wenn die Seite in einem Unterverzeichnis l\u00e4uft, diesen Teil weglassen)."
},
"9728a8f280cf7e8b0b89b52749f7e800": {
"text": "Bitte lesen Sie die [Anweisungen](https:\/\/github.com\/ryancramerdesign\/ProcessWire\/blob\/dev\/wire\/modules\/Inputfield\/InputfieldCKEditor\/README.md#custom-editor-css-file) zur Benutzung."
"text": "Bitte lesen Sie die [Anweisungen](https:\/\/github.com\/processwire\/processwire\/blob\/master\/wire\/modules\/Inputfield\/InputfieldCKEditor\/README.md#custom-editor-css-file) zur Benutzung."
},
"63ba0561c6027fed354e6c5af252a77b": {
"text": "Bitte lesen Sie die [Anweisungen](https:\/\/github.com\/ryancramerdesign\/ProcessWire\/blob\/dev\/wire\/modules\/Inputfield\/InputfieldCKEditor\/README.md#custom-editor-js-styles-set) zur Benutzung."
"text": "Bitte lesen Sie die [Anweisungen](https:\/\/github.com\/processwire\/processwire\/blob\/master\/wire\/modules\/Inputfield\/InputfieldCKEditor\/README.md#custom-editor-js-styles-set) zur Benutzung."
},
"8cd140c060d298234e0079cb77ee8190": {
"text": "Folgende Plugins wurden gefunden. Markieren Sie die Box neben jedem Plugin welches geladen werden soll."

View File

@@ -60,8 +60,10 @@
</head>
<body class="<?php if($sidebar) echo "has-sidebar"; ?>">
<a href="#main" class="visually-hidden element-focusable bypass-to-main"><?php echo _x('Skip to content', 'bypass'); ?></a>
<!-- language switcher / navigation -->
<ul class='languages'><?php
<ul class='languages' role='navigation'><?php
foreach($languages as $language) {
if(!$page->viewable($language)) continue; // is page viewable in this language?
if($language->id == $user->language->id) {
@@ -76,11 +78,11 @@
?></ul>
<!-- top navigation -->
<ul class='topnav'><?php
<ul class='topnav' role='navigation'><?php
// top navigation consists of homepage and its visible children
foreach($homepage->and($homepage->children) as $item) {
if($item->id == $page->rootParent->id) {
echo "<li class='current'>";
echo "<li class='current' aria-current='true'><span class='visually-hidden'>" . _x('Current page:', 'navigation') . " </span>";
} else {
echo "<li>";
}
@@ -92,7 +94,7 @@
?></ul>
<!-- breadcrumbs -->
<div class='breadcrumbs'><?php
<div class='breadcrumbs' role='navigation' aria-label='<?php echo _x('You are here:', 'breadcrumbs'); ?>'><?php
// breadcrumbs are the current page's parents
foreach($page->parents() as $item) {
echo "<span><a href='$item->url'>$item->title</a></span> ";
@@ -103,12 +105,13 @@
<!-- search engine -->
<form class='search' action='<?php echo $pages->get('template=search')->url; ?>' method='get'>
<input type='text' name='q' placeholder='<?php echo _x('Search', 'placeholder'); ?>' />
<button type='submit' name='submit'><?php echo _x('Search', 'button'); ?></button>
<label for='search' class='visually-hidden'><?php echo _x('Search:', 'label'); ?></label>
<input type='text' name='q' id='search' placeholder='<?php echo _x('Search', 'placeholder'); ?>' />
<button type='submit' name='submit' class='visually-hidden'><?php echo _x('Search', 'button'); ?></button>
</form>
<div id='main'>
<main id='main'>
<!-- main content -->
<div id='content'>
@@ -121,15 +124,15 @@
<!-- sidebar content -->
<?php if($sidebar): ?>
<div id='sidebar'>
<aside id='sidebar'>
<?php echo $sidebar; ?>
</div>
</aside>
<?php endif; ?>
</div>
</main>
<!-- footer -->
<footer id='footer'>

View File

@@ -6,6 +6,7 @@
* 3. Main content and sidebar
* 4. Footer
* 5. Media queries for responsive layout
* 6. Accessibility helpers
*
*/
@@ -156,9 +157,6 @@ form.search {
border: 1px solid #ccc;
width: 100%;
}
form.search button {
display: none;
}
.breadcrumbs {
font-size: 80%;
@@ -351,3 +349,43 @@ figure figcaption {
font-size: 115%;
}
}
/*********************************************************************
* 6. Accessibility helpers
*
*/
/* Hide visually, but remain approachable for screenreader */
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
white-space: nowrap;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
border: 0;
}
/* Show bypass link on hover */
.element-focusable:focus {
clip: auto;
overflow: visible;
height: auto;
}
/* Sample styling for bypass link */
.bypass-to-main:focus {
top: 0;
left: 0;
width: 100%;
height: 40px;
line-height: 40px;
text-align: center;
background: #333;
color: #fff;
}

View File

@@ -0,0 +1,4 @@
<?php
// Intentionally left blank to test that htaccess rewrite rules are working.
// Accessing this file from http should produce a '403 forbidden' error,
// since all PHP files are blocked under /assets/.

32
site-regular/config.php Normal file
View File

@@ -0,0 +1,32 @@
<?php
/**
* ProcessWire Configuration File
*
* Site-specific configuration for ProcessWire.
* https://processwire.com/api/ref/config/
*
* Please see the file /wire/config.php which contains all configuration options you may
* specify here. Simply copy any of the configuration options from that file and paste
* them into this file in order to modify them.
*
* ProcessWire 3.x
* Copyright (C) 2019 by Ryan Cramer
*
* https://processwire.com
*
*/
if(!defined("PROCESSWIRE")) die();
/*** SITE CONFIG *************************************************************************/
$config->prependTemplateFile = '_init.php';
$config->appendTemplateFile = '_main.php';
$config->useMarkupRegions = true;
$config->useFunctionsAPI = true;
/*** INSTALLER CONFIG ********************************************************************/

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@@ -0,0 +1,6 @@
<?php if(!defined("PROCESSWIRE_INSTALL")) die();
$info = array(
'title' => "Regular Uikit 3.x site/blog profile",
'summary' => "This is a simple/regular blog site profile that uses Uikit 3 on the front-end and demonstrates several features new to ProcessWire 3.x.",
'screenshot' => "screen_shot_2017-01-27_at_1_30_19_pm.png"
);

View File

@@ -0,0 +1,718 @@
# --- WireDatabaseBackup {"time":"2017-01-27 13:32:39","user":"","dbName":"pw_xyz","description":"","tables":[],"excludeTables":["pages_drafts","pages_roles","permissions","roles","roles_permissions","users","users_roles","user","role","permission"],"excludeCreateTables":[],"excludeExportTables":["field_roles","field_permissions","field_email","field_pass","caches","session_login_throttle","page_path_history"]}
DROP TABLE IF EXISTS `caches`;
CREATE TABLE `caches` (
`name` varchar(191) NOT NULL,
`data` mediumtext NOT NULL,
`expires` datetime NOT NULL,
PRIMARY KEY (`name`),
KEY `expires` (`expires`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `field_body`;
CREATE TABLE `field_body` (
`pages_id` int(10) unsigned NOT NULL,
`data` mediumtext NOT NULL,
PRIMARY KEY (`pages_id`),
FULLTEXT KEY `data` (`data`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `field_body` (`pages_id`, `data`) VALUES('1', '<p>This is a simple <a href=\"https://processwire.com\">ProcessWire</a> site profile that is somewhat like our default site profile, but also includes a blog. It demonstrates development of various features including some new to ProcessWire 3.x. The front-end of this profile uses the <a href=\"http://www.getuikit.com\" target=\"_blank\" rel=\"noreferrer\">Uikit 3</a> library and includes a library of time-saving functions for working with Uikit 3. Below are a few highlights you\'ll find in this site profile:</p>\n\n<ul><li>Use of markup regions and the new ProcessWire functions API.</li>\n <li>Use of Uikit 3 in template files and includes a handy PHP library of Uikit-specific functions.</li>\n <li>Demonstrates front-end editing features on <a data-pwid=1024 href=\"/about/front-end-editor-demo/\">this page</a>.</li>\n <li>Uses pagination (after 10+ blog posts) and demonstrates use of comments as well.</li>\n <li>Demonstrates caching of markup (see mobile off-canvas navigation).</li>\n <li>Demonstrates use of a Page reference field, as used by categories in the blog.</li>\n <li>The template files are easy-to-read and modify, and serve as a good platform to build from.</li>\n <li>Demonstrates implementation of a custom hook function (see in the /site/ready.php file).</li>\n</ul>');
INSERT INTO `field_body` (`pages_id`, `data`) VALUES('27', '<h3>The page you were looking for is not found.</h3>\n\n<p>Please use the navigation above to find the page, or use the search engine in the footer. </p>');
INSERT INTO `field_body` (`pages_id`, `data`) VALUES('1001', '<p>Dolore ad nunc, mos accumsan paratus duis suscipit luptatum facilisis macto uxor iaceo quadrum. Demoveo, appellatio elit neque ad commodo ea. Wisi, iaceo, tincidunt at commoveo rusticus et, ludus. Feugait at blandit bene blandit suscipere abdo duis ideo bis commoveo pagus ex, velit. Consequat commodo roto accumsan, duis transverbero.</p>');
INSERT INTO `field_body` (`pages_id`, `data`) VALUES('1002', '<p>Iusto incassum appellatio cui macto genitus vel. Lobortis aliquam luctus, roto enim, imputo wisi tamen. Ratis odio, genitus acsi, neo illum consequat consectetuer ut.</p>\n\n<p>Patria iriure vel vel autem proprius indoles ille sit. Tation blandit refoveo, accumsan ut ulciscor lucidus inhibeo capto aptent opes, foras.</p>\n\n<h3>Dolore ea valde refero feugait utinam luctus</h3>\n\n<p><img alt=\"Copyright by Austin Cramer for DesignIntelligence. This is a placeholder while he makes new ones for us.\" class=\"align_right hidpi\" src=\"/site/assets/files/1002/psych_cartoon_4-20.300x0-is-hidpi.jpg\" width=\"300\" />Usitas, nostrud transverbero, in, amet, nostrud ad. Ex feugiat opto diam os aliquam regula lobortis dolore ut ut quadrum. Esse eu quis nunc jugis iriure volutpat wisi, fere blandit inhibeo melior, hendrerit, saluto velit. Eu bene ideo dignissim delenit accumsan nunc. Usitas ille autem camur consequat typicus feugait elit ex accumsan nutus accumsan nimis pagus, occuro. Immitto populus, qui feugiat opto pneum letalis paratus. Mara conventio torqueo nibh caecus abigo sit eum brevitas. Populus, duis ex quae exerci hendrerit, si antehabeo nobis, consequat ea praemitto zelus.</p>\n\n<p>Immitto os ratis euismod conventio erat jus caecus sudo. code test Appellatio consequat, et ibidem ludus nulla dolor augue abdo tego euismod plaga lenis. Sit at nimis venio venio tego os et pecus enim pneum magna nobis ad pneum. Saepius turpis probo refero molior nonummy aliquam neque appellatio jus luctus acsi. Ulciscor refero pagus imputo eu refoveo valetudo duis dolore usitas. Consequat suscipere quod torqueo ratis ullamcorper, dolore lenis, letalis quia quadrum plaga minim.</p>');
INSERT INTO `field_body` (`pages_id`, `data`) VALUES('1004', '<p>Magna in gemino, gilvus iusto capto jugis abdo mos aptent acsi qui. Utrum inhibeo humo humo duis quae. Lucidus paulatim facilisi scisco quibus hendrerit conventio adsum.</p>\n\n<h2>Si lobortis singularis genitus ibidem saluto</h2>\n\n<ul><li>Feugiat eligo foras ex elit sed indoles hos elit ex antehabeo defui et nostrud.</li>\n <li>Letatio valetudo multo consequat inhibeo ille dignissim pagus et in quadrum eum eu.</li>\n <li>Aliquam si consequat, ut nulla amet et turpis exerci, adsum luctus ne decet, delenit.</li>\n <li>Commoveo nunc diam valetudo cui, aptent commoveo at obruo uxor nulla aliquip augue.</li>\n</ul><p>Iriure, ex velit, praesent vulpes delenit capio vero gilvus inhibeo letatio aliquip metuo qui eros. Transverbero demoveo euismod letatio torqueo melior. Ut odio in suscipit paulatim amet huic letalis suscipere eros causa, letalis magna.</p>\n\n<ol><li>Feugiat eligo foras ex elit sed indoles hos elit ex antehabeo defui et nostrud.</li>\n <li>Letatio valetudo multo consequat inhibeo ille dignissim pagus et in quadrum eum eu.</li>\n <li>Aliquam si consequat, ut nulla amet et turpis exerci, adsum luctus ne decet, delenit.</li>\n <li>Commoveo nunc diam valetudo cui, aptent commoveo at obruo uxor nulla aliquip augue.</li>\n</ol>');
INSERT INTO `field_body` (`pages_id`, `data`) VALUES('1015', '<p>Fixed effect pulse current remote integer potentiometer anomoly. Gigabyte recognition deviation active sequential bypass echo distributed. Embedded encapsulated mainframe reducer logarithmic potentiometer duplex. Software metafile reducer deviation boolean overflow bridgeware.</p>\n\n<p>Patch internet nano. Converter a inversion recursive adaptive encapsulated transport floating-point transistorized plasma microscopic node. PC duplex partitioned. Network scalar dithering encapsulated generator normalizing. Remote interval fixed plasma normalizing microscopic procedural scalar dynamic read-only high boolean.</p>\n\n<h3>Reducer hybrid force key</h3>\n\n<p>Cascading wave network logarithmic digital powered scan. Frequency coordinated particle transmission supporting. Log distributed bus scan force particle computer inversion servicing reverberated device. In coordinated services backbone silicon hyperlinked. Scalar error fiber transponder digital.</p>\n\n<p>Vector developer connectivity connectivity modular supporting broadband solution. For modular vector timer indeterminate debugged optical kilohertz procedural procedural. Infrared fuzzy procedural capacitance fiber. Algorithm direct procedural echo. Digital bridgeware by timer fragmentation ethernet inducer phase network.</p>\n\n<p>Transaction active by. Effect partitioned by timer system services computer. Spawned coordinated developer fuzzy. Technician fuzzy supporting protocol coordinated ethernet. Bridgeware video remote prototype development.</p>');
INSERT INTO `field_body` (`pages_id`, `data`) VALUES('1021', '<p>Grown plus industry open for when when sharpest ordinary offer by. Better huggable opportunity too. Rosy sleek while exclusive gentle not on. Offer colossal silky this sweet magically announcing durable sold soaking our try. Sold one zesty velvety awesome flavored ever with effervescent gentle. Screamin\' improved permanent treat now tasty we space 100%.</p>\n\n<p>Think affordable artificial blast while choice. Appetizing available really thank-you out proven desire fresh rich. Natural and flash power effective grand premium. Secret lifetime grand quenches by ocean as comfort golden youthful fast. Disposable zesty dazzling open sure spacious multi-purpose the super market rare.</p>\n\n<p>Spring special bigger wherever only this comfort tummy extravaganza save. Very messy keen leading incredible.</p>\n\n<p>Hearty brand chocolatey comfort admire ultra. Want kids touch discount love appetizing talking inside buttery. For keeps admire youthful. Wherever super thirsty lasting limited discover picky can\'t.</p>\n\n<p>Good appreciate flexible product best. Full-bodied don\'t customer gigantic also.</p>');
INSERT INTO `field_body` (`pages_id`, `data`) VALUES('1022', '<p>Genuine symphony solid educated de-jour regal gifted guests. Using gilded member silk dignified gilded panoramic art politically. Diamond upper brokerage pleasure society reserved. First-class topiary treasure travel is the best wishlist vacation solid penthouse world.</p>\n\n<p>Board marquis estate career blissfull treasure saphire. Delegate cultered regal marquis cigar sterling penthouse.</p>\n\n<p>Sterling butler solid penthouse gilded gilded pedigree wine using investments cigar. Cultered doctoral symphony extra accredited. Private benefactor monogram high-rise a.</p>\n\n<p>Career gilded extra aristocratic cruise brilliant impresario. European ambassador acumen ambassador. Rare suite cruise club crafted butler grande.</p>\n\n<p>Distinctly rich auction penthouse travel.</p>');
INSERT INTO `field_body` (`pages_id`, `data`) VALUES('1024', '<p>If you are logged in with edit access to this page, you can double-click this body copy to edit it. You can also do the same to edit the headline above, or the sidebar text to the right.</p>\n\n<p>Illum aliquip loquor. Hendrerit interdico dolor zelus diam metuo causa lobortis scisco. Euismod damnum quibus ideo patria opto. Haero odio jus virtus haero pagus erat cogo diam minim vulputate autem.</p>\n\n<h3>Ullamcorper venio bene</h3>\n\n<p>Amet ea oppeto nullus esse meus immitto sudo dignissim. Letalis velit utrum luptatum ullamcorper illum ad fere molior populus ut. Et augue eligo jumentum populus nonummy virtus. Valetudo odio ex opes mos delenit immitto ex. Illum tincidunt commoveo nostrud et ratis ne vulputate vereor tego.</p>\n\n<ul><li>Capto elit vel eu esse quia</li>\n <li>Te gemino natu et augue ad</li>\n <li>Amet aliquip valde blandit olim facilisi</li>\n</ul><p>Nulla iusto pertineo camur similis enim abigo luptatum ymo nullus. Inhibeo nutus pagus capto dolus capio pecus. Pala vereor esse melior nisl bis. Veniam eros consequat.</p>');
DROP TABLE IF EXISTS `field_categories`;
CREATE TABLE `field_categories` (
`pages_id` int(10) unsigned NOT NULL,
`data` int(11) NOT NULL,
`sort` int(10) unsigned NOT NULL,
PRIMARY KEY (`pages_id`,`sort`),
KEY `data` (`data`,`pages_id`,`sort`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `field_categories` (`pages_id`, `data`, `sort`) VALUES('1015', '1017', '0');
INSERT INTO `field_categories` (`pages_id`, `data`, `sort`) VALUES('1015', '1018', '1');
INSERT INTO `field_categories` (`pages_id`, `data`, `sort`) VALUES('1021', '1018', '1');
INSERT INTO `field_categories` (`pages_id`, `data`, `sort`) VALUES('1021', '1019', '0');
INSERT INTO `field_categories` (`pages_id`, `data`, `sort`) VALUES('1022', '1019', '0');
INSERT INTO `field_categories` (`pages_id`, `data`, `sort`) VALUES('1022', '1020', '1');
INSERT INTO `field_categories` (`pages_id`, `data`, `sort`) VALUES('1021', '1027', '2');
DROP TABLE IF EXISTS `field_comments`;
CREATE TABLE `field_comments` (
`pages_id` int(10) unsigned NOT NULL,
`data` text NOT NULL,
`sort` int(10) unsigned NOT NULL,
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`status` tinyint(3) NOT NULL DEFAULT '0',
`cite` varchar(128) NOT NULL DEFAULT '',
`email` varchar(250) NOT NULL DEFAULT '',
`created` int(10) unsigned NOT NULL,
`created_users_id` int(10) unsigned NOT NULL,
`ip` varchar(15) NOT NULL DEFAULT '',
`user_agent` varchar(250) NOT NULL DEFAULT '',
`website` varchar(250) NOT NULL DEFAULT '',
`parent_id` int(10) unsigned NOT NULL DEFAULT '0',
`flags` int(10) unsigned NOT NULL DEFAULT '0',
`code` varchar(128) DEFAULT NULL,
`subcode` varchar(40) DEFAULT NULL,
`upvotes` int(10) unsigned NOT NULL DEFAULT '0',
`downvotes` int(10) unsigned NOT NULL DEFAULT '0',
`stars` tinyint(3) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `pages_id_sort` (`pages_id`,`sort`),
KEY `status` (`status`,`email`(191)),
KEY `pages_id` (`pages_id`,`status`,`created`),
KEY `created` (`created`,`status`),
KEY `code` (`code`),
KEY `subcode` (`subcode`),
FULLTEXT KEY `data` (`data`)
) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
INSERT INTO `field_comments` (`pages_id`, `data`, `sort`, `id`, `status`, `cite`, `email`, `created`, `created_users_id`, `ip`, `user_agent`, `website`, `parent_id`, `flags`, `code`, `subcode`, `upvotes`, `downvotes`, `stars`) VALUES('1021', 'They good night the piper good night good queen white as snow they magical beans winding path up the hill dragon beautiful dress. So loud magic wand took fought angry lion ding-dong. Winding path fought ran away whale swallowed crystal ball poison apple took the piper sang twinkled.', '2', '1', '1', 'Jim', 'jim@processwire.com', '1485450830', '41', '0.0.0.0', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36', '', '0', '0', 'aeHvmkn88ncb4214OXegP8uUrQy6D5UZcaD9pPxT9gPFDdEOf1EqCM6UD6JUnY7Jtv9MPNjcPrJWUxKhyh89r1H6nywk1Se_GdwAoj2guU_9YYa9MEgiuJUekuk93YvE', 'JzPW6751GqTqk1Oh__k0IbNfOi_Nc6nYvPPa2wl6', '0', '0', NULL);
INSERT INTO `field_comments` (`pages_id`, `data`, `sort`, `id`, `status`, `cite`, `email`, `created`, `created_users_id`, `ip`, `user_agent`, `website`, `parent_id`, `flags`, `code`, `subcode`, `upvotes`, `downvotes`, `stars`) VALUES('1021', 'LED harmonic nominal femtosecond data solid alphanumeric alphanumeric. By sampling bus recursive null. Modular timer recognition passive interval. Theory capacitance application fragmentation with supporting indeterminate. Microscopic record indeterminate scalar concept deviation system.', '3', '2', '1', 'ryan', 'ryan@processwire.com', '1485453231', '41', '0.0.0.0', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36', 'https://processwire.com', '0', '0', 'eWwAZPZHyC4JcShlKDPrr5Y_rC8sntJOildm2ecqUegXPwgfwmRhyOn5ssyQhABWwaweM74e_TApOLMQu4MGt9lSf7VcxH994ciwggF0f3lpEdJ3OMtjYe4MvW4gDzNF', 'CcaWnsxrcWYxfCgnnvYc2DEN7qU3bd_7HVm3NK_0', '0', '0', NULL);
INSERT INTO `field_comments` (`pages_id`, `data`, `sort`, `id`, `status`, `cite`, `email`, `created`, `created_users_id`, `ip`, `user_agent`, `website`, `parent_id`, `flags`, `code`, `subcode`, `upvotes`, `downvotes`, `stars`) VALUES('1022', 'Run Spot play help I am hungry I can help too oh no. He wants to play oh no for a ride I can help for a ride too on our bikes chase the cat for a ride. Bring it here walk we have two bark I am hungry jump high now he is funny it is Sally for a ride oh please. We can oh please down the toy I can help no Jane is looking she is happy share with them.\n\nLook too eating cake he wants to play he wants to play down don\'t worry on our bikes on our bikes. I am hungry down jump high they are silly and oh no help but. Thank you over there I can help Dick said over there on our bikes see Puff over there do it Jane is looking I can see he is fast. Quick in the wagon no jump high.', '2', '3', '1', 'Ryan', 'ryan@processwire.com', '1485528109', '41', '0.0.0.0', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36', 'https://processwire.com', '0', '0', 'CVaOB64FqwwVlmAXUbhP5KbZQDFM7OarKON1ZIb_PYP9cDkpcb0NCYp56iAPHTZoIjJEZNQ4mnuEyLkYw97XkfOgmtRDd33rfZB0Zt1yfDKOjY4tdkWP08BKUbI_MImr', 'ySOMziIfOxoi_BfPVBNxNaGVrKHGzHGtFbnbkodo', '0', '0', NULL);
DROP TABLE IF EXISTS `field_comments_votes`;
CREATE TABLE `field_comments_votes` (
`comment_id` int(10) unsigned NOT NULL,
`vote` tinyint(4) NOT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`ip` varchar(15) NOT NULL DEFAULT '',
`user_id` int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`comment_id`,`ip`,`vote`),
KEY `created` (`created`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `field_date`;
CREATE TABLE `field_date` (
`pages_id` int(10) unsigned NOT NULL,
`data` datetime NOT NULL,
PRIMARY KEY (`pages_id`),
KEY `data` (`data`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `field_date` (`pages_id`, `data`) VALUES('1015', '2017-01-25 00:00:00');
INSERT INTO `field_date` (`pages_id`, `data`) VALUES('1022', '2017-01-26 00:00:00');
INSERT INTO `field_date` (`pages_id`, `data`) VALUES('1021', '2017-01-27 00:00:00');
DROP TABLE IF EXISTS `field_email`;
CREATE TABLE `field_email` (
`pages_id` int(10) unsigned NOT NULL,
`data` varchar(191) NOT NULL DEFAULT '',
PRIMARY KEY (`pages_id`),
KEY `data_exact` (`data`),
FULLTEXT KEY `data` (`data`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `field_headline`;
CREATE TABLE `field_headline` (
`pages_id` int(10) unsigned NOT NULL,
`data` text NOT NULL,
PRIMARY KEY (`pages_id`),
FULLTEXT KEY `data` (`data`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `field_headline` (`pages_id`, `data`) VALUES('1', 'Uikit 3 site/blog profile');
INSERT INTO `field_headline` (`pages_id`, `data`) VALUES('27', '404 Page Not Found');
INSERT INTO `field_headline` (`pages_id`, `data`) VALUES('1001', 'About Us');
INSERT INTO `field_headline` (`pages_id`, `data`) VALUES('1024', 'Demonstration of front-end-editing');
DROP TABLE IF EXISTS `field_images`;
CREATE TABLE `field_images` (
`pages_id` int(10) unsigned NOT NULL,
`data` varchar(191) NOT NULL,
`sort` int(10) unsigned NOT NULL,
`description` text NOT NULL,
`modified` datetime DEFAULT NULL,
`created` datetime DEFAULT NULL,
PRIMARY KEY (`pages_id`,`sort`),
KEY `data` (`data`),
KEY `modified` (`modified`),
KEY `created` (`created`),
FULLTEXT KEY `description` (`description`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `field_images` (`pages_id`, `data`, `sort`, `description`, `modified`, `created`) VALUES('1002', 'psych_cartoon_4-20.jpg', '0', 'Copyright by Austin Cramer for DesignIntelligence. This is a placeholder while he makes new ones for us.', '2017-01-24 06:11:43', '2017-01-24 06:11:43');
INSERT INTO `field_images` (`pages_id`, `data`, `sort`, `description`, `modified`, `created`) VALUES('1021', 'screen_shot_2017-01-27_at_10_46_35_am.png', '0', '', '2017-01-27 10:56:13', '2017-01-27 10:56:13');
DROP TABLE IF EXISTS `field_pass`;
CREATE TABLE `field_pass` (
`pages_id` int(10) unsigned NOT NULL,
`data` char(40) NOT NULL,
`salt` char(32) NOT NULL,
PRIMARY KEY (`pages_id`),
KEY `data` (`data`)
) ENGINE=MyISAM DEFAULT CHARSET=ascii;
DROP TABLE IF EXISTS `field_permissions`;
CREATE TABLE `field_permissions` (
`pages_id` int(10) unsigned NOT NULL,
`data` int(11) NOT NULL,
`sort` int(10) unsigned NOT NULL,
PRIMARY KEY (`pages_id`,`sort`),
KEY `data` (`data`,`pages_id`,`sort`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `field_process`;
CREATE TABLE `field_process` (
`pages_id` int(11) NOT NULL DEFAULT '0',
`data` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`pages_id`),
KEY `data` (`data`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('10', '7');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('23', '10');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('3', '12');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('8', '12');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('9', '14');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('6', '17');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('11', '47');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('16', '48');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('21', '50');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('29', '66');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('30', '68');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('22', '76');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('28', '76');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('2', '87');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('300', '104');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('301', '109');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('302', '121');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('303', '129');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('31', '136');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('304', '138');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('1007', '150');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('1009', '158');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('1011', '159');
INSERT INTO `field_process` (`pages_id`, `data`) VALUES('1025', '165');
DROP TABLE IF EXISTS `field_roles`;
CREATE TABLE `field_roles` (
`pages_id` int(10) unsigned NOT NULL,
`data` int(11) NOT NULL,
`sort` int(10) unsigned NOT NULL,
PRIMARY KEY (`pages_id`,`sort`),
KEY `data` (`data`,`pages_id`,`sort`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `field_sidebar`;
CREATE TABLE `field_sidebar` (
`pages_id` int(10) unsigned NOT NULL,
`data` mediumtext NOT NULL,
PRIMARY KEY (`pages_id`),
FULLTEXT KEY `data` (`data`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `field_sidebar` (`pages_id`, `data`) VALUES('1', '<h3>Requirements</h3>\n\n<p>This site profile requires ProcessWire 3.0.105 or newer, Uikit 3, and the server must be running on PHP 5.4 or newer.</p>');
INSERT INTO `field_sidebar` (`pages_id`, `data`) VALUES('1002', '<h3>Sudo nullus</h3>\r\n\r\n<p>Et torqueo vulpes vereor luctus augue quod consectetuer antehabeo causa patria tation ex plaga ut. Abluo delenit wisi iriure eros feugiat probo nisl aliquip nisl, patria. Antehabeo esse camur nisl modo utinam. Sudo nullus ventosus ibidem facilisis saepius eum sino pneum, vicis odio voco opto.</p>');
INSERT INTO `field_sidebar` (`pages_id`, `data`) VALUES('1024', '<h3>Double click me</h3>\n\n<p>Esca demoveo exputo sagaciter ullamcorper inhibeo ut nimis refoveo praemitto defui ut. Hendrerit ratis dignissim ea eligo. Genitus utinam suscipere caecus ad neque verto at regula saluto esse turpis. Refero autem et nulla ibidem caecus fere acsi plaga in turpis. Nobis sit nunc esse capio suscipit vulpes facilisis brevitas. Pagus odio eros accumsan et interdico nunc abdo eligo epulae.</p>');
DROP TABLE IF EXISTS `field_summary`;
CREATE TABLE `field_summary` (
`pages_id` int(10) unsigned NOT NULL,
`data` mediumtext NOT NULL,
PRIMARY KEY (`pages_id`),
FULLTEXT KEY `data` (`data`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `field_summary` (`pages_id`, `data`) VALUES('1', 'A simple blog site about nothing in particular.');
INSERT INTO `field_summary` (`pages_id`, `data`) VALUES('1001', 'This is a placeholder page with two child pages to serve as an example. ');
INSERT INTO `field_summary` (`pages_id`, `data`) VALUES('1002', 'Dolore ea valde refero feugait utinam luctus. Probo velit commoveo et, delenit praesent, suscipit zelus, hendrerit zelus illum facilisi, regula. ');
INSERT INTO `field_summary` (`pages_id`, `data`) VALUES('1004', 'Mos erat reprobo in praesent, mara premo, obruo iustum pecus velit lobortis te sagaciter populus.');
INSERT INTO `field_summary` (`pages_id`, `data`) VALUES('1005', 'View this template\'s source for a demonstration of how to create a basic site map. ');
INSERT INTO `field_summary` (`pages_id`, `data`) VALUES('1024', 'If you are logged in with edit access, pages using the basic-page-edit template (like this one) are editable on the front-end.');
DROP TABLE IF EXISTS `field_title`;
CREATE TABLE `field_title` (
`pages_id` int(10) unsigned NOT NULL,
`data` text NOT NULL,
PRIMARY KEY (`pages_id`),
KEY `data_exact` (`data`(191)),
FULLTEXT KEY `data` (`data`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1', 'Home');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('2', 'Admin');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('3', 'Pages');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('6', 'Add Page');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('7', 'Trash');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('8', 'Tree');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('9', 'Save Sort');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('10', 'Edit');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('11', 'Templates');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('16', 'Fields');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('21', 'Modules');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('22', 'Setup');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('23', 'Login');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('27', '404 Page');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('28', 'Access');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('29', 'Users');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('30', 'Roles');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('31', 'Permissions');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('32', 'Edit pages');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('34', 'Delete pages');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('35', 'Move pages (change parent)');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('36', 'View pages');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('50', 'Sort child pages');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('51', 'Change templates on pages');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('52', 'Administer users');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('53', 'User can update profile/password');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('54', 'Lock or unlock a page');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('300', 'Search');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('301', 'Empty Trash');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('302', 'Insert Link');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('303', 'Insert Image');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('304', 'Profile');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1000', 'Search');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1001', 'About');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1002', 'Child page example 1');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1004', 'Child page example 2');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1005', 'Site Map');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1006', 'Use Page Lister');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1007', 'Find');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1009', 'Recent');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1010', 'Can see recently edited pages');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1011', 'Logs');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1012', 'Can view system logs');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1013', 'Can manage system logs');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1014', 'Blog');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1015', 'Phase data extended transaction');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1016', 'Categories');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1017', 'Coffee');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1018', 'Beer');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1019', 'Plants');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1020', 'Cats');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1021', 'Think affordable artificial blast');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1022', 'Genuine symphony');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1023', 'Use the front-end page editor');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1024', 'Front-end editing demo');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1025', 'Comments');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1026', 'Use the comments manager');
INSERT INTO `field_title` (`pages_id`, `data`) VALUES('1027', 'Recipes');
DROP TABLE IF EXISTS `fieldgroups`;
CREATE TABLE `fieldgroups` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(191) CHARACTER SET ascii NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=MyISAM AUTO_INCREMENT=102 DEFAULT CHARSET=utf8;
INSERT INTO `fieldgroups` (`id`, `name`) VALUES('2', 'admin');
INSERT INTO `fieldgroups` (`id`, `name`) VALUES('83', 'basic-page');
INSERT INTO `fieldgroups` (`id`, `name`) VALUES('101', 'basic-page-edit');
INSERT INTO `fieldgroups` (`id`, `name`) VALUES('98', 'blog');
INSERT INTO `fieldgroups` (`id`, `name`) VALUES('97', 'blog-post');
INSERT INTO `fieldgroups` (`id`, `name`) VALUES('99', 'categories');
INSERT INTO `fieldgroups` (`id`, `name`) VALUES('100', 'category');
INSERT INTO `fieldgroups` (`id`, `name`) VALUES('1', 'home');
INSERT INTO `fieldgroups` (`id`, `name`) VALUES('5', 'permission');
INSERT INTO `fieldgroups` (`id`, `name`) VALUES('4', 'role');
INSERT INTO `fieldgroups` (`id`, `name`) VALUES('80', 'search');
INSERT INTO `fieldgroups` (`id`, `name`) VALUES('88', 'sitemap');
INSERT INTO `fieldgroups` (`id`, `name`) VALUES('3', 'user');
DROP TABLE IF EXISTS `fieldgroups_fields`;
CREATE TABLE `fieldgroups_fields` (
`fieldgroups_id` int(10) unsigned NOT NULL DEFAULT '0',
`fields_id` int(10) unsigned NOT NULL DEFAULT '0',
`sort` int(11) unsigned NOT NULL DEFAULT '0',
`data` text,
PRIMARY KEY (`fieldgroups_id`,`fields_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('1', '1', '0', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('1', '44', '5', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('1', '76', '3', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('1', '78', '1', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('1', '79', '2', '{\"label\":\"Site tagline\"}');
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('1', '82', '4', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('2', '1', '0', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('2', '2', '1', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('3', '3', '0', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('3', '4', '2', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('3', '92', '1', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('4', '5', '0', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('5', '1', '0', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('80', '1', '0', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('83', '1', '0', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('83', '44', '5', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('83', '76', '3', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('83', '78', '1', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('83', '79', '2', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('83', '82', '4', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('88', '1', '0', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('88', '79', '1', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('97', '1', '0', '{\"columnWidth\":75}');
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('97', '44', '3', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('97', '76', '2', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('97', '97', '1', '{\"columnWidth\":25}');
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('97', '98', '4', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('97', '99', '5', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('98', '1', '0', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('99', '1', '0', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('100', '1', '0', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('101', '1', '0', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('101', '44', '5', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('101', '76', '3', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('101', '78', '1', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('101', '79', '2', NULL);
INSERT INTO `fieldgroups_fields` (`fieldgroups_id`, `fields_id`, `sort`, `data`) VALUES('101', '82', '4', NULL);
DROP TABLE IF EXISTS `fields`;
CREATE TABLE `fields` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`type` varchar(128) CHARACTER SET ascii NOT NULL,
`name` varchar(191) CHARACTER SET ascii NOT NULL,
`flags` int(11) NOT NULL DEFAULT '0',
`label` varchar(191) NOT NULL DEFAULT '',
`data` text NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`),
KEY `type` (`type`)
) ENGINE=MyISAM AUTO_INCREMENT=100 DEFAULT CHARSET=utf8;
INSERT INTO `fields` (`id`, `type`, `name`, `flags`, `label`, `data`) VALUES('1', 'FieldtypePageTitle', 'title', '13', 'Title', '{\"required\":1,\"textformatters\":[\"TextformatterEntities\"],\"size\":0,\"maxlength\":255}');
INSERT INTO `fields` (`id`, `type`, `name`, `flags`, `label`, `data`) VALUES('2', 'FieldtypeModule', 'process', '25', 'Process', '{\"description\":\"The process that is executed on this page. Since this is mostly used by ProcessWire internally, it is recommended that you don\'t change the value of this unless adding your own pages in the admin.\",\"collapsed\":1,\"required\":1,\"moduleTypes\":[\"Process\"],\"permanent\":1}');
INSERT INTO `fields` (`id`, `type`, `name`, `flags`, `label`, `data`) VALUES('3', 'FieldtypePassword', 'pass', '24', 'Set Password', '{\"collapsed\":1,\"size\":50,\"maxlength\":128}');
INSERT INTO `fields` (`id`, `type`, `name`, `flags`, `label`, `data`) VALUES('4', 'FieldtypePage', 'roles', '24', 'Roles', '{\"derefAsPage\":0,\"parent_id\":30,\"labelFieldName\":\"name\",\"inputfield\":\"InputfieldCheckboxes\",\"description\":\"User will inherit the permissions assigned to each role. You may assign multiple roles to a user. When accessing a page, the user will only inherit permissions from the roles that are also assigned to the page\'s template.\"}');
INSERT INTO `fields` (`id`, `type`, `name`, `flags`, `label`, `data`) VALUES('5', 'FieldtypePage', 'permissions', '24', 'Permissions', '{\"derefAsPage\":0,\"parent_id\":31,\"labelFieldName\":\"title\",\"inputfield\":\"InputfieldCheckboxes\"}');
INSERT INTO `fields` (`id`, `type`, `name`, `flags`, `label`, `data`) VALUES('44', 'FieldtypeImage', 'images', '0', 'Images', '{\"extensions\":\"gif jpg jpeg png\",\"adminThumbs\":1,\"inputfieldClass\":\"InputfieldImage\",\"maxFiles\":0,\"descriptionRows\":1,\"fileSchema\":2,\"textformatters\":[\"TextformatterEntities\"],\"outputFormat\":1,\"defaultValuePage\":0,\"defaultGrid\":0,\"icon\":\"camera\"}');
INSERT INTO `fields` (`id`, `type`, `name`, `flags`, `label`, `data`) VALUES('76', 'FieldtypeTextarea', 'body', '0', 'Body', '{\"inputfieldClass\":\"InputfieldCKEditor\",\"rows\":10,\"contentType\":1,\"toolbar\":\"Format, Bold, Italic, -, RemoveFormat\\nNumberedList, BulletedList, -, Blockquote\\nPWLink, Unlink, Anchor\\nPWImage, Table, HorizontalRule, SpecialChar\\nPasteText, PasteFromWord\\nScayt, -, Sourcedialog\",\"inlineMode\":0,\"useACF\":1,\"usePurifier\":1,\"formatTags\":\"p;h2;h3;h4;h5;h6;pre;address\",\"extraPlugins\":[\"pwimage\",\"pwlink\",\"sourcedialog\"],\"removePlugins\":\"image,magicline\",\"toggles\":[2,4,8],\"htmlOptions\":[2],\"collapsed\":0,\"minlength\":0,\"maxlength\":0,\"showCount\":0}');
INSERT INTO `fields` (`id`, `type`, `name`, `flags`, `label`, `data`) VALUES('78', 'FieldtypeText', 'headline', '0', 'Headline', '{\"description\":\"Use this instead of the Title if a longer headline is needed than what you want to appear in navigation.\",\"textformatters\":[\"TextformatterEntities\"],\"collapsed\":2,\"size\":0,\"maxlength\":1024,\"minlength\":0,\"showCount\":0}');
INSERT INTO `fields` (`id`, `type`, `name`, `flags`, `label`, `data`) VALUES('79', 'FieldtypeTextarea', 'summary', '1', 'Summary', '{\"textformatters\":[\"TextformatterEntities\"],\"inputfieldClass\":\"InputfieldTextarea\",\"collapsed\":2,\"rows\":3,\"contentType\":0}');
INSERT INTO `fields` (`id`, `type`, `name`, `flags`, `label`, `data`) VALUES('82', 'FieldtypeTextarea', 'sidebar', '0', 'Sidebar', '{\"inputfieldClass\":\"InputfieldCKEditor\",\"rows\":5,\"contentType\":1,\"toolbar\":\"Format, Bold, Italic, -, RemoveFormat\\r\\nNumberedList, BulletedList, -, Blockquote\\r\\nPWLink, Unlink, Anchor\\r\\nPWImage, Table, HorizontalRule, SpecialChar\\r\\nPasteText, PasteFromWord\\r\\nScayt, -, Sourcedialog\",\"inlineMode\":0,\"useACF\":1,\"usePurifier\":1,\"formatTags\":\"p;h2;h3;h4;h5;h6;pre;address\",\"extraPlugins\":[\"pwimage\",\"pwlink\",\"sourcedialog\"],\"removePlugins\":\"image,magicline\",\"toggles\":[2,4,8],\"collapsed\":2}');
INSERT INTO `fields` (`id`, `type`, `name`, `flags`, `label`, `data`) VALUES('92', 'FieldtypeEmail', 'email', '9', 'E-Mail Address', '{\"size\":70,\"maxlength\":255}');
INSERT INTO `fields` (`id`, `type`, `name`, `flags`, `label`, `data`) VALUES('97', 'FieldtypeDatetime', 'date', '0', 'Date', '{\"dateOutputFormat\":\"j F Y\",\"collapsed\":0,\"size\":25,\"datepicker\":3,\"timeInputSelect\":0,\"dateInputFormat\":\"Y\\/m\\/d\",\"defaultToday\":1,\"placeholder\":\"yyyy\\/mm\\/dd\",\"icon\":\"calendar\"}');
INSERT INTO `fields` (`id`, `type`, `name`, `flags`, `label`, `data`) VALUES('98', 'FieldtypePage', 'categories', '0', 'Categories', '{\"derefAsPage\":0,\"inputfield\":\"InputfieldAsmSelect\",\"parent_id\":1016,\"template_id\":46,\"labelFieldName\":\"title\",\"addable\":1,\"collapsed\":0}');
INSERT INTO `fields` (`id`, `type`, `name`, `flags`, `label`, `data`) VALUES('99', 'FieldtypeComments', 'comments', '0', 'Comments', '{\"schemaVersion\":6,\"moderate\":1,\"redirectAfterPost\":1,\"quietSave\":1,\"useNotify\":0,\"deleteSpamDays\":3,\"depth\":0,\"useWebsite\":1,\"dateFormat\":\"relative\",\"useVotes\":0,\"useStars\":0,\"useGravatar\":\"g\",\"collapsed\":0}');
DROP TABLE IF EXISTS `modules`;
CREATE TABLE `modules` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`class` varchar(128) CHARACTER SET ascii NOT NULL,
`flags` int(11) NOT NULL DEFAULT '0',
`data` text NOT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `class` (`class`)
) ENGINE=MyISAM AUTO_INCREMENT=167 DEFAULT CHARSET=utf8;
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('1', 'FieldtypeTextarea', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('2', 'FieldtypeNumber', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('3', 'FieldtypeText', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('4', 'FieldtypePage', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('6', 'FieldtypeFile', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('7', 'ProcessPageEdit', '1', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('10', 'ProcessLogin', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('12', 'ProcessPageList', '0', '{\"pageLabelField\":\"title\",\"paginationLimit\":25,\"limit\":50}', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('14', 'ProcessPageSort', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('15', 'InputfieldPageListSelect', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('17', 'ProcessPageAdd', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('25', 'InputfieldAsmSelect', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('27', 'FieldtypeModule', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('28', 'FieldtypeDatetime', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('29', 'FieldtypeEmail', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('30', 'InputfieldForm', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('32', 'InputfieldSubmit', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('33', 'InputfieldWrapper', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('34', 'InputfieldText', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('35', 'InputfieldTextarea', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('36', 'InputfieldSelect', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('37', 'InputfieldCheckbox', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('38', 'InputfieldCheckboxes', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('39', 'InputfieldRadios', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('40', 'InputfieldHidden', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('41', 'InputfieldName', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('43', 'InputfieldSelectMultiple', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('45', 'JqueryWireTabs', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('46', 'ProcessPage', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('47', 'ProcessTemplate', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('48', 'ProcessField', '32', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('50', 'ProcessModule', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('55', 'InputfieldFile', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('56', 'InputfieldImage', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('57', 'FieldtypeImage', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('60', 'InputfieldPage', '0', '{\"inputfieldClasses\":[\"InputfieldSelect\",\"InputfieldSelectMultiple\",\"InputfieldCheckboxes\",\"InputfieldRadios\",\"InputfieldAsmSelect\",\"InputfieldPageListSelect\",\"InputfieldPageListSelectMultiple\",\"InputfieldPageAutocomplete\"]}', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('61', 'TextformatterEntities', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('66', 'ProcessUser', '0', '{\"showFields\":[\"name\",\"email\",\"roles\"]}', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('67', 'MarkupAdminDataTable', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('68', 'ProcessRole', '0', '{\"showFields\":[\"name\"]}', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('76', 'ProcessList', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('78', 'InputfieldFieldset', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('79', 'InputfieldMarkup', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('80', 'InputfieldEmail', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('83', 'ProcessPageView', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('84', 'FieldtypeInteger', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('85', 'InputfieldInteger', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('86', 'InputfieldPageName', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('87', 'ProcessHome', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('89', 'FieldtypeFloat', '1', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('90', 'InputfieldFloat', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('94', 'InputfieldDatetime', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('97', 'FieldtypeCheckbox', '1', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('98', 'MarkupPagerNav', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('103', 'JqueryTableSorter', '1', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('104', 'ProcessPageSearch', '1', '{\"searchFields\":\"title\",\"displayField\":\"title path\"}', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('105', 'FieldtypeFieldsetOpen', '1', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('106', 'FieldtypeFieldsetClose', '1', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('107', 'FieldtypeFieldsetTabOpen', '1', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('108', 'InputfieldURL', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('109', 'ProcessPageTrash', '1', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('111', 'FieldtypePageTitle', '1', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('112', 'InputfieldPageTitle', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('113', 'MarkupPageArray', '3', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('114', 'PagePermissions', '3', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('115', 'PageRender', '3', '{\"clearCache\":1}', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('116', 'JqueryCore', '1', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('117', 'JqueryUI', '1', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('121', 'ProcessPageEditLink', '1', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('122', 'InputfieldPassword', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('125', 'SessionLoginThrottle', '11', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('129', 'ProcessPageEditImageSelect', '1', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('131', 'InputfieldButton', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('133', 'FieldtypePassword', '1', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('134', 'ProcessPageType', '33', '{\"showFields\":[]}', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('135', 'FieldtypeURL', '1', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('136', 'ProcessPermission', '1', '{\"showFields\":[\"name\",\"title\"]}', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('137', 'InputfieldPageListSelectMultiple', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('138', 'ProcessProfile', '1', '{\"profileFields\":[\"pass\",\"email\"]}', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('139', 'SystemUpdater', '1', '{\"systemVersion\":15,\"coreVersion\":\"3.0.50\"}', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('148', 'AdminThemeDefault', '10', '{\"colors\":\"classic\"}', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('149', 'InputfieldSelector', '42', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('150', 'ProcessPageLister', '32', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('151', 'JqueryMagnific', '1', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('152', 'PagePathHistory', '3', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('155', 'InputfieldCKEditor', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('156', 'MarkupHTMLPurifier', '0', '', '2017-01-24 06:11:43');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('158', 'ProcessRecentPages', '1', '', '2017-01-24 06:12:09');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('159', 'ProcessLogger', '1', '', '2017-01-24 06:12:17');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('160', 'InputfieldIcon', '0', '', '2017-01-24 06:12:17');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('161', 'FieldtypeComments', '1', '', '2017-01-26 11:32:48');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('162', 'InputfieldCommentsAdmin', '0', '', '2017-01-26 11:32:48');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('163', 'InputfieldPageAutocomplete', '0', '', '2017-01-27 11:18:20');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('164', 'PageFrontEdit', '2', '', '2017-01-27 11:32:31');
INSERT INTO `modules` (`id`, `class`, `flags`, `data`, `created`) VALUES('165', 'ProcessCommentsManager', '1', '', '2017-01-27 12:17:47');
DROP TABLE IF EXISTS `page_path_history`;
CREATE TABLE `page_path_history` (
`path` varchar(191) NOT NULL,
`pages_id` int(10) unsigned NOT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`path`),
KEY `pages_id` (`pages_id`),
KEY `created` (`created`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `pages`;
CREATE TABLE `pages` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`parent_id` int(11) unsigned NOT NULL DEFAULT '0',
`templates_id` int(11) unsigned NOT NULL DEFAULT '0',
`name` varchar(128) CHARACTER SET ascii NOT NULL,
`status` int(10) unsigned NOT NULL DEFAULT '1',
`modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`modified_users_id` int(10) unsigned NOT NULL DEFAULT '2',
`created` timestamp NOT NULL DEFAULT '2015-12-18 06:09:00',
`created_users_id` int(10) unsigned NOT NULL DEFAULT '2',
`published` datetime DEFAULT NULL,
`sort` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `name_parent_id` (`name`,`parent_id`),
KEY `parent_id` (`parent_id`),
KEY `templates_id` (`templates_id`),
KEY `modified` (`modified`),
KEY `created` (`created`),
KEY `status` (`status`),
KEY `published` (`published`)
) ENGINE=MyISAM AUTO_INCREMENT=1029 DEFAULT CHARSET=utf8;
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1', '0', '1', 'home', '9', '2017-01-27 13:29:31', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '0');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('2', '1', '2', 'processwire', '1035', '2017-01-24 06:12:10', '40', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '6');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('3', '2', '2', 'page', '21', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '0');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('6', '3', '2', 'add', '21', '2017-01-24 06:12:22', '40', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '0');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('7', '1', '2', 'trash', '1039', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '7');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('8', '3', '2', 'list', '1045', '2017-01-24 06:15:58', '40', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '1');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('9', '3', '2', 'sort', '1047', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '2');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('10', '3', '2', 'edit', '1045', '2017-01-24 06:15:58', '40', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '3');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('11', '22', '2', 'template', '21', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '0');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('16', '22', '2', 'field', '21', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '2');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('21', '2', '2', 'module', '21', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '2');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('22', '2', '2', 'setup', '21', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '1');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('23', '2', '2', 'login', '1035', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '4');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('27', '1', '29', 'http404', '1035', '2017-01-27 12:25:04', '41', '2017-01-24 06:11:43', '3', '2017-01-24 06:11:43', '5');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('28', '2', '2', 'access', '13', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '3');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('29', '28', '2', 'users', '29', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '0');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('30', '28', '2', 'roles', '29', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '1');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('31', '28', '2', 'permissions', '29', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '2');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('32', '31', '5', 'page-edit', '25', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '2');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('34', '31', '5', 'page-delete', '25', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '3');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('35', '31', '5', 'page-move', '25', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '4');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('36', '31', '5', 'page-view', '25', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '0');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('37', '30', '4', 'guest', '25', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '0');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('38', '30', '4', 'superuser', '25', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '1');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('40', '29', '3', 'guest', '25', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '1');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('41', '29', '3', 'admin', '1', '2017-01-24 06:12:10', '40', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '0');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('50', '31', '5', 'page-sort', '25', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '5');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('51', '31', '5', 'page-template', '25', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '6');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('52', '31', '5', 'user-admin', '25', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '10');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('53', '31', '5', 'profile-edit', '1', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '13');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('54', '31', '5', 'page-lock', '1', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '8');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('300', '3', '2', 'search', '1045', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '5');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('301', '3', '2', 'trash', '1047', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '5');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('302', '3', '2', 'link', '1041', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '6');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('303', '3', '2', 'image', '1041', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '7');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('304', '2', '2', 'profile', '1025', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '41', '2017-01-24 06:11:43', '5');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1000', '1', '26', 'search', '1025', '2017-01-26 09:55:14', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '4');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1001', '1', '29', 'about', '1', '2017-01-25 08:36:43', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '0');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1002', '1001', '29', 'what', '1', '2017-01-27 12:08:45', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '0');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1004', '1001', '29', 'background', '1', '2017-01-27 09:45:20', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '1');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1005', '1', '34', 'site-map', '1', '2017-01-26 09:55:10', '41', '2017-01-24 06:11:43', '2', '2017-01-24 06:11:43', '3');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1006', '31', '5', 'page-lister', '1', '2017-01-24 06:11:43', '40', '2017-01-24 06:11:43', '40', '2017-01-24 06:11:43', '9');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1007', '3', '2', 'lister', '1', '2017-01-24 06:11:43', '40', '2017-01-24 06:11:43', '40', '2017-01-24 06:11:43', '8');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1009', '3', '2', 'recent-pages', '1', '2017-01-24 06:12:09', '40', '2017-01-24 06:12:09', '40', '2017-01-24 06:12:09', '9');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1010', '31', '5', 'page-edit-recent', '1', '2017-01-24 06:12:09', '40', '2017-01-24 06:12:09', '40', '2017-01-24 06:12:09', '10');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1011', '22', '2', 'logs', '1', '2017-01-24 06:12:17', '40', '2017-01-24 06:12:17', '40', '2017-01-24 06:12:17', '2');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1012', '31', '5', 'logs-view', '1', '2017-01-24 06:12:17', '40', '2017-01-24 06:12:17', '40', '2017-01-24 06:12:17', '11');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1013', '31', '5', 'logs-edit', '1', '2017-01-24 06:12:17', '40', '2017-01-24 06:12:17', '40', '2017-01-24 06:12:17', '12');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1014', '1', '44', 'blog', '1', '2017-01-25 15:22:52', '41', '2017-01-25 15:22:52', '41', '2017-01-25 15:22:52', '1');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1015', '1014', '43', 'phase-data-extended-transaction', '1', '2017-01-26 06:18:13', '41', '2017-01-25 15:23:04', '41', '2017-01-25 15:23:20', '0');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1016', '1', '45', 'categories', '1', '2017-01-26 05:55:33', '41', '2017-01-26 05:54:06', '41', '2017-01-26 05:54:06', '2');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1017', '1016', '46', 'coffee', '1', '2017-01-26 05:54:49', '41', '2017-01-26 05:54:46', '41', '2017-01-26 05:54:46', '0');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1018', '1016', '46', 'beer', '1', '2017-01-26 05:54:53', '41', '2017-01-26 05:54:53', '41', '2017-01-26 05:54:53', '1');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1019', '1016', '46', 'plants', '1', '2017-01-26 05:56:01', '41', '2017-01-26 05:56:01', '41', '2017-01-26 05:56:01', '2');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1020', '1016', '46', 'cats', '1', '2017-01-26 06:10:41', '41', '2017-01-26 06:10:41', '41', '2017-01-26 06:10:41', '3');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1021', '1014', '43', 'think-affordable-artificial-blast', '1', '2017-01-27 12:37:31', '41', '2017-01-26 06:38:37', '41', '2017-01-26 06:39:03', '1');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1022', '1014', '43', 'genuine-symphony', '1', '2017-01-27 12:18:48', '41', '2017-01-26 09:50:20', '41', '2017-01-26 09:50:54', '2');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1023', '31', '5', 'page-edit-front', '1', '2017-01-27 11:32:31', '41', '2017-01-27 11:32:31', '41', '2017-01-27 11:32:31', '13');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1024', '1001', '47', 'front-end-editor-demo', '1', '2017-01-27 12:31:38', '41', '2017-01-27 12:01:56', '41', '2017-01-27 12:03:43', '2');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1025', '22', '2', 'comments', '1', '2017-01-27 12:17:47', '41', '2017-01-27 12:17:47', '41', '2017-01-27 12:17:47', '3');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1026', '31', '5', 'comments-manager', '1', '2017-01-27 12:17:47', '41', '2017-01-27 12:17:47', '41', '2017-01-27 12:17:47', '14');
INSERT INTO `pages` (`id`, `parent_id`, `templates_id`, `name`, `status`, `modified`, `modified_users_id`, `created`, `created_users_id`, `published`, `sort`) VALUES('1027', '1016', '46', 'recipes', '1', '2017-01-27 12:37:06', '41', '2017-01-27 12:37:06', '41', '2017-01-27 12:37:06', '4');
DROP TABLE IF EXISTS `pages_access`;
CREATE TABLE `pages_access` (
`pages_id` int(11) NOT NULL,
`templates_id` int(11) NOT NULL,
`ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`pages_id`),
KEY `templates_id` (`templates_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('32', '2', '2017-01-24 06:11:43');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('34', '2', '2017-01-24 06:11:43');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('35', '2', '2017-01-24 06:11:43');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('36', '2', '2017-01-24 06:11:43');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('37', '2', '2017-01-24 06:11:43');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('38', '2', '2017-01-24 06:11:43');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('50', '2', '2017-01-24 06:11:43');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('51', '2', '2017-01-24 06:11:43');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('52', '2', '2017-01-24 06:11:43');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('53', '2', '2017-01-24 06:11:43');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('54', '2', '2017-01-24 06:11:43');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('1006', '2', '2017-01-24 06:11:43');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('1010', '2', '2017-01-24 06:12:09');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('1012', '2', '2017-01-24 06:12:17');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('1013', '2', '2017-01-24 06:12:17');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('1014', '1', '2017-01-25 15:22:52');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('1015', '1', '2017-01-25 15:23:04');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('1016', '1', '2017-01-26 05:54:06');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('1017', '1', '2017-01-26 05:54:46');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('1018', '1', '2017-01-26 05:54:53');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('1019', '1', '2017-01-26 05:56:01');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('1020', '1', '2017-01-26 06:10:41');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('1021', '1', '2017-01-26 06:38:37');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('1022', '1', '2017-01-26 09:50:20');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('1023', '2', '2017-01-27 11:32:31');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('1024', '1', '2017-01-27 12:01:56');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('1026', '2', '2017-01-27 12:17:47');
INSERT INTO `pages_access` (`pages_id`, `templates_id`, `ts`) VALUES('1027', '1', '2017-01-27 12:37:06');
DROP TABLE IF EXISTS `pages_parents`;
CREATE TABLE `pages_parents` (
`pages_id` int(10) unsigned NOT NULL,
`parents_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`pages_id`,`parents_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('2', '1');
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('3', '1');
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('3', '2');
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('7', '1');
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('22', '1');
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('22', '2');
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('28', '1');
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('28', '2');
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('29', '1');
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('29', '2');
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('29', '28');
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('30', '1');
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('30', '2');
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('30', '28');
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('31', '1');
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('31', '2');
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('31', '28');
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('1001', '1');
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('1002', '1');
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('1002', '1001');
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('1004', '1');
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('1004', '1001');
INSERT INTO `pages_parents` (`pages_id`, `parents_id`) VALUES('1005', '1');
DROP TABLE IF EXISTS `pages_sortfields`;
CREATE TABLE `pages_sortfields` (
`pages_id` int(10) unsigned NOT NULL DEFAULT '0',
`sortfield` varchar(20) NOT NULL DEFAULT '',
PRIMARY KEY (`pages_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `pages_sortfields` (`pages_id`, `sortfield`) VALUES('1016', 'name');
DROP TABLE IF EXISTS `session_login_throttle`;
CREATE TABLE `session_login_throttle` (
`name` varchar(128) NOT NULL,
`attempts` int(10) unsigned NOT NULL DEFAULT '0',
`last_attempt` int(10) unsigned NOT NULL,
PRIMARY KEY (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `templates`;
CREATE TABLE `templates` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(191) CHARACTER SET ascii NOT NULL,
`fieldgroups_id` int(10) unsigned NOT NULL DEFAULT '0',
`flags` int(11) NOT NULL DEFAULT '0',
`cache_time` mediumint(9) NOT NULL DEFAULT '0',
`data` text NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`),
KEY `fieldgroups_id` (`fieldgroups_id`)
) ENGINE=MyISAM AUTO_INCREMENT=48 DEFAULT CHARSET=utf8;
INSERT INTO `templates` (`id`, `name`, `fieldgroups_id`, `flags`, `cache_time`, `data`) VALUES('1', 'home', '1', '0', '0', '{\"useRoles\":1,\"noParents\":1,\"slashUrls\":1,\"compile\":3,\"label\":\"Home\",\"modified\":1485537359,\"ns\":\"ProcessWire\",\"roles\":[37]}');
INSERT INTO `templates` (`id`, `name`, `fieldgroups_id`, `flags`, `cache_time`, `data`) VALUES('2', 'admin', '2', '8', '0', '{\"useRoles\":1,\"parentTemplates\":[2],\"allowPageNum\":1,\"redirectLogin\":23,\"slashUrls\":1,\"noGlobal\":1,\"compile\":3,\"modified\":1453457709,\"ns\":\"ProcessWire\"}');
INSERT INTO `templates` (`id`, `name`, `fieldgroups_id`, `flags`, `cache_time`, `data`) VALUES('3', 'user', '3', '8', '0', '{\"useRoles\":1,\"noChildren\":1,\"parentTemplates\":[2],\"slashUrls\":1,\"pageClass\":\"User\",\"noGlobal\":1,\"noMove\":1,\"noTrash\":1,\"noSettings\":1,\"noChangeTemplate\":1,\"nameContentTab\":1}');
INSERT INTO `templates` (`id`, `name`, `fieldgroups_id`, `flags`, `cache_time`, `data`) VALUES('4', 'role', '4', '8', '0', '{\"noChildren\":1,\"parentTemplates\":[2],\"slashUrls\":1,\"pageClass\":\"Role\",\"noGlobal\":1,\"noMove\":1,\"noTrash\":1,\"noSettings\":1,\"noChangeTemplate\":1,\"nameContentTab\":1}');
INSERT INTO `templates` (`id`, `name`, `fieldgroups_id`, `flags`, `cache_time`, `data`) VALUES('5', 'permission', '5', '8', '0', '{\"noChildren\":1,\"parentTemplates\":[2],\"slashUrls\":1,\"guestSearchable\":1,\"pageClass\":\"Permission\",\"noGlobal\":1,\"noMove\":1,\"noTrash\":1,\"noSettings\":1,\"noChangeTemplate\":1,\"nameContentTab\":1}');
INSERT INTO `templates` (`id`, `name`, `fieldgroups_id`, `flags`, `cache_time`, `data`) VALUES('26', 'search', '80', '0', '0', '{\"noChildren\":1,\"noParents\":1,\"allowPageNum\":1,\"slashUrls\":1,\"compile\":3,\"label\":\"Search\",\"modified\":1485526981,\"ns\":\"ProcessWire\"}');
INSERT INTO `templates` (`id`, `name`, `fieldgroups_id`, `flags`, `cache_time`, `data`) VALUES('29', 'basic-page', '83', '0', '0', '{\"slashUrls\":1,\"compile\":3,\"label\":\"Basic page\",\"modified\":1485526981,\"ns\":\"ProcessWire\"}');
INSERT INTO `templates` (`id`, `name`, `fieldgroups_id`, `flags`, `cache_time`, `data`) VALUES('34', 'sitemap', '88', '0', '0', '{\"noChildren\":1,\"noParents\":1,\"redirectLogin\":23,\"slashUrls\":1,\"compile\":3,\"label\":\"Sitemap\",\"modified\":1485427810,\"ns\":\"ProcessWire\"}');
INSERT INTO `templates` (`id`, `name`, `fieldgroups_id`, `flags`, `cache_time`, `data`) VALUES('43', 'blog-post', '97', '0', '0', '{\"parentTemplates\":[44],\"slashUrls\":1,\"compile\":3,\"label\":\"Blog post\",\"modified\":1485532830,\"ns\":\"ProcessWire\"}');
INSERT INTO `templates` (`id`, `name`, `fieldgroups_id`, `flags`, `cache_time`, `data`) VALUES('44', 'blog', '98', '0', '0', '{\"sortfield\":\"-97\",\"noParents\":-1,\"childTemplates\":[43],\"allowPageNum\":1,\"slashUrls\":1,\"compile\":3,\"label\":\"Blog\",\"modified\":1485530079,\"ns\":\"ProcessWire\"}');
INSERT INTO `templates` (`id`, `name`, `fieldgroups_id`, `flags`, `cache_time`, `data`) VALUES('45', 'categories', '99', '0', '0', '{\"noParents\":-1,\"childTemplates\":[46],\"slashUrls\":1,\"compile\":3,\"label\":\"Categories\",\"modified\":1485541446,\"ns\":\"ProcessWire\"}');
INSERT INTO `templates` (`id`, `name`, `fieldgroups_id`, `flags`, `cache_time`, `data`) VALUES('46', 'category', '100', '0', '0', '{\"noChildren\":1,\"parentTemplates\":[45],\"allowPageNum\":1,\"slashUrls\":1,\"compile\":3,\"label\":\"Category\",\"modified\":1485530079,\"ns\":\"ProcessWire\"}');
INSERT INTO `templates` (`id`, `name`, `fieldgroups_id`, `flags`, `cache_time`, `data`) VALUES('47', 'basic-page-edit', '101', '0', '0', '{\"slashUrls\":1,\"compile\":3,\"label\":\"Basic page (front-end editable)\",\"modified\":1485536717,\"ns\":\"ProcessWire\"}');
UPDATE pages SET created_users_id=41, modified_users_id=41, created=NOW(), modified=NOW();
# --- /WireDatabaseBackup {"numTables":26,"numCreateTables":33,"numInserts":416,"numSeconds":0}

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

View File

@@ -0,0 +1,88 @@
ABOUT /SITE/MODULES/
====================
This directory /site/modules/ is where you may install additional plugin modules.
These modules are specific to your site only. There is also a corresponding
/wire/modules/ directory, which contains ProcessWire's core modules (and best to
leave those alone).
If safe for your hosting environment, you may wish to make this directory
writable to PHP so that the installation of your modules can be managed from
ProcessWire's admin. However, this is not necessarily safe in all shared hosting
environments and is completely optional.
Where to get modules?
---------------------
Visit the modules directory at: http://modules.processwire.com
Installing modules from the ProcessWire admin
---------------------------------------------
If your /site/modules/ directory is writable, you can install modules from
ProcessWire's admin directly from the Modules Directory, from a ZIP file or from
a URL to a ZIP file. In your ProcessWire admin, see Modules > New for
installation options.
Installing modules from the file system
---------------------------------------
Each module (and any related files) should live in a directory of its own. The
directory should generally carry the same name as the module. For instance, if
you are installing a module named ProcessDatabaseBackups.module, then it should
live in the directory /site/modules/ProcessDatabaseBackups/.
Once you have placed a new module in this directory, you need to let ProcessWire
know about it. Login to the admin and click "Modules". Then click the "Check for
new modules" button. It will find your new module(s). Click the "Install" button
next to any new modules that you want to install.
Removing modules
----------------
The first step in removing a module is to uninstall it from ProcessWire (if it
isn't already). You do this by going to the "Modules" page, and "Site" tab in
your ProcessWire admin. Click the "Uninstall" button next to the module you
want to remove.
After the module is uninstalled, you may remove the module files. If your
modules file system is writable to ProcessWire, it will give you a "Delete"
button next to the module in your "Modules" admin page. You may click that to
remove the module files.
If your file system is not writable, you may remove the module files manually
from the file system (via SFTP or whatever tool you are using to manage your
files on the server).
Interested in learning how to make your own modules?
----------------------------------------------------
We've created two "Hello World" modules as examples for those interested in
learning module development:
- Helloworld.module demonstrates the basics of modules and hooks.
http://modules.processwire.com/modules/helloworld/
- ProcessHello.module demonstrates the basics of how to create a Process
module. Process modules are those that create applications in the admin.
http://modules.processwire.com/modules/process-hello/
There is a module development forum located at:
https://processwire.com/talk/forum/19-moduleplugin-development/
For a tutorial on how to create modules, see:
http://wiki.processwire.com/index.php/Module_Creation
Additional resources
--------------------
To find and download new modules, see the modules directory at:
http://modules.processwire.com/
For more information about modules, see the documentation at:
http://processwire.com/api/modules/
For discussion and support of modules, see:
http://processwire.com/talk/forum/4-modulesplugins/

47
site-regular/ready.php Normal file
View File

@@ -0,0 +1,47 @@
<?php namespace ProcessWire;
/**
* ProcessWire Bootstrap API Ready
* ===============================
* This ready.php file is called during ProcessWire bootstrap initialization process.
* This occurs after the current page has been determined and the API is fully ready
* to use, but before the current page has started rendering. This file receives a
* copy of all ProcessWire API variables. This file is an idea place for adding your
* own hook methods.
*
*/
if(!defined("PROCESSWIRE")) die();
/** @var ProcessWire $wire */
/**
* Example of a custom hook method
*
* This hook adds a “numPosts” method to pages using template “category”.
* The return value is the quantity of posts in category.
*
* Usage:
* ~~~~~
* $numPosts = $page->numPosts(); // returns integer
* numPosts = $page->numPosts(true); // returns string like "5 posts"
* ~~~~~
*
*/
$wire->addHook('Page(template=category)::numPosts', function($event) {
/** @var Page $page */
$page = $event->object;
// only category pages have numPosts
if($page->template != 'category') return;
// find number of posts
$numPosts = $event->pages->count("template=blog-post, categories=$page");
if($event->arguments(0) === true) {
// if true argument was specified, format it as a "5 posts" type string
$numPosts = sprintf(_n('%d post', '%d posts', $numPosts), $numPosts);
}
$event->return = $numPosts;
});

View File

@@ -0,0 +1,11 @@
<?php namespace ProcessWire;
/**
* This _init.php file is called automatically by ProcessWire before every page render
*
*/
/** @var ProcessWire $wire */
include_once('./_uikit.php');

View File

@@ -0,0 +1,137 @@
<?php namespace ProcessWire;
// _main.php template file, called after a pages template file
$home = pages()->get('/'); // homepage
$siteTitle = 'Regular';
$siteTagline = $home->summary;
// as a convenience, set location of our 3rd party resources (Uikit and jQuery)...
urls()->set('uikit', 'wire/modules/AdminTheme/AdminThemeUikit/uikit/dist/');
urls()->set('jquery', 'wire/modules/Jquery/JqueryCore/JqueryCore.js');
// ...or if you prefer to use CDN hosted resources, use these instead:
// urls()->set('uikit', 'https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.40/');
// urls()->set('jquery', 'https://code.jquery.com/jquery-2.2.4.min.js');
?><!DOCTYPE html>
<html lang='en'>
<head id='html-head'>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title id='html-title'><?=page()->title?></title>
<meta name="description" content="<?=page()->summary?>">
<link rel="stylesheet" href="<?=urls()->uikit?>css/uikit.min.css" />
<link rel="stylesheet" href="<?=urls()->templates?>styles/main.css">
<script src="<?=urls()->jquery?>"></script>
<script src="<?=urls()->uikit?>js/uikit.min.js"></script>
<script src="<?=urls()->uikit?>js/uikit-icons.min.js"></script>
</head>
<body id='html-body'>
<!-- MASTHEAD -->
<header class='uk-background-muted'>
<div id='masthead' class="uk-container">
<h2 id='masthead-logo' class='uk-text-center uk-margin-medium-top uk-margin-small-bottom'>
<a href='<?=urls()->root?>'>
<img src='<?=urls()->templates?>styles/images/coffee4.svg' alt='coffee'><br />
</a>
<?=$siteTitle?>
</h2>
<p id='masthead-tagline' class='uk-text-center uk-text-small uk-text-muted uk-margin-remove'>
<?=$siteTagline?>
</p>
<nav id='masthead-navbar' class="uk-navbar-container" uk-navbar>
<div class="uk-navbar-center uk-visible@m">
<?=ukNavbarNav($home->and($home->children), [
'dropdown' => [ 'basic-page', 'categories' ]
])?>
</div>
</nav>
</div>
</header>
<!-- MAIN CONTENT -->
<main id='main' class='uk-container uk-margin uk-margin-large-bottom'>
<?php if(page()->parent->id > $home->id) echo ukBreadcrumb(page(), [ 'class' => 'uk-visible@m' ]); ?>
<div class='uk-grid-large' uk-grid>
<div id='content' class='uk-width-expand'>
<h1 id='content-head' class='uk-margin-small-top'>
<?=page()->get('headline|title')?>
</h1>
<div id='content-body'>
<?=page()->body?>
</div>
</div>
<aside id='sidebar' class='uk-width-1-3@m'>
<?=page()->sidebar?>
</aside>
</div>
</main>
<?php if(config()->debug && user()->isSuperuser()): // display region debugging info ?>
<section id='debug' class='uk-section uk-section-muted'>
<div class='uk-container'>
<!--PW-REGION-DEBUG-->
</div>
</section>
<?php endif; ?>
<!-- FOOTER -->
<footer class='uk-section uk-section-secondary'>
<div id='footer' class='uk-container'>
<div uk-grid>
<div class='uk-width-1-3@m uk-flex-last@m uk-text-center'>
<form class='uk-search uk-search-default' action='<?=pages()->get('template=search')->url?>' method='get'>
<button type='submit' class='uk-search-toggle uk-search-icon-flip' uk-search-icon></button>
<input type='search' id='search-query' name='q' class='uk-search-input' placeholder='Search&hellip;'>
</form>
</div>
<div class='uk-width-2-3@m uk-flex-first@m uk-text-center uk-text-left@m'>
<h3 class='uk-margin-remove'>
<?=$siteTitle?>
<small class='uk-text-small uk-text-muted'><?=$siteTagline?></small>
</h3>
<p class='uk-margin-remove'>
<small class='uk-text-small uk-text-muted'>&copy; <?=date('Y')?> &bull;</small>
<a href='https://processwire.com'>Powered by ProcessWire CMS</a>
</p>
</div>
</div>
</div>
</footer>
<!-- OFFCANVAS NAV TOGGLE -->
<a id='offcanvas-toggle' class='uk-hidden@m' href="#offcanvas-nav" uk-toggle>
<?=ukIcon('menu', 1.3)?>
</a>
<!-- OFFCANVAS NAVIGATION -->
<div id="offcanvas-nav" uk-offcanvas>
<div class="uk-offcanvas-bar">
<h3><a href='<?=urls()->root?>'><?=$siteTitle?></a></h3>
<?php
// offcanvas navigation
// example of caching generated markup (for 600 seconds/10 minutes)
echo cache()->get('offcanvas-nav', 10, function() {
return ukNav(pages()->get('/')->children(), [
'depth' => 1,
'accordion' => true,
'blockParents' => [ 'blog' ],
'repeatParent' => true,
'noNavQty' => 20
]);
});
?>
</div>
</div>
<?php if(page()->editable): ?>
<!-- PAGE EDIT LINK -->
<a id='edit-page' href='<?=page()->editUrl?>'>
<?=ukIcon('pencil')?> Edit
</a>
<?php endif; ?>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,15 @@
<?php namespace ProcessWire;
/**
* Admin template just loads the admin application controller,
* and admin is just an application built on top of ProcessWire.
*
* This demonstrates how you can use ProcessWire as a front-end
* to another application.
*
* Feel free to hook admin-specific functionality from this file,
* but remember to leave the require() statement below at the end.
*
*/
require($config->paths->adminTemplates . 'controller.php');

View File

@@ -0,0 +1,21 @@
<?php namespace ProcessWire;
// this template is very much like the basic-page template except that it
// demonstrates making the headline, body and sidebar fields editable on the
// front-end, using the <edit> tags
?>
<h1 id='content-head'>
<edit field='headline'><?=page()->headline?></edit>
</h1>
<div id='content-body'>
<edit field='body'><?=page()->body?></edit>
<?=ukDescriptionListPages(page()->children)?>
</div>
<aside id='sidebar'>
<?=ukNav(page()->rootParent, "depth=3, class=uk-margin-medium-bottom")?>
<edit field='sidebar'><?=page()->sidebar?></edit>
</aside>

View File

@@ -0,0 +1,10 @@
<?php namespace ProcessWire; ?>
<nav pw-append='content-body'>
<?=ukDescriptionListPages(page()->children)?>
</nav>
<aside id='sidebar' pw-prepend>
<?=ukNav(page()->rootParent, "depth=3")?>
</aside>

View File

@@ -0,0 +1,49 @@
<?php namespace ProcessWire; ?>
<head id='html-head' pw-append>
<script src='<?=urls()->FieldtypeComments?>comments.min.js'></script>
<link rel="stylesheet" href="<?=urls()->FieldtypeComments?>comments.css">
</head>
<div id='content'>
<?php
// blog post content
echo ukBlogPost(page());
// comments
$comments = page()->comments;
// comment list
if(count($comments)) {
echo ukHeading3("Comments", "icon=comments");
echo ukComments($comments);
}
// comment form
echo ukHeading3("Post a comment", "icon=comment");
echo ukCommentForm($comments);
// link to the next blog post, if there is one
$nextPost = page()->next();
if($nextPost->id): ?>
<p class='next-blog-post'>
Next <?=ukIcon('chevron-right')?>
<a href='<?=$nextPost->url?>'><?=$nextPost->title?></a>
</p>
<?php endif; ?>
</div>
<aside id='sidebar' pw-prepend>
<?php
$img = page()->images->first();
if($img) {
$img = $img->width(600);
echo "<p class='uk-text-center'><img src='$img->url' alt='$img->description'></p>";
}
?>
<?=ukNav(page()->parent->children('limit=10'), [ 'heading' => 'Recent blog posts' ])?>
<p><a href='<?=page()->parent->url?>'>More posts<?=ukIcon('arrow-right')?></a></p>
</aside>

View File

@@ -0,0 +1,20 @@
<?php namespace ProcessWire;
// This is the template file for main /blog/ page that lists blog post summaries.
// If there are more than 10 posts, it also paginates them.
?>
<div id='content'>
<?php
echo ukHeading1(page()->title, 'divider');
$posts = page()->children('limit=10');
echo ukBlogPosts($posts);
?>
</div>
<aside id='sidebar'>
<?php
$categories = pages()->get('/categories/');
echo ukNav($categories->children, [ 'header' => $categories->title ]);
?>
</aside>

View File

@@ -0,0 +1,16 @@
<?php namespace ProcessWire; ?>
<div class='uk-child-width-1-2@s uk-child-width-1-3@m uk-grid-match uk-margin-large-bottom' pw-append='content' uk-grid>
<?php foreach(page()->children as $category): ?>
<a class='uk-link-reset' href='<?=$category->url?>'>
<div class='uk-card uk-card-default uk-card-hover uk-card-body'>
<h3 class='uk-card-title uk-margin-remove'><?=$category->title?></h3>
<span class='uk-text-muted'><?=$category->numPosts(true)?></span>
</div>
</a>
<?php endforeach; ?>
</div>
<aside id='sidebar'>
<?=ukNav(pages()->get('/blog/')->children('limit=3'), [ 'header' => 'Recent posts' ])?>
</aside>

View File

@@ -0,0 +1,16 @@
<?php namespace ProcessWire; ?>
<div id='content'>
<?php
echo ukHeading1(page()->title, 'divider');
$posts = pages()->get('/blog/')->children("categories=$page, limit=10");
echo ukBlogPosts($posts);
?>
</div>
<aside id='sidebar'>
<?php
$categories = page()->parent->children();
echo ukNav($categories);
?>
</aside>

View File

@@ -0,0 +1,11 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html>
<head>
<title>500 Internal Server Error</title>
</head>
<body>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error or misconfiguration and was unable to complete your request.</p>
<p>{message}</p>
</body>
</html>

View File

@@ -0,0 +1,21 @@
When a fatal error occurs, ProcessWire displays the message:
"Unable to complete this request due to an error."
The message is intentionally vague for security purposes.
Details will be logged to /site/assets/logs/errors.txt.
When present in this directory, the file 500.html will be
displayed instead of the generic error message above. Feel
free to modify this file to show whatever you would like.
Please note the following:
* 500.html is plain HTML and has no PHP or API access.
* You may enter the tag {message} and ProcessWire will
replace this with additional details when applicable.
When not applicable, it will make it blank.
* If you are logged in as an admin, ProcessWire will
give you a detailed error message rather than 500.html.

View File

@@ -0,0 +1,32 @@
<?php namespace ProcessWire;
// get most recent blog post
$blog = pages()->get('/blog/');
$blogPost = $blog->child();
?>
<h1 id='content-head'>
<?=page()->headline?>
</h1>
<div class='uk-margin-top' id='content-body'>
<?=page()->body?>
<hr>
<p class='uk-margin-small'>
<a class='uk-button uk-button-link uk-link-muted' href='<?=$blog->url?>'>
In the blog
</a>
</p>
<?=ukBlogPost($blogPost)?>
<p class='uk-margin-small'>
<a href='<?=$blog->url?>'>More blog posts <?=ukIcon('arrow-right')?></a>
</p>
</div>
<aside id='sidebar'>
<?=ukNav(pages()->get('/categories/')->children)?>
<div class='uk-card uk-card-default uk-card-hover uk-card-body uk-margin-medium-top'>
<?=page()->sidebar?>
</div>
</aside>

View File

@@ -0,0 +1,4 @@
We typically use this directory for javascript files,
but this site profile does not have any at present.
This file is a placeholder so that this directory
exists in Git.

View File

@@ -0,0 +1,49 @@
<?php namespace ProcessWire;
// look for a GET variable named 'q' and sanitize it
$q = input()->get('q');
// sanitize to text, which removes markup, newlines, too long, etc.
$q = sanitizer()->text($q);
// did $q have anything in it after sanitizing to text?
if($q) {
// Make the search query appear in the top-right search box.
// Always entity encode any user input that also gets output
echo '<input id="search-query" value="' . sanitizer()->entities($q) . '">';
// Sanitize for placement within a selector string. This is important for any
// values that you plan to bundle in a selector string like we are doing here.
// It quotes them when necessary, and removes characters that might cause issues.
$q = sanitizer()->selectorValue($q);
// Search the title and body fields for our query text.
// Limit the results to 50 pages. The has_parent!=2 excludes irrelevant admin
// pages from the search, for when an admin user performs a search.
$selector = "title|body~=$q, limit=50, has_parent!=2";
// Find pages that match the selector
$matches = pages()->find($selector);
} else {
$matches = array();
}
// unset the variable that we no longer need, since it can contain user input
unset($q);
?>
<div id='content-body'>
<?php
// did we find any matches?
if(count($matches)) {
// yes we did, render them
echo ukAlert("Found $matches->count page(s)", "default", "search");
echo ukDescriptionListPages($matches);
} else {
// we didn't find any
echo ukAlert("Sorry, no results were found.", "danger", "warning");
}
?>
</div>

View File

@@ -0,0 +1,9 @@
<?php namespace ProcessWire; ?>
<div id='content-body'>
<?php
$home = pages()->get('/');
echo ukNav($home, [ 'depth' => 4 ])
?>
</div>

View File

@@ -0,0 +1,382 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:ns1="http://sozi.baierouge.fr"
id="svg2"
sodipodi:docname="coffeecup-3d.svg"
inkscape:export-filename="D:\mocholand\Development\vectorial-cofee-cup\coffeecup-3d.png"
viewBox="0 0 444.47 310.87"
inkscape:export-xdpi="90"
version="1.1"
inkscape:export-ydpi="90"
inkscape:version="0.47pre3 "
>
<defs
id="defs4"
>
<radialGradient
id="radialGradient4309"
gradientUnits="userSpaceOnUse"
cy="351.05"
cx="368.58"
gradientTransform="matrix(1 0 0 .54810 0 141.63)"
r="185.64"
inkscape:collect="always"
>
<stop
id="stop4197"
style="stop-color:#e2e2e2"
offset="0"
/>
<stop
id="stop4199"
style="stop-color:#989898"
offset="1"
/>
</radialGradient
>
<linearGradient
id="linearGradient4311"
y2="321.49"
gradientUnits="userSpaceOnUse"
x2="369.95"
y1="346.56"
x1="369.95"
inkscape:collect="always"
>
<stop
id="stop4220"
style="stop-color:#838383"
offset="0"
/>
<stop
id="stop4222"
style="stop-color:#c6c6c6;stop-opacity:0"
offset="1"
/>
</linearGradient
>
<linearGradient
id="linearGradient4313"
y2="378.81"
gradientUnits="userSpaceOnUse"
x2="388.13"
gradientTransform="translate(0 -60)"
y1="327.72"
x1="368.58"
inkscape:collect="always"
>
<stop
id="stop4265"
style="stop-color:#461f00"
offset="0"
/>
<stop
id="stop4267"
style="stop-color:#753300"
offset="1"
/>
</linearGradient
>
<linearGradient
id="linearGradient4315"
y2="327.72"
gradientUnits="userSpaceOnUse"
x2="368.58"
gradientTransform="translate(0 -60)"
y1="362.58"
x1="368.58"
inkscape:collect="always"
>
<stop
id="stop4275"
style="stop-color:#ffffff"
offset="0"
/>
<stop
id="stop4277"
style="stop-color:#ffffff;stop-opacity:0"
offset="1"
/>
</linearGradient
>
<filter
id="filter4335"
height="2.8094"
width="1.3596"
color-interpolation-filters="sRGB"
y="-.90469"
x="-.17981"
inkscape:collect="always"
>
<feGaussianBlur
id="feGaussianBlur4337"
stdDeviation="12.0625"
inkscape:collect="always"
/>
</filter
>
<radialGradient
id="radialGradient4339"
gradientUnits="userSpaceOnUse"
cy="582.55"
cx="408.1"
gradientTransform="matrix(1 0 0 .18691 0 409.16)"
r="222.23"
inkscape:collect="always"
>
<stop
id="stop4109"
style="stop-color:#e9e9e9"
offset="0"
/>
<stop
id="stop4119"
style="stop-color:#b3b3b3"
offset="1"
/>
</radialGradient
>
</defs
>
<sodipodi:namedview
id="base"
bordercolor="#666666"
inkscape:pageshadow="2"
inkscape:window-y="-4"
pagecolor="#ffffff"
inkscape:window-height="816"
inkscape:window-maximized="1"
inkscape:zoom="1"
inkscape:window-x="-4"
showgrid="false"
borderopacity="1.0"
inkscape:current-layer="layer1"
inkscape:cx="238.91057"
inkscape:cy="148.53567"
inkscape:window-width="1152"
inkscape:pageopacity="0.0"
inkscape:document-units="px"
/>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(-145.59 -250.53)"
>
<path
id="path4182"
sodipodi:rx="222.23357"
sodipodi:ry="62.629459"
style="fill-rule:evenodd;fill:#595959"
sodipodi:type="arc"
d="m630.34 492.74c0 34.589-99.497 62.629-222.23 62.629-122.74 0-222.23-28.04-222.23-62.629s99.497-62.629 222.23-62.629c122.74 0 222.23 28.04 222.23 62.629z"
transform="matrix(.94938 0 0 1.0617 -20.871 -28.258)"
sodipodi:cy="492.73767"
sodipodi:cx="408.10162"
/>
<path
id="path4159"
sodipodi:rx="222.23357"
sodipodi:ry="62.629459"
style="fill-rule:evenodd;fill:#f2f2f2"
sodipodi:type="arc"
d="m630.34 492.74c0 34.589-99.497 62.629-222.23 62.629-122.74 0-222.23-28.04-222.23-62.629s99.497-62.629 222.23-62.629c122.74 0 222.23 28.04 222.23 62.629z"
transform="translate(-40.279 -1.7029)"
sodipodi:cy="492.73767"
sodipodi:cx="408.10162"
/>
<path
id="path3333"
sodipodi:rx="222.23357"
sodipodi:ry="62.629459"
style="fill-rule:evenodd;fill:url(#radialGradient4339)"
sodipodi:type="arc"
d="m630.34 492.74c0 34.589-99.497 62.629-222.23 62.629-122.74 0-222.23-28.04-222.23-62.629s99.497-62.629 222.23-62.629c122.74 0 222.23 28.04 222.23 62.629z"
transform="translate(-40.279 -5.7029)"
sodipodi:cy="492.73767"
sodipodi:cx="408.10162"
/>
<path
id="path4186"
sodipodi:rx="123.3854"
sodipodi:ry="29.52813"
style="fill-opacity:.40698;fill:#858585"
sodipodi:type="arc"
d="m487.21 550.38c0 16.308-55.242 29.528-123.39 29.528-68.144 0-123.39-13.22-123.39-29.528s55.242-29.528 123.39-29.528c68.144 0 123.39 13.22 123.39 29.528z"
transform="translate(6 -66)"
sodipodi:cy="550.38397"
sodipodi:cx="363.82874"
/>
<path
id="path4317"
sodipodi:rx="80.5"
sodipodi:ry="16"
style="opacity:.75;filter:url(#filter4335);fill:#000000"
sodipodi:type="arc"
d="m448 483.36c0 8.8366-36.041 16-80.5 16s-80.5-7.1634-80.5-16 36.041-16 80.5-16 80.5 7.1634 80.5 16z"
transform="matrix(.93409 0 0 .91146 23.008 44.818)"
sodipodi:cy="483.36218"
sodipodi:cx="367.5"
/>
<g
id="g4295"
transform="translate(0,4)"
>
<path
id="path4188"
style="fill:url(#radialGradient4309)"
d="m182.94 284.44c18.733 116.39 94.715 203.5 185.62 203.5 90.911 0 166.92-87.107 185.66-203.5h-371.28z"
/>
<path
id="path4193"
sodipodi:rx="181.46394"
sodipodi:ry="25.064081"
style="fill:#eaeaea"
sodipodi:type="arc"
d="m551.41 346.56c0 13.843-81.244 25.064-181.46 25.064s-181.46-11.222-181.46-25.064c0-13.843 81.244-25.064 181.46-25.064s181.46 11.222 181.46 25.064z"
transform="matrix(1.0234 0 0 1.6116 -10.017 -271.57)"
sodipodi:cy="346.55768"
sodipodi:cx="369.94583"
/>
<path
id="path4216"
sodipodi:rx="181.46394"
sodipodi:ry="25.064081"
style="fill:url(#linearGradient4311)"
sodipodi:type="arc"
d="m551.41 346.56c0 13.843-81.244 25.064-181.46 25.064s-181.46-11.222-181.46-25.064c0-13.843 81.244-25.064 181.46-25.064s181.46 11.222 181.46 25.064z"
transform="matrix(.98128 0 0 1.4117 5.5602 -205.8)"
sodipodi:cy="346.55768"
sodipodi:cx="369.94583"
/>
<path
id="path4255"
style="fill:url(#linearGradient4313)"
d="m368.59 267.72c-91.89 0-166.42 12.631-166.62 28.219 25.429 13.369 90.44 22.875 166.62 22.875 76.175 0 141.16-9.5089 166.59-22.875-0.2008-15.588-74.704-28.219-166.59-28.219z"
/>
<path
id="path4271"
sodipodi:nodetypes="cccc"
style="opacity:0.04;fill:url(#linearGradient4315)"
d="m368.59 267.72c-87.769 0-159.71 11.539-166.16 26.156 107.47-8.4524 217.07-11.433 332.28 0-6.4479-14.617-78.356-26.156-166.12-26.156z"
/>
<path
id="path4293"
sodipodi:nodetypes="cccc"
style="fill-opacity:.15116;fill:#ffffff"
d="m213.75 309.23c9.0001 2.9648 14.241 3.9652 20.516 5.5258 0 0 1.2916 65.75 23.592 103.41-24.432-33.615-44.951-65.463-44.108-108.94z"
/>
</g
>
</g
>
<metadata
>
<rdf:RDF
>
<cc:Work
>
<dc:format
>image/svg+xml</dc:format
>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage"
/>
<cc:license
rdf:resource="http://creativecommons.org/licenses/publicdomain/"
/>
<dc:publisher
>
<cc:Agent
rdf:about="http://openclipart.org/"
>
<dc:title
>Openclipart</dc:title
>
</cc:Agent
>
</dc:publisher
>
<dc:title
>Realistic Coffee cup - Front 3D view</dc:title
>
<dc:date
>2009-10-17T06:56:28</dc:date
>
<dc:description
>Realistic Coffee cup, with a shiny 3d look, front view.</dc:description
>
<dc:source
>https://openclipart.org/detail/27828/realistic-coffee-cup---front-3d-view-by-mokush</dc:source
>
<dc:creator
>
<cc:Agent
>
<dc:title
>mokush</dc:title
>
</cc:Agent
>
</dc:creator
>
<dc:subject
>
<rdf:Bag
>
<rdf:li
>cofe</rdf:li
>
<rdf:li
>cofee</rdf:li
>
<rdf:li
>coffe</rdf:li
>
<rdf:li
>coffee</rdf:li
>
<rdf:li
>coffeecup</rdf:li
>
<rdf:li
>cup</rdf:li
>
<rdf:li
>photorealistic</rdf:li
>
</rdf:Bag
>
</dc:subject
>
</cc:Work
>
<cc:License
rdf:about="http://creativecommons.org/licenses/publicdomain/"
>
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction"
/>
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution"
/>
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks"
/>
</cc:License
>
</rdf:RDF
>
</metadata
>
</svg
>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,111 @@
#html-body, h1, h2, h3, h4, h5 {
/* default font */
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
#masthead-logo img {
/* image for masthead logo */
width: 100px;
}
#edit-page {
/* the edit page link that appears when page is editable */
position: absolute;
top: 10px;
right: 10px
}
#offcanvas-toggle {
/* the hamburger icon that toggles the mobile navigation */
position: absolute;
top: 10px;
left: 15px;
}
#offcanvas-nav .uk-search-input {
/* the search box that appears in offcanvas nav */
width: 100%;
}
img.uk-comment-avatar {
/* avatar that appears in comments */
width: 60px;
height: 60px;
}
@media only screen and (max-width: 959px) {
/* custom adjustments for mobile layouts under 960px */
#masthead {
/* primary nav is not visible, so it needs some padding here */
padding-bottom: 20px;
}
}
/****************************************************************
* Bodycopy text and images
*
* These styles are good to have on any ProcessWire site
*
*/
.align_left {
/* for images placed in rich text editor */
float: left;
margin: 0 1em 0.5em 0;
position: relative;
top: 0.5em;
max-width: 50%;
}
.align_right {
/* for images placed in rich text editor */
float: right;
margin: 0 0 0.5em 1em;
max-width: 50%;
}
.align_center {
/* for images placed in rich text editor */
display: block;
margin: 1em auto;
position: relative;
top: 0.5em;
}
figure {
/* figure for image that has a caption */
display: table;
margin: 1em 0;
}
figure figcaption {
/* display caption text below image contained to image width */
display: table-caption;
caption-side: bottom;
font-size: 13px;
line-height: 1.4em;
margin-top: 5px;
color: #777;
}
@media only screen and (max-width: 767px) {
/* common PW mobile layout adjustments for widths under 768px */
.align_left, .align_right, .align_center {
/* display images in center rather than aligned */
display: block;
float: none;
margin: 1em auto;
max-width: 100%;
}
figure,
figure figcaption {
/* let figcaption display as wide as needed below image */
display: block;
text-align: center;
}
}

28
wire/.editorconfig Normal file
View File

@@ -0,0 +1,28 @@
; This file is for unifying the coding style for different editors and IDEs.
; More information at http://editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
[*.{php,inc,module}]
indent_style = tab
trim_trailing_whitespace = false
insert_final_newline = true
[*.js]
indent_style = tab
trim_trailing_whitespace = true
insert_final_newline = true
[*.{css,less,scss}]
indent_style = tab
trim_trailing_whitespace = true
insert_final_newline = true

View File

@@ -46,6 +46,9 @@ if(!defined("PROCESSWIRE")) die();
* always have this disabled for live/production sites since it reveals more information
* than is advisible for security.
*
* You may also set this to the constant `Config::debugVerbose` to enable verbose debug mode,
* which uses more memory and time.
*
* #notes This enables debug mode for ALL requests. See the debugIf option for an alternative.
*
* @var bool
@@ -111,9 +114,53 @@ $config->advanced = false;
*
* If true, disables save functions in Process modules (admin).
*
* @var bool
*
*/
$config->demo = false;
/**
* Enable core API variables to be accessed as function calls?
*
* Benefits are better type hinting, always in scope, and potentially shorter API calls.
* See the file /wire/core/FunctionsAPI.php for details on these functions.
*
* @var bool
*
*/
$config->useFunctionsAPI = false;
/**
* Enable use of front-end markup regions?
*
* When enabled, HTML elements with an "id" attribute that are output before the opening
* `<!doctype>` or `<html>` tag can replace elements in the document that have the same id.
* Also supports append, prepend, replace, remove, before and after options.
*
* @var bool
*
*/
$config->useMarkupRegions = false;
/**
* Disable all HTTPS requirements?
*
* Use this option only for development or staging environments, on sites where you are
* otherwise requiring HTTPS. By setting this option to something other than false, you
* can disable any HTTPS requirements specified per-template, enabling you to use your
* site without SSL during development or staging, etc.
*
* The following options are available:
* - boolean true: Disable HTTPS requirements globally
* - string containing hostname: Disable HTTPS requirements only for specified hostname.
* - array containing hostnames: Disable HTTPS requirements for specified hostnames.
*
* @var bool|string|array
*
*/
$config->noHTTPS = false;
/*** 2. DATES & TIMES *************************************************************************/
@@ -200,8 +247,8 @@ $config->sessionExpireSeconds = 86400;
* // if there is a session cookie, a session is likely already in use so keep it going
* if($session->hasCookie()) return true;
*
* // if URL is an admin URL, allow session
* if(strpos($_SERVER['REQUEST_URI'], $session->config->urls->admin) === 0) return true;
* // if URL is an admin URL, allow session (replace /processwire/ with your admin URL)
* if(strpos($_SERVER['REQUEST_URI'], '/processwire/) === 0) return true;
*
* // otherwise disallow session
* return false;
@@ -240,11 +287,44 @@ $config->sessionChallenge = true;
* 12: Fingerprint the forwarded/client IP and useragent
* 14: Fingerprint the remote IP, forwarded/client IP and useragent (all).
*
* If using fingerprint in an environment where the users
* IP address may change during the session, you should
* fingerprint only the useragent, or disable fingerprinting.
*
* If using fingerprint with an AWS load balancer, you should
* use one of the options that uses the “client IP” rather than
* the “remote IP”, fingerprint only the useragent, or disable
* fingerprinting.
*
* @var int
*
*/
$config->sessionFingerprint = 1;
/**
* Force current session IP address (overriding auto-detect)
*
* This overrides the return value of `$session->getIP()` method.
* Use this property only for setting the IP address. To get the IP address
* always use the `$session->getIP()` method instead.
*
* This is useful if you are in an environment where the remote IP address
* comes from some property other than the REMOTE_ADDR in $_SERVER. For instance,
* if you are using a load balancer, whats usually detected as the IP address is
* actually the IP address between the load balancer and the server, rather than
* the client IP address. So in that case, youd want to set this property as
* follows:
* ~~~~~
* $config->sessionForceIP = $_SERVER['HTTP_X_FORWARDED_FOR'];
* ~~~~~
* If you dont have a specific need to override the IP address of the user
* then you should leave this blank.
*
* @var string
*
*/
$config->sessionForceIP = '';
/**
* Use secure cookies when on HTTPS?
*
@@ -261,6 +341,17 @@ $config->sessionFingerprint = 1;
*/
$config->sessionCookieSecure = 1;
/**
* Cookie domain for sessions
*
* Enables a session to traverse multiple subdomains.
* Specify a string having “.domain.com” (with leading period) or NULL to disable (default/recommended).
*
* @var string|null
*
*/
$config->sessionCookieDomain = null;
/**
* Number of session history entries to record.
*
@@ -283,6 +374,24 @@ $config->sessionHistory = 0;
*/
$config->userAuthHashType = 'sha1';
/**
* Names (string) or IDs (int) of roles that are not allowed to login
*
* Note that you must create these roles yourself in the admin. When a user has
* one of these named roles, $session->login() will not accept a login from them.
* This affects the admin login form and any other login forms that use ProcessWires
* session system.
*
* The default value specifies a role name of "login-disabled", meaning if you create
* a role with that name, and assign it to a user, that user will no longer be able
* to login.
*
* @var array
*
*/
$config->loginDisabledRoles = array(
'login-disabled'
);
/*** 4. TEMPLATE FILES **************************************************************************/
@@ -292,6 +401,7 @@ $config->userAuthHashType = 'sha1';
*
* Set to false do disable the option for compiled template files.
* When set to true, it will be used unless a given template's 'compile' option is set to 0.
* This setting also covers system status files like /site/ready.php, /site/init.php, etc. (3.0.142+)
*
* @var bool
*
@@ -496,6 +606,8 @@ $config->fileContentTypes = array(
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/x-png',
'svg' => 'image/svg+xml',
'webp' => 'image/webp'
);
@@ -506,10 +618,12 @@ $config->fileContentTypes = array(
*
* #property bool upscaling Upscale if necessary to reach target size? (1=true, 0=false)
* #property bool cropping Crop if necessary to reach target size? (1=true, 0=false)
* #property bool autoRotation Automatically correct orientation?
* #property bool autoRotation Automatically correct orientation? (1=true, 0=false)
* #property bool interlace Use interlaced JPEGs by default? Recommended. (1=true, 0=false)
* #property string sharpening Sharpening mode, enter one of: none, soft, medium, strong
* #property int quality Image quality, enter a value between 1 and 100, where 100 is highest quality (and largest files)
* #property float defaultGamma Default gamma of 0.5 to 4.0 or -1 to disable gamma correction (default=2.0)
* #property bool webpAdd Create a WEBP copy with every new image variation? (default=false)
*
* @var array
*
@@ -518,10 +632,30 @@ $config->imageSizerOptions = array(
'upscaling' => true, // upscale if necessary to reach target size?
'cropping' => true, // crop if necessary to reach target size?
'autoRotation' => true, // automatically correct orientation?
'interlace' => false, // use interlaced JPEGs by default? (recommended)
'sharpening' => 'soft', // sharpening: none | soft | medium | strong
'quality' => 90, // quality: 1-100 where higher is better but bigger
'hidpiQuality' => 60, // Same as above quality setting, but specific to hidpi images
'defaultGamma' => 2.0, // defaultGamma: 0.5 to 4.0 or -1 to disable gamma correction (default=2.0)
'webpAdd' => false, // set this to true, if the imagesizer engines should create a Webp copy with every (new) image variation
);
/**
* Options for webp images
*
* #property int quality Quality setting where 1-100 where higher is better but bigger
* #property bool useSrcExt Use source file extension in webp filename? (file.jpg.webp rather than file.webp)
* #property bool useSrcUrlOnSize Fallback to source file URL when webp file is larger than source?
* #property bool useSrcUrlOnFail Fallback to source file URL when webp file fails for some reason?
*
* @var array
*
*/
$config->webpOptions = array(
'quality' => 90, // Quality setting of 1-100 where higher is better but bigger
'useSrcExt' => false, // Use source file extension in webp filename? (file.jpg.webp rather than file.webp)
'useSrcUrlOnSize' => true, // Fallback to source file URL when webp file is larger than source?
'useSrcUrlOnFail' => true, // Fallback to source file URL when webp file fails for some reason?
);
/**
@@ -577,8 +711,8 @@ $config->fileCompilerOptions = array(
'siteOnly' => false, // only allow compilation of files in /site/ directory
'showNotices' => true, // show notices about compiled files to superuser when logged in
'logNotices' => true, // log notices about compiled files and maintenance to file-compiler.txt log.
'chmodFile' => $config->chmodFile, // mode to use for created files, i.e. "0644"
'chmodDir' => $config->chmodDir, // mode to use for created directories, i.e. "0755"
'chmodFile' => '', // mode to use for created files, i.e. "0644"
'chmodDir' => '', // mode to use for created directories, i.e. "0755"
'exclusions' => array(), // exclude filenames or paths that start with any of these
'extensions' => array('php', 'module', 'inc'), // file extensions we compile
'cachePath' => $config->paths->cache . 'FileCompiler/', // path where compiled files are stored
@@ -650,6 +784,14 @@ $config->protectCSRF = true;
*/
$config->maxUrlSegments = 4;
/**
* Maximum length for any individual URL segment (default=128)
*
* @var int
*
*/
$config->maxUrlSegmentLength = 128;
/**
* Maximum URL/path slashes (depth) for request URLs
*
@@ -710,6 +852,16 @@ $config->pageNameCharset = 'ascii';
*/
$config->pageNameWhitelist = '-_.abcdefghijklmnopqrstuvwxyz0123456789æåäßöüđжхцчшщюяàáâèéëêěìíïîõòóôøùúûůñçčćďĺľńňŕřšťýžабвгдеёзийклмнопрстуфыэęąśłżź';
/**
* Name to use for untitled pages
*
* When page has this name, the name will be changed automatically (to a field like title) when it is possible to do so.
*
* @var string
*
*/
$config->pageNameUntitled = "untitled";
/**
* Maximum paginations
*
@@ -735,11 +887,79 @@ $config->maxPageNum = 999;
*/
$config->wireInputOrder = 'get post';
/**
* Lazy-load get/post/cookie input into $input API var?
*
* This is an experimental option for reduced memory usage when a lot of input data is present.
*
* This prevents PW from keeping separate copies of get/post/cookie data, and it instead works
* directly from the PHP $_GET, $_POST and $_COOKIE vars.
*
* This option is also useful in that anything you SET to PWs $input->get/post/cookie also gets
* set to the equivalent PHP $_GET, $_POST and $_COOKIE.
*
* @var bool
*
*/
$config->wireInputLazy = false;
/**
* Options for setting cookies from $input->cookie()->set(...)
*
* Additional details about some of these options can also be found on PHPs [setcookie](https://www.php.net/manual/en/function.setcookie.php) doc page.
*
* #property int age Max age of cookies in seconds or 0 to expire with session (3600=1hr, 86400=1day, 604800=1week, 2592000=30days, etc.)
* #property string|null Cookie path or null for PW installations root URL (default=null).
* #property string|null|bool domain Cookie domain: null for current hostname, true for all subdomains of current domain, domain.com for domain and all subdomains, www.domain.com for www subdomain.
* #property bool|null secure Transmit cookies only over secure HTTPS connection? (true, false, or null to auto-detect, using true option for cookies set when HTTPS is active).
* #property bool httponly When true, cookie is http/server-side and not visible to JS code in most browsers.
*
* @var array
* @since 3.0.141
*
*/
$config->cookieOptions = array(
'age' => 604800, // Max age of cookies in seconds or 0 to expire with session (3600=1hr, 86400=1day, 604800=1week, 2592000=30days, etc.)
'path' => null, // Cookie path/URL or null for PW installations root URL (default=null).
'domain' => null, // Cookie domain: null for current hostname, true for all subdomains of current domain, domain.com for domain and all subdomains, www.domain.com for www subdomain.
'secure' => null, // Transmit cookies only over secure HTTPS connection? (true, false, or null to auto-detect, substituting true for cookies set when HTTPS is active).
'httponly' => false, // When true, cookie is http/server-side only and not visible to client-side JS code.
'fallback' => true, // If set cookie fails (perhaps due to output already sent), attempt to set at beginning of next request? (default=true)
);
/*** 7. DATABASE ********************************************************************************/
/**
* Database name
*
*/
$config->dbName = '';
/**
* Database username
*
*/
$config->dbUser = '';
/**
* Database password
*
*/
$config->dbPass = '';
/**
* Database host
*
*/
$config->dbHost = '';
/**
* Database port
*
*/
$config->dbPort = 3306;
/**
* Database character set
*
@@ -754,9 +974,7 @@ $config->dbCharset = 'utf8';
/**
* Database engine
*
* MyISAM is the recommended value, but you may also use InnoDB (experimental).
*
* Note that use of 'InnoDB' is currently experimental. Avoid changing this after install.
* May be 'InnoDB' or 'MyISAM'. Avoid changing this after install.
*
*/
$config->dbEngine = 'MyISAM';
@@ -797,31 +1015,48 @@ $config->dbPath = '';
$config->dbLowercaseTables = true;
/**
* Database username
* Database init command (PDO::MYSQL_ATTR_INIT_COMMAND)
*
* Note: Placeholder "{charset}" gets automatically replaced with $config->dbCharset.
*
* @var string
*
*/
$config->dbUser = '';
$config->dbInitCommand = "SET NAMES '{charset}'";
/**
* Database password
* Set or adjust SQL mode per MySQL version
*
* Array indexes are minimum MySQL version mode applies to. Array values are
* the names of the mode(s) to apply. If value is preceded with "remove:" the mode will
* be removed, or if preceded with "add:" the mode will be added. If neither is present
* then the mode will be set exactly as given. To specify more than one SQL mode for the
* value, separate them by commas (CSV). To specify multiple statements for the same
* version, separate them with a slash "/".
*
* ~~~~~
* array("5.7.0" => "remove:STRICT_TRANS_TABLES,ONLY_FULL_GROUP_BY/add:NO_ZERO_DATE")
* ~~~~~
*
* @var array
*
*/
$config->dbPass = '';
$config->dbSqlModes = array(
"5.7.0" => "remove:STRICT_TRANS_TABLES,ONLY_FULL_GROUP_BY"
);
/**
* Database host
* A key=>value array of any additional driver-specific connection options.
*
* @var array
*
*/
$config->dbHost = '';
/**
* Database port
*
*/
$config->dbPort = 3306;
$config->dbOptions = array();
/**
* Optional DB socket config for sites that need it (for most you should exclude this)
*
* @var string
*
*/
$config->dbSocket = '';
@@ -834,6 +1069,17 @@ $config->dbSocket = '';
*/
$config->dbQueryLogMax = 500;
/**
* Remove 4-byte characters (like emoji) when dbEngine is not utf8mb4?
*
* When charset is not “utf8mb4” and this value is true, 4-byte UTF-8 characters are stripped
* out of inserted values when possible. Note that this can add some overhead to INSERTs.
*
* @var bool
*
*/
$config->dbStripMB4 = false;
/*** 8. MODULES *********************************************************************************/
@@ -884,6 +1130,57 @@ $config->substituteModules = array(
'InputfieldTinyMCE' => 'InputfieldCKEditor'
);
/**
* WireMail module(s) default settings
*
* Note you can add any other properties to the wireMail array that are supported by WireMail settings
* like weve done with from, fromName and headers here. Any values set here become defaults for the
* WireMail module.
*
* Blacklist property
* ==================
* The blacklist property lets you specify email addresses, domains, partial host names or regular
* expressions that prevent sending to certain email addresses. This is demonstrated by example:
* ~~~~~
* // Example of blacklist definition
* $config->wireMail('blacklist', [
* 'email@domain.com', // blacklist this email address
* '@host.domain.com', // blacklist all emails ending with @host.domain.com
* '@domain.com', // blacklist all emails ending with @domain.com
* 'domain.com', // blacklist any email address ending with domain.com (would include mydomain.com too).
* '.domain.com', // blacklist any email address at any host off domain.com (domain.com, my.domain.com, but NOT mydomain.com).
* '/something/', // blacklist any email containing "something". PCRE regex assumed when "/" is used as opening/closing delimiter.
* '/.+@really\.bad\.com$/', // another example of using a PCRE regular expression (blocks all "@really.bad.com").
* ]);
*
* // Test out the blacklist
* $email = 'somebody@bad-domain.com';
* $result = $mail->isBlacklistEmail($email, [ 'why' => true ]);
* if($result === false) {
* echo "<p>Email address is not blacklisted</p>";
* } else {
* echo "<p>Email is blacklisted by rule: $result</p>";
* }
* ~~~~~
*
* #property string module Name of WireMail module to use or blank to auto-detect. (default='')
* #property string from Default from email address, when none provided at runtime. (default=$config->adminEmail)
* #property string fromName Default from name string, when none provided at runtime. (default='')
* #property string newline What to use for newline if different from RFC standard of "\r\n" (optional).
* #property array headers Default additional headers to send in email, key=value. (default=[])
* #property array blacklist Email blacklist addresses or rules. (default=[])
*
* @var array
*
*/
$config->wireMail = array(
'module' => '',
'from' => '',
'fromName' => '',
'headers' => array(),
'blacklist' => array()
);
/**
* PageList default settings
*
@@ -914,6 +1211,7 @@ $config->pageList = array(
* #property bool confirm Notify user if they attempt to navigate away from unsaved changes?
* #property bool ajaxChildren Whether to load the 'children' tab via ajax
* #property bool ajaxParent Whether to load the 'parent' field via ajax
* #property bool editCrumbs Whether or not breadcrumbs load page editor (false=load page list).
*
* @var array
*
@@ -923,8 +1221,34 @@ $config->pageEdit = array(
'confirm' => true,
'ajaxChildren' => true,
'ajaxParent' => true,
'editCrumbs' => false,
);
/**
* PageAdd default settings
*
* #property string noSuggestTemplates Disable suggestions for new pages (1=disable all, or specify template names separated by space)
*
*/
$config->pageAdd = array(
'noSuggestTemplates' => '',
);
/**
* MarkupQA (markup quality assurance) optional settings
*
* This is used by Textarea Fieldtype when enabled and using content-type HTML.
*
* #property array ignorePaths Paths that begin with any of these will be ignored and left alone (not abstracted), i.e. [ '/a/b/', '/c/d/' ]
* #property bool debug Show debugging info to superusers? (default=false). May also be specified in $config->debugMarkupQA.
*
* @var array
*
*/
$config->markupQA = array(
// 'ignorePaths' => [ "/some/path/", "/another/path/", "/and/so/on/" ],
// 'debug' => true,
);
/*** 9. MISC ************************************************************************************/
@@ -944,12 +1268,20 @@ $config->logs = array(
'exceptions',
);
/**
* Include IP address in logs, when applicable?
*
* @var bool
*
*/
$config->logIP = false;
/**
* Default admin theme
*
* Module name of default admin theme for guest and users that haven't already selected one
*
* Core options include: **AdminThemeDefault** or **AdminThemeReno**.
* Core options include: **AdminThemeDefault** or **AdminThemeReno** or **AdminThemeUikit**.
* Additional options will depend on what other 3rd party AdminTheme modules you have installed.
*
* @var string
@@ -1063,6 +1395,14 @@ $config->allowExceptions = false;
*/
$config->usePoweredBy = true;
/**
* Chunk size for lazy-loaded pages used by $pages->findMany()
*
* @var int
*
*/
$config->lazyPageChunkSize = 250;
/**
* Settings specific to InputfieldWrapper class
*
@@ -1079,6 +1419,133 @@ $config->usePoweredBy = true;
*
*/
/**
* statusFiles: Optional automatic include files when ProcessWire reaches each status/state
*
* **Using status/state files:**
*
* - These files must be located in your /site/ directory, i.e. /site/ready.php.
* - If a file does not exist, PW will see that and ignore it.
* - For any state/status files that you dont need, it is preferable to make them
* blank or remove them, so that PW does not have to check if the file exists.
* - It is also preferable that included files have the ProcessWire namespace, and it is
* required that a `boot` file (if used) have the Processwire namespace.
* - The `init` and `ready` status files are called *after* autoload modules have had their
* corresponding methods (init or ready) called. Use `_init` or `_ready` (with leading
* underscore) as the keys to specify files that should instead be called *before* the state.
* - While php files in /site/ are blocked from direct access by the .htaccess file, its
* also a good idea to add `if(!defined("PROCESSWIRE")) die();` at the top of them.
*
* **Status files and available API variables:**
*
* - Included files receive current available ProcessWire API variables, locally scoped.
* - In the `boot` state, only $wire, $hooks, $config, $classLoader API variables are available.
* - In the `init` state, all core API variables are available, except for $page.
* - In the `ready`, `render`, `download` and `finished` states, all API variables are available.
* - In the `failed` state, an unknown set of API variables is available, so use isset() to check.
*
* **Description of statuses/states:**
*
* The statuses occur in this order:
*
* 1. The `boot` status occurs in the ProcessWire class constructor, after PW has initialized its
* class loader, loaded its config files, and initialized its hooks system. One use for this
* state is to add static hooks to methods that might be executed between boot and init, which
* would be missed by the time the init state is reached.
*
* 2. The `init` status occurs after ProcessWire has loaded all of its core API variables, except
* that the $page API variable has not yet been determined. At this point, all of the autoload
* modules have had their init() methods called as well.
*
* - If you want to target the state right before modules.init() methods are called, (rather
* than after), you can use `initBefore`.
*
* 3. The `ready` status is similar to the init status except that the current Page is now known,
* and the $page API variable is known and available. The ready file is included after autoload
* modules have had their ready() methods called.
*
* - If you want to limit your ready file to just be called for front-end (site) requests,
* you can use `readySite`.
*
* - If you want to limit your ready file to just be called for back-end (admin) requests with
* a logged-in user, you can use `readyAdmin`.
*
* - If you want to target the state right before modules.ready() methods are called, (rather
* than after), you can use `readyBefore`. This is the same as the init state, except that
* the current $page is available.
*
* 4. The `render` status occurs when a page is about to be rendered and the status is retained
* until the page has finished rendering. If using a status file for this, in addition to API
* variables, it will also receive a `$contentType` variable that contains the matched content-
* type header, or it may be blank for text/html content-type, or if not yet known. If externally
* bootstrapped it will contain the value “external”.
*
* 5. The `download` status occurs when a file is about to be sent as a download to the user.
* It occurs *instead* of a render status (rather than in addition to). If using an include file
* for this, in addition to API vars, it will also receive a `$downloadFile` variable containing
* the filename requested to be downloaded (string).
*
* 6. The `finished` status occurs after the request has been delivered and output sent. ProcessWire
* performs various maintenance tasks during this state.
*
* 7. The `failed` status occurs when the request has failed due an Exception being thrown.
* In addition to available API vars, it also receives these variables:
*
* - `$exception` (\Exception): The Exception that triggered the failed status, this is most
* commonly going to be a Wire404Exception, WirePermissionException or WireException.
* - `$reason` (string): Additional text info about error, beyond $exception->getMessage().
* - `$failPage` (Page|NullPage): Page where the error occurred
*
* **Defining status files:**
*
* You can define all of the status files at once using an array like the one this documentation
* is for, but chances are you want to set one or two rather than all of them. You can do so like
* this, after creating /site/boot.php and site/failed.php files (as examples):
* ~~~~~
* $config->statusFiles('boot', 'boot.php');
* $config->statusFiles('failed', 'failed.php');
* ~~~~~
*
* @since 3.0.142
* @var array
*
* #property string boot File to include for 'boot' state.
* #property string init File to include for 'init' state.
* #property string initBefore File to include right before 'init' state, before modules.init().
* #property string ready File to include for API 'ready' state.
* #property string readyBefore File to include right before 'ready'state, before modules.ready().
* #property string readySite File to include for 'ready' state on front-end/site only.
* #property string readyAdmin File to include for 'ready' state on back-end/admin only.
* #property string download File to include for API 'download' state (sending file to user).
* #property string render File to include for the 'render' state (always called before).
* #property string finished File to include for the 'finished' state.
* #property string failed File to include for the 'failed' state.
*
*/
$config->statusFiles = array(
'boot' => '',
'initBefore' => '',
'init' => 'init.php',
'readyBefore' => '',
'ready' => 'ready.php',
'readySite' => '',
'readyAdmin' => '',
'render' => '',
'download' => '',
'finished' => 'finished.php',
'failed' => '',
);
/**
* adminTemplates: Names of templates that ProcessWire should consider exclusive to the admin
*
* @since 3.0.142
* @var array
*
*/
$config->adminTemplates = array('admin');
/*** 10. RUNTIME ********************************************************************************
*
* The following are runtime-only settings and cannot be changed from /site/config.php
@@ -1097,12 +1564,32 @@ $config->https = null;
*/
$config->ajax = false;
/**
* modal: This is automatically set to TRUE when request is in a modal window.
*
*/
$config->modal = false;
/**
* external: This is automatically set to TRUE when PW is externally bootstrapped.
*
*/
$config->external = false;
/**
* status: Current runtime status (corresponding to ProcessWire::status* constants)
*
*/
$config->status = 0;
/**
* admin: TRUE when current request is for a logged-in user in the admin, FALSE when not, 0 when not yet known
*
* @since 3.0.142
*
*/
$config->admin = 0;
/**
* cli: This is automatically set to TRUE when PW is booted as a command line (non HTTP) script.
*
@@ -1115,7 +1602,6 @@ $config->cli = false;
*/
$config->version = '';
/**
* versionName: This is automatically populated with the current PW version name (i.e. 2.5.0 dev)
*
@@ -1128,9 +1614,17 @@ $config->versionName = '';
* Value is null, 0, or 1 or higher. This should be kept at null in this file.
*
*/
$config->inputfieldColumnWidthSpacing = null;
$config->inputfieldColumnWidthSpacing = null;
/**
* Populated to contain <link rel='next|prev'.../> tags for document head
*
* This is populated only after a MarkupPagerNav::render() has rendered pagination and is
* otherwise null.
*
* $config->pagerHeadTags = '';
*
*/
/*** 11. SYSTEM *********************************************************************************
*

View File

@@ -1,4 +1,4 @@
<?php
<?php
/**
* ProcessWire PhpStorm Meta
*
@@ -14,64 +14,66 @@ namespace PHPSTORM_META {
$STATIC_METHOD_TYPES = [
\wire('') => [
'' == '@',
'config' instanceof Config,
'wire' instanceof ProcessWire,
'log' instanceof WireLog,
'notices' instanceof Notices,
'sanitizer' instanceof Sanitizer,
'database' instanceof WireDatabasePDO,
'db' instanceof DatabaseMysqli,
'cache' instanceof MarkupCache,
'modules' instanceof Modules,
'procache' instanceof ProCache,
'fieldtypes' instanceof Fieldtypes,
'fields' instanceof Fields,
'fieldgroups' instanceof Fieldgroups,
'templates' instanceof Templates,
'pages' instanceof Pages,
'permissions' instanceof Permissions,
'roles' instanceof Roles,
'users' instanceof Users,
'user' instanceof User,
'session' instanceof Session,
'input' instanceof WireInput,
'languages' instanceof Languages,
'page' instanceof Page,
'hooks' instanceof WireHooks,
'files' instanceof WireFileTools,
'datetime' instanceof WireDateTime,
'mail' instanceof WireMailTools
'config' instanceof \ProcessWire\Config,
'cache' instanceof \ProcessWire\WireCache,
'wire' instanceof \ProcessWire\ProcessWire,
'log' instanceof \ProcessWire\WireLog,
'notices' instanceof \ProcessWire\Notices,
'sanitizer' instanceof \ProcessWire\Sanitizer,
'database' instanceof \ProcessWire\WireDatabasePDO,
'db' instanceof \ProcessWire\DatabaseMysqli,
'cache' instanceof \ProcessWire\MarkupCache,
'modules' instanceof \ProcessWire\Modules,
'procache' instanceof \ProCache,
'fieldtypes' instanceof \ProcessWire\Fieldtypes,
'fields' instanceof \ProcessWire\Fields,
'fieldgroups' instanceof \ProcessWire\Fieldgroups,
'templates' instanceof \ProcessWire\Templates,
'pages' instanceof \ProcessWire\Pages,
'permissions' instanceof \ProcessWire\Permissions,
'roles' instanceof \ProcessWire\Roles,
'users' instanceof \ProcessWire\Users,
'user' instanceof \ProcessWire\User,
'session' instanceof \ProcessWire\Session,
'input' instanceof \ProcessWire\WireInput,
'languages' instanceof \ProcessWire\Languages,
'page' instanceof \ProcessWire\Page,
'hooks' instanceof \ProcessWire\WireHooks,
'files' instanceof \ProcessWire\WireFileTools,
'datetime' instanceof \ProcessWire\WireDateTime,
'mail' instanceof \ProcessWire\WireMailTools
],
\Wire::wire('') => [
// this one does not appear to work, leaving in case someone knows how to make it work
'' == '@',
'config' instanceof Config,
'wire' instanceof ProcessWire,
'log' instanceof WireLog,
'notices' instanceof Notices,
'sanitizer' instanceof Sanitizer,
'database' instanceof WireDatabasePDO,
'db' instanceof DatabaseMysqli,
'cache' instanceof MarkupCache,
'modules' instanceof Modules,
'procache' instanceof ProCache,
'fieldtypes' instanceof Fieldtypes,
'fields' instanceof Fields,
'fieldgroups' instanceof Fieldgroups,
'templates' instanceof Templates,
'pages' instanceof Pages,
'permissions' instanceof Permissions,
'roles' instanceof Roles,
'users' instanceof Users,
'user' instanceof User,
'session' instanceof Session,
'input' instanceof WireInput,
'languages' instanceof Languages,
'page' instanceof Page,
'hooks' instanceof WireHooks,
'files' instanceof WireFileTools,
'datetime' instanceof WireDateTime,
'mail' instanceof WireMailTools
'config' instanceof \ProcessWire\Config,
'cache' instanceof \ProcessWire\WireCache,
'wire' instanceof \ProcessWire\ProcessWire,
'log' instanceof \ProcessWire\WireLog,
'notices' instanceof \ProcessWire\Notices,
'sanitizer' instanceof \ProcessWire\Sanitizer,
'database' instanceof \ProcessWire\WireDatabasePDO,
'db' instanceof \ProcessWire\DatabaseMysqli,
'cache' instanceof \ProcessWire\MarkupCache,
'modules' instanceof \ProcessWire\Modules,
'procache' instanceof \ProCache,
'fieldtypes' instanceof \ProcessWire\Fieldtypes,
'fields' instanceof \ProcessWire\Fields,
'fieldgroups' instanceof \ProcessWire\Fieldgroups,
'templates' instanceof \ProcessWire\Templates,
'pages' instanceof \ProcessWire\Pages,
'permissions' instanceof \ProcessWire\Permissions,
'roles' instanceof \ProcessWire\Roles,
'users' instanceof \ProcessWire\Users,
'user' instanceof \ProcessWire\User,
'session' instanceof \ProcessWire\Session,
'input' instanceof \ProcessWire\WireInput,
'languages' instanceof \ProcessWire\Languages,
'page' instanceof \ProcessWire\Page,
'hooks' instanceof \ProcessWire\WireHooks,
'files' instanceof \ProcessWire\WireFileTools,
'datetime' instanceof \ProcessWire\WireDateTime,
'mail' instanceof \ProcessWire\WireMailTools
]
];
}

View File

@@ -10,13 +10,14 @@
* This file is licensed under the MIT license.
* https://processwire.com/about/license/mit/
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2018 by Ryan Cramer
* https://processwire.com
*
* @property int|string $version Current admin theme version
*
* @method void install()
* @method void uninstall()
* @method array getExtraMarkup()
*
*/
@@ -61,6 +62,30 @@ abstract class AdminTheme extends WireData implements Module {
*/
protected $bodyClasses = array();
/**
* General purpose classes indexed by name
*
* @var array
*
*/
protected $classes = array();
/**
* Extra markup regions
*
* @var array
*
*/
protected $extraMarkup = array(
'head' => '',
'notices' => '',
'body' => '',
'masthead' => '',
'content' => '',
'footer' => '',
'sidebar' => '', // sidebar not used in all admin themes
);
/**
* URLs to place in link prerender tags
*
@@ -78,9 +103,11 @@ abstract class AdminTheme extends WireData implements Module {
}
/**
* Initialize the admin theme systme and determine which admin theme should be used
* Initialize the admin theme system and determine which admin theme should be used
*
* All admin themes must call this init() method to register themselves.
*
* Note: this should be called after API ready.
*
*/
public function init() {
@@ -99,40 +126,40 @@ abstract class AdminTheme extends WireData implements Module {
// if admin theme has already been set, then no need to continue
if($this->wire('adminTheme')) return;
$isCurrent = false;
$adminTheme = $this->wire('user')->admin_theme;
/** @var Config $config */
$config = $this->wire('config');
/** @var Session $session */
$session = $this->wire('session');
/** @var User $user */
$user = $this->wire('user');
/** @var string $adminTheme */
$adminTheme = $user->admin_theme;
if($adminTheme) {
// there is user specified admin theme
// check if this is the one that should be used
if($adminTheme == $this->className()) $isCurrent = true;
if($adminTheme == $this->className()) $this->setCurrent();
} else if($this->wire('config')->defaultAdminTheme == $this->className()) {
// there is no user specified admin theme, so use this one
$isCurrent = true;
}
// set as an API variable and populate configuration variables
if($isCurrent) {
$this->wire('adminTheme', $this);
$this->config->paths->set('adminTemplates', $this->config->paths->get($this->className()));
$this->config->urls->set('adminTemplates', $this->config->urls->get($this->className()));
$this->setCurrent();
}
// adjust $config adminThumbOptions[scale] for auto detect when requested
$o = $this->wire('config')->adminThumbOptions;
$o = $config->adminThumbOptions;
if($o && isset($o['scale']) && $o['scale'] === 1) {
$o['scale'] = $this->wire('session')->hidpi ? 0.5 : 1.0;
$this->wire('config')->adminThumbOptions = $o;
$o['scale'] = $session->get('hidpi') ? 0.5 : 1.0;
$config->adminThumbOptions = $o;
}
$this->config->js('modals', $this->config->modals);
$config->js('modals', $config->modals);
if($session->get('hidpi')) $this->addBodyClass('hidpi-device');
if($session->get('touch')) $this->addBodyClass('touch-device');
if($this->wire('session')->hidpi) $this->addBodyClass('hidpi-device');
if($this->wire('session')->touch) $this->addBodyClass('touch-device');
$this->addBodyClass($this->className());
}
public function get($key) {
if($key == 'version') return $this->version;
return parent::get($key);
@@ -146,6 +173,19 @@ abstract class AdminTheme extends WireData implements Module {
return $this->wire('adminTheme') === $this;
}
/**
* Set this admin theme as the current one
*
*/
protected function setCurrent() {
$config = $this->wire('config');
$name = $this->className();
$config->paths->set('adminTemplates', $config->paths->get($name));
$config->urls->set('adminTemplates', $config->urls->get($name));
$config->set('defaultAdminTheme', $name);
$this->wire('adminTheme', $this);
}
/**
* Enables hooks to append extra markup to various sections of the admin page
*
@@ -160,15 +200,7 @@ abstract class AdminTheme extends WireData implements Module {
*
*/
public function ___getExtraMarkup() {
$parts = array(
'head' => '',
'notices' => '',
'body' => '',
'masthead' => '',
'content' => '',
'footer' => '',
'sidebar' => '', // sidebar not used in all admin themes
);
$parts = $this->extraMarkup;
$isLoggedin = $this->wire('user')->isLoggedin();
if($isLoggedin && $this->wire('modules')->isInstalled('InputfieldCKEditor')
&& $this->wire('process') instanceof WirePageEditor) {
@@ -177,21 +209,118 @@ abstract class AdminTheme extends WireData implements Module {
"window.CKEDITOR_BASEPATH='" . $this->wire('config')->urls->InputfieldCKEditor .
'ckeditor-' . InputfieldCKEditor::CKEDITOR_VERSION . "/';</script>";
}
/*
if($isLoggedin && $this->wire('config')->advanced) {
$parts['footer'] = "<p class='AdvancedMode'><i class='fa fa-flask'></i> " . $this->_('Advanced Mode') . "</p>";
}
*/
foreach($this->preRenderURLs as $url) {
$parts['head'] .= "<link rel='prerender' href='$url'>";
}
return $parts;
}
public function addBodyClass($className) {
$this->bodyClasses[$className] = $className;
/**
* Add extra markup to a region in the admin theme
*
* @param string $name
* @param string $value
*
*/
public function addExtraMarkup($name, $value) {
if(!empty($this->extraMarkup[$name])) {
$this->extraMarkup[$name] .= "\n$value";
} else {
$this->extraMarkup[$name] = $value;
}
}
/**
* Add a <body> class to the admin theme
*
* @param string $className
*
*/
public function addBodyClass($className) {
$this->addClass('body', $className);
}
/**
* Get the body[class] attribute string
*
* @return string
*
*/
public function getBodyClass() {
return trim(implode(' ', $this->bodyClasses));
return $this->getClass('body');
}
/**
* Return class for a given named item or blank if none available
*
* Omit the first argument to return all classes in an array.
*
* @param string $name Tag or item name, i.e. “input”, or omit to return all defined [tags=classes]
* @param bool $getArray Specify true to return array of class name(s) rather than string (default=false). $tagName argument required.
* @return string|array Returns string or array of class names, or array of all [tags=classes] or $tagName argument is empty.
*
*/
public function getClass($name = '', $getArray = false) {
if(empty($name)) {
return $this->classes;
} else if(isset($this->classes[$name])) {
return $getArray ? explode(' ', $this->classes[$name]) : $this->classes[$name];
} else {
return $getArray ? array() : '';
}
}
/**
* Add class for given named item
*
* Default behavior is to merge classes if existing classes are already present for given item $name.
*
* #pw-internal
*
* @param string $name
* @param string|array $class
* @param bool $replace Specify true to replace any existing classes rather than merging them
*
*/
public function addClass($name, $class, $replace = false) {
if(is_array($class)) {
foreach($class as $c) {
$this->addClass($name, $c);
}
} else if(!$replace && isset($this->classes[$name])) {
$classes = $this->classes[$name];
if(strpos($classes, $class) !== false) {
// avoid re-adding class if it is already present
if(array_search($class, explode(' ', $classes)) !== false) return;
}
$this->classes[$name] = trim($classes . ' ' . ltrim($class));
} else {
$this->classes[$name] = trim($class);
}
}
/**
* Set classes for multiple tags
*
* #pw-internal
*
* @param array $classes Array of strings (class names) where keys are tag names
* @param bool $replace Specify true to replace any existing classes rather than merge them (default=false)
*
*/
public function setClasses(array $classes, $replace = false) {
if($replace || empty($this->classes)) {
$this->classes = $classes;
} else {
foreach($classes as $name => $class) {
$this->addClass($name, $class);
}
}
}
/**
@@ -213,36 +342,44 @@ abstract class AdminTheme extends WireData implements Module {
// we already have this field installed, no need to continue
if($field) {
$this->message($toUseNote);
return;
} else {
// this will be the 2nd admin theme installed, so add a field that lets them select admin theme
$field = $this->wire(new Field());
$field->name = 'admin_theme';
$field->type = $this->wire('modules')->get('FieldtypeModule');
$field->set('moduleTypes', array('AdminTheme'));
$field->set('labelField', 'title');
$field->set('inputfieldClass', 'InputfieldRadios');
$field->label = 'Admin Theme';
$field->flags = Field::flagSystem;
try {
$field->save();
} catch(\Exception $e) {
// $this->error("Error creating 'admin_theme' field: " . $e->getMessage());
}
}
// this will be the 2nd admin theme installed, so add a field that lets them select admin theme
$field = $this->wire(new Field());
$field->name = 'admin_theme';
$field->type = $this->wire('modules')->get('FieldtypeModule');
$field->set('moduleTypes', array('AdminTheme'));
$field->set('labelField', 'title');
$field->set('inputfieldClass', 'InputfieldRadios');
$field->label = 'Admin Theme';
$field->flags = Field::flagSystem;
$field->save();
$fieldgroup = $this->wire('fieldgroups')->get('user');
$fieldgroup->add($field);
$fieldgroup->save();
// make this field one that the user is allowed to configure in their profile
$data = $this->wire('modules')->getModuleConfigData('ProcessProfile');
$data['profileFields'][] = 'admin_theme';
$this->wire('modules')->saveModuleConfigData('ProcessProfile', $data);
$this->message($this->_('Installed field "admin_theme" and added to user profile settings.'));
$this->message($toUseNote);
if($field && $field->id) {
/** @var Fieldgroup $fieldgroup */
$fieldgroup = $this->wire('fieldgroups')->get('user');
if(!$fieldgroup->hasField($field)) {
$fieldgroup->add($field);
$fieldgroup->save();
$this->message($this->_('Installed field "admin_theme" and added to user profile settings.'));
$this->message($toUseNote);
}
// make this field one that the user is allowed to configure in their profile
$data = $this->wire('modules')->getModuleConfigData('ProcessProfile');
$data['profileFields'][] = 'admin_theme';
$this->wire('modules')->saveModuleConfigData('ProcessProfile', $data);
}
}
/**
* Set a pre-render URL or get currently pre-render URL(s)
*
* #pw-internal
*
* @param string $url
* @return array
*
@@ -253,6 +390,14 @@ abstract class AdminTheme extends WireData implements Module {
}
public function ___uninstall() {
$defaultAdminTheme = $this->wire('config')->defaultAdminTheme;
if($defaultAdminTheme == $this->className()) {
throw new WireException(
"Cannot uninstall this admin theme because \$config->defaultAdminTheme = '$defaultAdminTheme'; " .
"Please add this setting with a different value in /site/config.php"
);
}
/*
if(self::$numAdminThemes > 1) return;

View File

@@ -0,0 +1,812 @@
<?php namespace ProcessWire;
/**
* AdminTheme Framework
*
* The methods in this class may eventually be merged to AdminTheme.php,
* but are isolated to this class during development.
*
* @property bool $isSuperuser
* @property bool $isEditor
* @property bool $isLoggedIn
* @property bool|string $isModal
* @property bool|int $useAsLogin
* @method array getUserNavArray()
*
*/
abstract class AdminThemeFramework extends AdminTheme {
/**
* Is there currently a logged in user?
*
* @var bool
*
*/
protected $isLoggedIn = false;
/**
* Is user logged in with page-edit permission?
*
* @var bool
*
*/
protected $isEditor = false;
/**
* Is current user a superuser?
*
* @var bool
*
*/
protected $isSuperuser = false;
/**
* Is the current request a modal request?
*
* @var bool|string Either false, true, or "inline"
*
*/
protected $isModal = false;
/**
* @var Sanitizer
*
*/
protected $sanitizer;
/**
* Construct
*
*/
public function __construct() {
parent::__construct();
$this->set('useAsLogin', false);
$this->sanitizer = $this->wire('sanitizer');
}
/**
* Override get() method from WireData to support additional properties
*
* @param string $key
* @return bool|int|mixed|null|string
*
*/
public function get($key) {
switch($key) {
case 'isSuperuser': $value = $this->isSuperuser; break;
case 'isEditor': $value = $this->isEditor; break;
case 'isLoggedIn': $value = $this->isLoggedIn; break;
case 'isModal': $value = $this->isModal; break;
default: $value = parent::get($key);
}
return $value;
}
/**
* Initialize and attach hooks
*
* Note: descending classes should call this after API ready
*
*/
public function init() {
$user = $this->wire('user');
if(!$user->isLoggedin() && $this->useAsLogin) $this->setCurrent();
parent::init();
// if this is not the current admin theme, exit now so no hooks are attached
if(!$this->isCurrent()) return;
$this->isLoggedIn = $user->isLoggedin();
$this->isSuperuser = $this->isLoggedIn && $user->isSuperuser();
$this->isEditor = $this->isLoggedIn && ($this->isSuperuser || $user->hasPermission('page-edit'));
$this->includeInitFile();
$modal = $this->wire('input')->get('modal');
if($modal) $this->isModal = $modal == 'inline' ? 'inline' : true;
// test notices when requested
if($this->wire('input')->get('test_notices') && $this->isLoggedIn) $this->testNotices();
}
/**
* Include the admin theme init file
*
*/
public function includeInitFile() {
$config = $this->wire('config');
$initFile = $config->paths->adminTemplates . 'init.php';
if(file_exists($initFile)) {
if(strpos($initFile, $config->paths->site) === 0) {
// admin themes in /site/modules/ may be compiled
$initFile = $this->wire('files')->compile($initFile);
}
/** @noinspection PhpIncludeInspection */
include_once($initFile);
}
}
/**
* Perform a translation, based on text from shared admin file: /wire/templates-admin/default.php
*
* @param string $text
* @return string
*
*/
public function _($text) {
static $translate = null;
if(is_null($translate)) $translate = $this->wire('languages') !== null;
if($translate === false) return $text;
$value = __($text, $this->wire('config')->paths->root . 'wire/templates-admin/default.php');
if($value === $text) $value = parent::_($text);
return $value;
}
/**
* Get the current page headline
*
* @return string
*
*/
public function getHeadline() {
$headline = $this->wire('processHeadline');
if(!$headline) $headline = $this->wire('page')->get('title|name');
if($this->wire('languages')) $headline = $this->_($headline);
return $this->sanitizer->entities1($headline);
}
/**
* Get navigation title for the given page, return blank if page should not be shown
*
* @param Page $p
* @return string
*
*/
public function getPageTitle(Page $p) {
if($p->name == 'add' && $p->parent->name == 'page') {
$title = $this->getAddNewLabel();
} else {
$title = $this->_($p->title);
}
$title = $this->sanitizer->entities1($title);
return $title;
}
/**
* Get icon used by the given page
*
* @param Page $p
* @return mixed|null|string
*
*/
public function getPageIcon(Page $p) {
$icon = '';
if($p->template == 'admin') {
$info = $this->wire('modules')->getModuleInfo($p->process);
if(!empty($info['icon'])) $icon = $info['icon'];
}
// allow for option of an admin field overriding the module icon
$pageIcon = $p->get('page_icon');
if($pageIcon) $icon = $pageIcon;
if(!$icon) switch($p->id) {
case 22: $icon = 'gears'; break; // Setup
case 21: $icon = 'plug'; break; // Modules
case 28: $icon = 'key'; break; // Access
}
if(!$icon && $p->parent->id != $this->wire('config')->adminRootPageID) {
$icon = 'file-o ui-priority-secondary';
}
return $icon;
}
/**
* Get “Add New” button actions
*
* - Returns array of arrays, each with 'url', 'label' and 'icon' properties.
* - Returns empty array if Add New button should not be displayed.
*
* @return array
*
*/
public function getAddNewActions() {
$page = $this->wire('page');
$process = $this->wire('process');
$input = $this->wire('input');
if(!$this->isEditor) return array();
if($page->name != 'page' || $this->wire('input')->urlSegment1) return array();
if($input->urlSegment1 || $input->get('modal')) return array();
if(strpos($process, 'ProcessPageList') !== 0) return array();
/** @var ProcessPageAdd $module */
$module = $this->wire('modules')->getModule('ProcessPageAdd', array('noInit' => true));
$data = $module->executeNavJSON(array('getArray' => true));
$actions = array();
foreach($data['list'] as $item) {
$item['url'] = $data['url'] . $item['url'];
$actions[] = $item;
}
return $actions;
}
/**
* Get the translated “Add New” label thats used in a couple spots
*
* @return string
*
*/
public function getAddNewLabel() {
return $this->_('Add New');
}
/**
* Get the classes that will be used in the <body class=''> tag
*
* @return string
*
*/
public function getBodyClass() {
$page = $this->wire('page');
$process = $this->wire('process');
$classes = array(
"id-{$page->id}",
"template-{$page->template->name}",
"pw-init",
parent::getBodyClass(),
);
if($this->isModal) $classes[] = 'modal';
if($this->isModal === 'inline') $classes[] = 'modal-inline';
if($this->wire('input')->urlSegment1) $classes[] = 'hasUrlSegments';
if($process) $classes[] = $process->className();
if(!$this->isLoggedIn) $classes[] = 'pw-guest';
return implode(' ', $classes);
}
/**
* Get Javascript that must be present in the document <head>
*
* @return string
*
*/
public function getHeadJS() {
/** @var Config $config */
$config = $this->wire('config');
/** @var Paths $urls */
$urls = $config->urls;
/** @var array $jsConfig */
$jsConfig = $config->js();
$jsConfig['debug'] = $config->debug;
$jsConfig['urls'] = array(
'root' => $urls->root,
'admin' => $urls->admin,
'modules' => $urls->modules,
'core' => $urls->core,
'files' => $urls->files,
'templates' => $urls->templates,
'adminTemplates' => $urls->adminTemplates,
);
$out =
"var ProcessWire = { config: " . wireEncodeJSON($jsConfig, true, $config->debug) . " }; " .
"var config = ProcessWire.config;\n"; // legacy support
return $out;
}
/**
* Allow the given Page to appear in admin theme navigation?
*
* @param Page $p Page to test
* @param PageArray|array $children Children of page, if applicable (optional)
* @param string|null $permission Specify required permission (optional)
* @return bool
*
*/
public function allowPageInNav(Page $p, $children = array(), $permission = null) {
if($this->isSuperuser) return true;
$pageViewable = $p->viewable();
if(!$pageViewable) return false;
$allow = false;
$numChildren = count($children);
if($p->process == 'ProcessPageAdd') {
// ProcessPageAdd: avoid showing this menu item if there are no predefined family settings to use
$numAddable = $this->wire('session')->getFor('ProcessPageAdd', 'numAddable');
if($numAddable === null) {
/** @var ProcessPageAdd $processPageAdd */
$processPageAdd = $this->wire('modules')->getModule('ProcessPageAdd', array('noInit' => true));
if($processPageAdd) {
$addData = $processPageAdd->executeNavJSON(array('getArray' => true));
$numAddable = $addData['list'];
$this->wire('session')->setFor('ProcessPageAdd', 'numAddable', $numAddable);
}
}
// no addable options, so do not show the "Add New" item
if(!$numAddable) return false;
} else if(empty($permission)) {
// no permission specified
if(!$p->process) {
// no process module present, so we delegate to just the page viewable state if no children to check
if($pageViewable && !$numChildren) return true;
} else if($p->process == 'ProcessList') {
// page just serves as a list for children
} else {
// determine permission from Process module, if present
$moduleInfo = $this->wire('modules')->getModuleInfo($p->process);
if(!empty($moduleInfo['permission'])) $permission = $moduleInfo['permission'];
}
}
if($permission) {
// specific permission required to determine view access
$allow = $this->wire('user')->hasPermission($permission);
} else if($pageViewable && $p->parent_id == $this->wire('config')->adminRootPageID) {
// primary nav page requires that at least one child is viewable
foreach($children as $child) {
if($this->allowPageInNav($child)) {
$allow = true;
break;
}
}
}
return $allow;
}
/**
* Return nav array of primary navigation
*
* @return array
*
*/
public function getPrimaryNavArray() {
$items = array();
$config = $this->wire('config');
$admin = $this->wire('pages')->get($config->adminRootPageID);
foreach($admin->children("check_access=0") as $p) {
$item = $this->pageToNavArray($p);
if($item) $items[] = $item;
}
return $items;
}
/**
* Get navigation array from a Process module
*
* @param array|Module|string $module Module info array or Module object or string
* @param Page $p Page upon which the Process module is contained
* @return array
*
*/
public function moduleToNavArray($module, Page $p) {
$config = $this->wire('config');
$modules = $this->wire('modules');
$textdomain = str_replace($config->paths->root, '/', $modules->getModuleFile($p->process));
$user = $this->wire('user');
$navArray = array();
if(is_array($module)) {
$moduleInfo = $module;
} else {
$moduleInfo = $modules->getModuleInfo($module);
}
foreach($moduleInfo['nav'] as $navItem) {
$permission = empty($navItem['permission']) ? '' : $navItem['permission'];
if($permission && !$user->hasPermission($permission)) continue;
$navArray[] = array(
'id' => 0,
'parent_id' => $p->id,
'title' => $this->sanitizer->entities1(__($navItem['label'], $textdomain)), // translate from context of Process module
'name' => '',
'url' => $p->url . $navItem['url'],
'icon' => empty($navItem['icon']) ? '' : $navItem['icon'],
'children' => array(),
'navJSON' => empty($navItem['navJSON']) ? '' : $p->url . $navItem['navJSON'],
);
}
return $navArray;
}
/**
* Get a navigation array the given Page, or null if page not allowed in nav
*
* @param Page $p
* @return array|null
*
*/
public function pageToNavArray(Page $p) {
$children = $p->numChildren ? $p->children("check_access=0") : array();
if(!$this->allowPageInNav($p, $children)) return null;
$navArray = array(
'id' => $p->id,
'parent_id' => $p->parent_id,
'url' => $p->url,
'name' => $p->name,
'title' => $this->getPageTitle($p),
'icon' => $this->getPageIcon($p),
'children' => array(),
'navJSON' => '',
);
if(!count($children)) {
// no children available
if($p->template == 'admin' && $p->process) {
// see if process module defines its own navigation
$moduleInfo = $this->wire('modules')->getModuleInfo($p->process);
if(!empty($moduleInfo['nav'])) {
$navArray['children'] = $this->moduleToNavArray($moduleInfo, $p);
}
} else {
// The /page/ and /page/list/ are the same process, so just keep them on /page/ instead.
if(strpos($navArray['url'], '/page/list/') !== false) {
$navArray['url'] = str_replace('/page/list/', '/page/', $navArray['url']);
}
}
return $navArray;
}
// if we reach this point, then we have a PageArray of children
$modules = $this->wire('modules');
foreach($children as $c) {
if(!$c->process) continue;
$moduleInfo = $modules->getModuleInfo($c->process);
$permission = empty($moduleInfo['permission']) ? '' : $moduleInfo['permission'];
if(!$this->allowPageInNav($c, array(), $permission)) continue;
$childItem = array(
'id' => $c->id,
'parent_id' => $c->parent_id,
'title' => $this->getPageTitle($c),
'name' => $c->name,
'url' => $c->url,
'icon' => $this->getPageIcon($c),
'children' => array(),
'navJSON' => empty($moduleInfo['useNavJSON']) ? '' : $c->url . 'navJSON/',
);
if(!empty($moduleInfo['nav'])) {
$childItem['children'] = $this->moduleToNavArray($moduleInfo, $c);
}
$navArray['children'][] = $childItem;
} // foreach
return $navArray;
}
/**
* Get navigation items for the “user” menu
*
* This is hookable so that something else could add stuff to it.
* See the method body for details on format used.
*
* @return array
*
*/
public function ___getUserNavArray() {
$urls = $this->wire('urls');
$navArray = array();
$navArray[] = array(
'url' => $urls->root,
'title' => $this->_('View site'),
'target' => '_top',
'icon' => 'eye',
);
if($this->wire('user')->hasPermission('profile-edit')) $navArray[] = array(
'url' => $urls->admin . 'profile/',
'title' => $this->_('Profile'),
'icon' => 'user',
'permission' => 'profile-edit',
);
$navArray[] = array(
'url' => $urls->admin . 'login/logout/',
'title' => $this->_('Logout'),
'target' => '_top',
'icon' => 'power-off',
);
return $navArray;
}
/**
* Get the browser <title>
*
* @return string
*
*/
public function getBrowserTitle() {
$browserTitle = $this->wire('processBrowserTitle');
$modal = $this->wire('input')->get('modal');
if(!$browserTitle) {
if($modal) return $this->wire('processHeadline');
$browserTitle = $this->_(strip_tags($this->wire('page')->get('title|name'))) . ' • ProcessWire';
}
if(!$modal) {
$httpHost = $this->wire('config')->httpHost;
if(strpos($httpHost, 'www.') === 0) $httpHost = substr($httpHost, 4); // remove www
if(strpos($httpHost, ':')) $httpHost = preg_replace('/:\d+/', '', $httpHost); // remove port
$browserTitle .= "$httpHost";
}
return $this->sanitizer->entities1($browserTitle);
}
/**
* Test all notice types
*
* @return bool
*
*/
public function testNotices() {
if(!$this->wire('user')->isLoggedin()) return false;
$this->message('Message test');
$this->message('Message test debug', Notice::debug);
$this->message('Message test markup <a href="#">example</a>', Notice::allowMarkup);
$this->warning('Warning test');
$this->warning('Warning test debug', Notice::debug);
$this->warning('Warning test markup <a href="#">example</a>', Notice::allowMarkup);
$this->error('Error test');
$this->error('Error test debug', Notice::debug);
$this->error('Error test markup <a href="#">example</a>', Notice::allowMarkup);
return true;
}
/**
* Render runtime notices div#notices
*
* @param Notices|bool $notices Notices object or specify boolean true to return array of all available $options
* @param array $options See defaults in method
* @return string|array Returns string unless you specify true for $notices argument, then it returns an array.
*
*/
public function renderNotices($notices, array $options = array()) {
$defaults = array(
'messageClass' => 'NoticeMessage', // class for messages
'messageIcon' => 'check-square', // default icon to show with notices
'warningClass' => 'NoticeWarning', // class for warnings
'warningIcon' => 'exclamation-circle', // icon for warnings
'errorClass' => 'NoticeError', // class for errors
'errorIcon' => 'exclamation-triangle', // icon for errors
'debugClass' => 'NoticeDebug', // class for debug items (appended)
'debugIcon' => 'bug', // icon for debug notices
'closeClass' => 'pw-notice-remove notice-remove', // class for close notices link <a>
'closeIcon' => 'times', // icon for close notices link
'listMarkup' => "<ul class='pw-notices' id='notices'>{out}</ul><!--/notices-->",
'itemMarkup' => "<li class='{class}'>{remove}{icon}{text}</li>",
// the following apply only when groupByType==true
'groupByType' => true, // Group notices by type
'groupParentClass' => 'pw-notice-group-parent', // class for parent notices
'groupChildClass' => 'pw-notice-group-child', // class for children (of parent notices)
'groupToggleMarkup' => "<a class='pw-notice-group-toggle' href='#'>{label}" .
"<i class='fa fa-fw fa-bell-o' data-toggle='fa-bell-o fa-bell'></i>" .
"<i class='fa fa-fw fa-angle-right' data-toggle='fa-angle-right fa-angle-down'></i></a>",
'groupToggleLabel' => $this->_("+{n-1}"),
);
$options = array_merge($defaults, $options);
if($notices === true) return $options;
$config = $this->wire('config');
$noticesArray = array();
$out = '';
$removeIcon = $this->renderIcon($options['closeIcon']);
$removeLabel = $this->_('Close all');
$removeLink = "<a class='$options[closeClass]' href='#' title='$removeLabel'>$removeIcon</a>";
if($this->isLoggedIn && $this->wire('modules')->isInstalled('SystemNotifications')) {
$defaults['groupByType'] = false;
//$systemNotifications = $this->wire('modules')->get('SystemNotifications');
//if(!$systemNotifications->placement) return '';
}
foreach($notices as $n => $notice) {
$text = $notice->text;
$allowMarkup = $notice->flags & Notice::allowMarkup;
$groupByType = $options['groupByType'] && !($notice->flags & Notice::noGroup) && !($notice instanceof NoticeError);
if($allowMarkup) {
// leave $text alone
} else {
// unencode + re-encode entities, just in case module already entity some or all of output
if(strpos($text, '&') !== false) $text = $this->sanitizer->unentities($text);
$text = $this->sanitizer->entities($text);
$text = nl2br($text);
}
if($notice instanceof NoticeError) {
$class = $options['errorClass'];
$icon = $options['errorIcon'];
$noticeType = 'errors';
} else if($notice instanceof NoticeWarning) {
$class = $options['warningClass'];
$icon = $options['warningIcon'];
$noticeType = 'warnings';
} else {
$class = $options['messageClass'];
$icon = $options['messageIcon'];
$noticeType = 'messages';
}
if($notice->flags & Notice::debug) {
$class .= " " . $options['debugClass'];
$icon = $options['debugIcon'];
// ensure non-debug version is set as well
if(!isset($noticesArray[$noticeType])) $noticesArray[$noticeType] = array();
$noticeType .= "-debug";
}
// indicate which class the notice originated from in debug mode
if($notice->class && $config->debug) $text = "{$notice->class}: $text";
$replacements = array(
'{class}' => $class,
'{remove}' => '',
'{icon}' => $this->renderNavIcon($notice->icon ? $notice->icon : $icon),
'{text}' => $text,
);
if($groupByType) {
if(!isset($noticesArray[$noticeType])) $noticesArray[$noticeType] = array();
$noticesArray[$noticeType][] = $replacements;
} else {
if($n === 0) $replacements['{remove}'] = $removeLink;
$out .= str_replace(array_keys($replacements), array_values($replacements), $options['itemMarkup']);
}
}
if($options['groupByType']) {
$cnt = 0;
foreach($noticesArray as $noticeType => $noticeReplacements) {
if(strpos($noticeType, '-debug')) continue;
if(isset($noticesArray["$noticeType-debug"])) {
$noticeReplacements = array_merge($noticeReplacements, $noticesArray["$noticeType-debug"]);
}
$n = count($noticeReplacements);
if($n > 1) {
$notice =& $noticeReplacements[0];
$label = str_replace(array('{n}', '{n-1}'), array($n, $n-1), $options['groupToggleLabel']);
$notice['{text}'] .= ' ' . str_replace(array('{label}'), array($label), $options['groupToggleMarkup']);
$notice['{class}'] .= ' ' . $options['groupParentClass'];
$childClass = $options['groupChildClass'];
} else {
$childClass = '';
}
foreach($noticeReplacements as $i => $replacements) {
if(!$cnt) $replacements['{remove}'] = $removeLink;
if($childClass && $i > 0) $replacements['{class}'] .= ' ' . $childClass;
$out .= str_replace(array_keys($replacements), array_values($replacements), $options['itemMarkup']);
$cnt++;
}
}
}
$out = str_replace('{out}', $out, $options['listMarkup']);
$out .= $this->renderExtraMarkup('notices');
return $out;
}
/**
* Render markup for a font-awesome icon
*
* @param string $icon Name of icon to render, excluding the “fa-” prefix
* @param bool $fw Specify true to make fixed width (default=false).
* @return string
*
*/
public function renderIcon($icon, $fw = false) {
if($fw) $icon .= ' fa-fw';
return "<i class='fa fa-$icon'></i>";
}
/**
* Render markup for a font-awesome icon that precedes a navigation label
*
* This is the same as renderIcon() except that fixed-width is assumed and a "nav-nav-icon"
* class is added to it.
*
* @param string $icon Name of icon to render, excluding the “fa-” prefix
* @return string
*
*/
public function renderNavIcon($icon) {
return $this->renderIcon("$icon pw-nav-icon", true);
}
/**
* Render an extra markup region
*
* @param string $for
* @return mixed|string
*
*/
public function renderExtraMarkup($for) {
static $extras = array();
if(empty($extras)) $extras = $this->getExtraMarkup();
return isset($extras[$for]) ? $extras[$for] : '';
}
/**
* Module Configuration
*
* @param InputfieldWrapper $inputfields
*
*/
public function getModuleConfigInputfields(InputfieldWrapper $inputfields) {
/** @var InputfieldCheckbox $f */
$f = $this->modules->get('InputfieldCheckbox');
$f->name = 'useAsLogin';
$f->label = $this->_('Use this admin theme for login screen?');
$f->description = $this->_('When checked, this admin theme will be used on the user login screen.');
$f->icon = 'sign-in';
$f->collapsed = Inputfield::collapsedBlank;
if($this->get('useAsLogin')) $f->attr('checked', 'checked');
$inputfields->add($f);
if($f->attr('checked') && $this->input->requestMethod('GET')) {
$class = $this->className();
foreach($this->modules->findByPrefix('AdminTheme') as $name) {
if($name == $class) continue;
$cfg = $this->modules->getConfig($name);
if(!empty($cfg['useAsLogin'])) {
unset($cfg['useAsLogin']);
$this->modules->saveConfig($name, $cfg);
$this->message("Removed 'useAsLogin' setting from $name", Notice::debug);
}
}
}
}
}

View File

@@ -210,7 +210,7 @@ class CacheFile extends Wire {
foreach($dir as $file) {
if($file->isDir() || $file->isDot()) continue;
//if(strpos($file->getFilename(), self::cacheFileExtension)) @unlink($file->getPathname());
if(self::isCacheFile($file->getPathname())) @unlink($file->getPathname());
if(self::isCacheFile($file->getPathname())) $this->wire('files')->unlink($file->getPathname());
}
return @rmdir($this->path);
@@ -223,7 +223,7 @@ class CacheFile extends Wire {
*
*/
protected function removeFilename($filename) {
@unlink($filename);
$this->wire('files')->unlink($filename);
}
@@ -250,7 +250,7 @@ class CacheFile extends Wire {
$numRemoved += self::removeAll($pathname, true);
} else if($file->isFile() && (self::isCacheFile($pathname) || ($file->getFilename() == self::globalExpireFilename))) {
if(unlink($pathname)) $numRemoved++;
if(wire('files')->unlink($pathname)) $numRemoved++;
}
}

View File

@@ -8,23 +8,30 @@
* This file is licensed under the MIT license
* https://processwire.com/about/license/mit/
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2019 by Ryan Cramer
* https://processwire.com
*
* #pw-summary Holds ProcessWire configuration settings as defined in /wire/config.php and /site/config.php.
* #pw-body =
* For more detailed descriptions of these $config properties, including default values, see the
* [/wire/config.php](https://github.com/processwire/processwire/blob/master/wire/config.php) file.
* #pw-body
*
*
* @see /wire/config.php for more detailed descriptions of all config properties.
*
* @property bool $ajax If the current request is an ajax (asynchronous javascript) request, this is set to true. #pw-group-runtime
* @property bool|int $modal If the current request is in a modal window, this is set to a positive number. False if not. #pw-group-runtime
* @property bool|int $admin Is current request for logged-in user in admin? True, false, or 0 if not yet known. @since 3.0.142 #pw-group-runtime
* @property string $httpHost Current HTTP host name. #pw-group-HTTP-and-input
* @property bool $https If the current request is an HTTPS request, this is set to true. #pw-group-runtime
* @property string $version Current ProcessWire version string (i.e. "2.2.3") #pw-group-system #pw-group-runtime
* @property int $systemVersion System version, used by SystemUpdater to determine when updates must be applied. #pw-group-system #pw-group-runtime
*
* @property FilenameArray $styles Array used by ProcessWire admin to keep track of what stylesheet files its template should load. It will be blank otherwise. Feel free to use it for the same purpose in your own sites. #pw-group-runtime
* @property FilenameArray $scripts Array used by ProcessWire admin to keep track of what javascript files its template should load. It will be blank otherwise. Feel free to use it for the same purpose in your own sites. #pw-group-runtime
*
* @property Paths $urls Items from $config->urls reflect the http path one would use to load a given location in the web browser. URLs retrieved from $config->urls always end with a trailing slash. #pw-group-runtime
* @property Paths $urls Items from $config->urls reflect the http path one would use to load a given location in the web browser. URLs retrieved from $config->urls always end with a trailing slash. This is the same as the $urls API variable. #pw-group-runtime #pw-group-URLs
* @property Paths $paths All of what can be accessed from $config->urls can also be accessed from $config->paths, with one important difference: the returned value is the full disk path on the server. There are also a few items in $config->paths that aren't in $config->urls. All entries in $config->paths always end with a trailing slash. #pw-group-runtime
*
* @property string $templateExtension Default is 'php' #pw-group-template-files
@@ -33,10 +40,12 @@
*
* @property bool $protectCSRF Enables CSRF (cross site request forgery) protection on all PW forms, recommended for security. #pw-group-HTTP-and-input
*
* @property array $imageSizerOptions Default value is array('upscaling' => true, 'cropping' => true, 'quality' => 90) #pw-group-images
* @property array $imageSizerOptions Options to set image sizing defaults. Please see the /wire/config.php file for all options and defaults. #pw-group-images
* @property array $webpOptions Options for webp images. Please see /wire/config.php for all options. #pw-group-images
*
* @property bool $pagefileSecure When used, files in /site/assets/files/ will be protected with the same access as the page. Routines files through a passthrough script. #pw-group-files
* @property string $pagefileSecurePathPrefix One or more characters prefixed to the pathname of protected file dirs. This should be some prefix that the .htaccess file knows to block requests for. #pw-group-files
* @property string $pagefileUrlPrefix Deprecated property that was a string that prefixes filenames in PW URLs, becoming a shortcut to a pages files URL (do not use, here for backwards compatibility only). #pw-internal
*
* @property array $contentTypes Array of extensions and the associated MIME type for each (for template file output). #pw-group-template-files
* @property array $fileContentTypes Array of extensions and the associated MIME type for each (for file output). See /wire/config.php for details and defaults. #pw-group-files
@@ -51,33 +60,41 @@
* @property string $sessionName Default session name to use (default='wire') #pw-group-session
* @property string $sessionNameSecure Session name when on HTTPS. Used when the sessionCookieSecure option is enabled (default). When blank (default), it will assume sessionName + 's'. #pw-group-session
* @property bool|int $sessionCookieSecure Use secure cookies when on HTTPS? When enabled, separate sessions will be maintained for HTTP vs. HTTPS. Good for security but tradeoff is login session may be lost when switching (default=1 or true). #pw-group-session
* @property null|string $sessionCookieDomain Domain to use for sessions, which enables a session to work across subdomains, or NULL to disable (default/recommended). #pw-group-session
* @property bool|callable $sessionAllow Are sessions allowed? Typically boolean true, unless provided a callable function that returns boolean. See /wire/config.php for an example. #pw-group-session
* @property int $sessionExpireSeconds How many seconds of inactivity before session expires? #pw-group-session
* @property bool $sessionChallenge Should login sessions have a challenge key? (for extra security, recommended) #pw-group-session
* @property bool $sessionFingerprint Should login sessions be tied to IP and user agent? May conflict with dynamic IPs. #pw-group-session
* @property int|bool $sessionFingerprint Should login sessions be tied to IP and user agent? 0 or false: Fingerprint off. 1 or true: Fingerprint on with default/recommended setting (currently 10). 2: Fingerprint only the remote IP. 4: Fingerprint only the forwarded/client IP (can be spoofed). 8: Fingerprint only the useragent. 10: Fingerprint the remote IP and useragent (default). 12: Fingerprint the forwarded/client IP and useragent. 14: Fingerprint the remote IP, forwarded/client IP and useragent (all). #pw-group-session
* @property int $sessionHistory Number of session entries to keep (default=0, which means off). #pw-group-session
* @property string $sessionForceIP Force the client IP address returned by $session->getIP() to be this rather than auto-detect (useful with load balancer). Use for setting value only. #pw-group-session
* @property array $loginDisabledRoles Array of role name(s) or ID(s) of roles where login is disallowed. #pw-group-session
*
* @property string $prependTemplateFile PHP file in /site/templates/ that will be loaded before each page's template file (default=none) #pw-group-template-files
* @property string $appendTemplateFile PHP file in /site/templates/ that will be loaded after each page's template file (default=none) #pw-group-template-files
* @property bool $templateCompile Allow use of compiled templates? #pw-group-template-files
*
* @property string $uploadUnzipCommand Shell command to unzip archives, used by WireUpload class. @deprecated #pw-group-deprecated
* @property string $uploadUnzipCommand Shell command to unzip archives, used by WireUpload class (deprecated, no longer in use). #pw-internal
* @property string $uploadTmpDir Optionally override PHP's upload_tmp_dir with your own. Should include a trailing slash. #pw-group-files
* @property string $uploadBadExtensions Space separated list of file extensions that are always disallowed from uploads. #pw-group-files
*
* @property string $adminEmail Email address to send fatal error notifications to. #pw-group-system
* @property array $adminTemplates Names of templates that ProcessWire should consider exclusive to the admin. #pw-group-system @since 3.0.142
*
* @property string $pageNameCharset Character set for page names, must be 'ascii' (default, lowercase) or 'UTF8' (uppercase). #pw-group-URLs
* @property string $pageNameWhitelist Whitelist of characters allowed in UTF8 page names. #pw-group-URLs
* @property string $pageNameUntitled Name to use for untitled pages (default="untitled"). #pw-group-URLs
* @property string $pageNumUrlPrefix Prefix used for pagination URLs. Default is "page", resulting in "/page1", "/page2", etc. #pw-group-URLs
* @property array $pageNumUrlPrefixes Multiple prefixes that may be used for detecting pagination (internal use, for multi-language) #pw-group-URLs
* @property int $maxUrlSegments Maximum number of extra stacked URL segments allowed in a page's URL (including page numbers) #pw-group-URLs
* @property int $maxUrlSegmentLength Maximum length of any individual URL segment (default=128). #pw-group-URLs
* @property int $maxUrlDepth Maximum URL/path slashes (depth) for request URLs. (Min=10, Max=60) #pw-group-URLs
* @property string $wireInputOrder Order that variables with the $input API var are handled when you access $input->var. #pw-group-HTTP-and-input
* @property bool $wireInputLazy Specify true for $input API var to load input data in a lazy fashion and potentially use less memory. Default is false. #pw-group-HTTP-and-input
* @property array $cookieOptions Options for setting cookies from $input->cookie #pw-group-HTTP-and-input
*
* @property bool $advanced Special mode for ProcessWire system development. Not recommended for regular site development or production use. #pw-group-system
* @property bool $demo Special mode for demonstration use that causes POST requests to be disabled. Applies to core, but may not be safe with 3rd party modules. #pw-group-system
* @property bool $debug Special mode for use when debugging or developing a site. Recommended TRUE when site is in development and FALSE when not. #pw-group-system
* @property bool|int $debug Special mode for use when debugging or developing a site. Recommended TRUE when site is in development and FALSE when not. Or set to Config::debugVerbose for verbose debug mode. #pw-group-system
* @property string $debugIf Enable debug mode if condition is met #pw-group-system
* @property array $debugTools Tools, and their order, to show in debug mode (admin) #pw-group-system
*
@@ -86,34 +103,45 @@
* @property array $adminThumbOptions Admin thumbnail image options #pw-group-images
* @property array $httpHosts HTTP hosts For added security, specify the host names ProcessWire should recognize. #pw-group-HTTP-and-input
* @property int $maxPageNum Maximum number of recognized paginations #pw-group-URLs
* @property bool|string|array $noHTTPS When boolean true, pages requiring HTTPS will not enforce it (useful for dev environments). May also specify hostname (string) or hostnames (array) to disable HTTPS for. #pw-group-HTTP-and-input
*
* @property string $dbHost Database host #pw-group-database
* @property string $dbName Database name #pw-group-database
* @property string $dbUser Database user #pw-group-database
* @property string $dbPass Database password #pw-group-database
* @property string $dbPort Database port (default=3306) #pw-group-database
* @property string $dbCharset Default is 'utf8' #pw-group-database
* @property string $dbCharset Default is 'utf8' but 'utf8mb4' is also supported. #pw-group-database
* @property string $dbEngine Database engine (MyISAM or InnoDB) #pw-group-database
* @property string $dbSocket Optional DB socket config for sites that need it. #pw-group-database
* @property bool $dbCache Whether to allow MySQL query caching. #pw-group-database
* @property bool $dbLowercaseTables Force any created field_* tables to be lowercase. #pw-group-database
* @property string $dbEngine Database engine (MyISAM or InnoDB) #pw-group-database
* @property string $dbPath MySQL database exec path (Path to mysqldump) #pw-group-database
* @property array $dbOptions Any additional driver options to pass as $options argument to "new PDO(...)". #pw-group-database
* @property array $dbSqlModes Set or adjust SQL mode per MySQL version, where array keys are MySQL version and values are SQL mode command(s). #pw-group-database
* @property int $dbQueryLogMax Maximum number of queries WireDatabasePDO will log in memory, when debug mode is enabled (default=1000). #pw-group-database
* @property string $dbInitCommand Database init command, for PDO::MYSQL_ATTR_INIT_COMMAND. Note placeholder {charset} gets replaced with $config->dbCharset. #pw-group-database
* @property bool $dbStripMB4 When dbEngine is not utf8mb4 and this is true, we will attempt to remove 4-byte characters (like emoji) from inserts when possible. Note that this adds some overhead. #pw-group-database
*
* @property array $pageList Settings specific to Page lists. #pw-group-modules
* @property array $pageEdit Settings specific to Page editors. #pw-group-modules
* @property array $pageAdd Settings specific to Page adding. #pw-group-modules
* @property string $moduleServiceURL URL where the modules web service can be accessed #pw-group-modules
* @property string $moduleServiceKey API key for modules web service #pw-group-modules
* @property bool $moduleCompile Allow use of compiled modules? #pw-group-modules
* @property array $wireMail Default WireMail module settings. #pw-group-modules
*
* @property array $substituteModules Associative array with names of substitutute modules for when requested module doesn't exist #pw-group-modules
* @property array $substituteModules Associative array with names of substitute modules for when requested module doesn't exist #pw-group-modules
* @property array $logs Additional core logs to keep #pw-group-admin
* @property bool $logIP Include IP address in logs, when applicable? #pw-group-admin
* @property string $defaultAdminTheme Default admin theme: AdminThemeDefault or AdminThemeReno #pw-group-admin
* @property string $fatalErrorHTML HTML used for fatal error messages in HTTP mode. #pw-group-system
* @property array $modals Settings for modal windows #pw-group-admin
* @property array $preloadCacheNames Cache names to preload at beginning of request #pw-group-system
* @property bool $allowExceptions Allow Exceptions to propagate? (default=false, specify true only if you implement your own exception handler) #pw-group-system
* @property bool $usePoweredBy Use the x-powered-by header? Set to false to disable. #pw-group-system
* @property bool $useFunctionsAPI Allow most API variables to be accessed as functions? (see /wire/core/FunctionsAPI.php) #pw-group-system
* @property bool $useMarkupRegions Enable support for front-end markup regions? #pw-group-system
* @property int $lazyPageChunkSize Chunk size for for $pages->findMany() calls. #pw-group-system
*
* @property string $userAuthSalt Salt generated at install time to be used as a secondary/non-database salt for the password system. #pw-group-session
* @property string $userAuthHashType Default is 'sha1' - used only if Blowfish is not supported by the system. #pw-group-session
@@ -124,31 +152,41 @@
* @property string $versionName This is automatically populated with the current PW version name (i.e. 2.5.0 dev) #pw-group-runtime
* @property int $inputfieldColumnWidthSpacing Used by some admin themes to commmunicate to InputfieldWrapper at runtime. #pw-internal
* @property bool $debugMarkupQA Set to true to make the MarkupQA class report verbose debugging messages (to superusers). #pw-internal
* @property array $markupQA Optional settings for MarkupQA class used by FieldtypeTextarea module. #pw-group-modules
* @property string|null $pagerHeadTags Populated at runtime to contain `<link rel=prev|next />` tags for document head, after pagination has been rendered by MarkupPagerNav module. #pw-group-runtime
* @property array $statusFiles File inclusions for ProcessWires runtime statuses/states. #pw-group-system @since 3.0.142
* @property int $status Value of current system status/state corresponding to ProcessWire::status* constants. #pw-internal
*
* @property int $rootPageID ID of homepage (usually 1) #pw-group-system-IDs
* @property int $adminRootPageID ID of admin root page #pw-group-system-IDs
* @property int $trashPageID #pw-group-system-IDs
* @property int $loginPageID #pw-group-system-IDs
* @property int $http404PageID #pw-group-system-IDs
* @property int $usersPageID #pw-group-system-IDs
* @property int $rootPageID Page ID of homepage (usually 1) #pw-group-system-IDs
* @property int $adminRootPageID Page ID of admin root page #pw-group-system-IDs
* @property int $trashPageID Page ID of the trash page. #pw-group-system-IDs
* @property int $loginPageID Page ID of the admin login page. #pw-group-system-IDs
* @property int $http404PageID Page ID of the 404 “page not found” page. #pw-group-system-IDs
* @property int $usersPageID Page ID of the page having users as children. #pw-group-system-IDs
* @property array $usersPageIDs Populated if multiple possible users page IDs (parent for users pages) #pw-group-system-IDs
* @property int $rolesPageID #pw-group-system-IDs
* @property int $permissionsPageID #pw-group-system-IDs
* @property int $guestUserPageID #pw-group-system-IDs
* @property int $superUserPageID #pw-group-system-IDs
* @property int $guestUserRolePageID #pw-group-system-IDs
* @property int $superUserRolePageID #pw-group-system-IDs
* @property int $userTemplateID #pw-group-system-IDs
* @property int $rolesPageID Page ID of the page having roles as children. #pw-group-system-IDs
* @property int $permissionsPageID Page ID of the page having permissions as children. #pw-group-system-IDs
* @property int $guestUserPageID Page ID of the guest (default/not-logged-in) user. #pw-group-system-IDs
* @property int $superUserPageID Page ID of the original superuser (created during installation). #pw-group-system-IDs
* @property int $guestUserRolePageID Page ID of the guest user role (inherited by all users, not just guest). #pw-group-system-IDs
* @property int $superUserRolePageID Page ID of the superuser role. #pw-group-system-IDs
* @property int $userTemplateID Template ID of the user template. #pw-group-system-IDs
* @property array $userTemplateIDs Array of template IDs when multiple allowed for users. #pw-group-system-IDs
* @property int $roleTemplateID #pw-group-system-IDs
* @property int $permissionTemplateID #pw-group-system-IDs
* @property int $externalPageID ID of page assigned to $page API variable when externally bootstrapped #pw-group-system-IDs
* @property array $preloadPageIDs IDs of pages that will be preloaded at beginning of request #pw-group-system-IDs
* @property int $installed Timestamp of when this PW was installed, set automatically for compatibility detection. #pw-group-system
* @property int $roleTemplateID Template ID of the role template. #pw-group-system-IDs
* @property int $permissionTemplateID Template ID of the permission template. #pw-group-system-IDs
* @property int $externalPageID Page ID of page assigned to $page API variable when externally bootstrapped #pw-group-system-IDs
* @property array $preloadPageIDs Page IDs of pages that will always be preloaded at beginning of request #pw-group-system-IDs
* @property int $installed Timestamp of when this PW was installed, set automatically by the installer for future compatibility detection. #pw-group-system
*
*/
class Config extends WireData {
/**
* Constant for verbose debug mode (uses more memory)
*
*/
const debugVerbose = 2;
/**
* Get URL for requested resource or module
*
@@ -162,7 +200,7 @@ class Config extends WireData {
* $url = $config->urls->admin;
* ~~~~~
*
* @param string $for Predefined ProcessWire URLs property or module name
* @param string|Wire $for Predefined ProcessWire URLs property or module name
* @return string|null
*
*/
@@ -171,15 +209,161 @@ class Config extends WireData {
}
/**
* Alias for the url() method
* Get URL for requested resource or module or get all URLs if no argument
*
* #pw-internal
*
* @param string $for Predefined ProcessWire URLs property or module name
* @return null|string
* @param string|Wire $for Predefined ProcessWire URLs property or module name
* @return null|string|Paths
* @since 3.0.130
*
*/
public function urls($for) { return $this->url($for); }
public function urls($for = '') {
return $for === '' ? $this->urls : $this->url($for);
}
/**
* Given a directory to a named location, updates $config->paths and $config->urls for it
*
* - Paths relative to PW installation root should omit the leading slash, i.e. use `site/templates/` and NOT `/site/templates/`.
*
* - If specifying just the `$dir` argument, it updates both `$config->paths` and `$config->urls` for it.
*
* - If specifying both `$dir` and `$url` arguments, then `$dir` refers to `$config->paths` and `$url` refers to `$config->urls`.
*
* - The `$for` argument can be: `cache`, `logs`, `files`, `tmp`, `templates`, or one of your own. Other named locations may
* also work, but since they can potentially be used before PWs “ready” state, they may not be reliable.
*
* - **Warning:** anything that changes a system URL may make the URL no longer have the protection of the root .htaccess file.
* As a result, if you modify system URLs for anything on a live server, you should also update your .htaccess file to
* reflect your changes (while leaving existing rules for original URL in place).
*
* The following example would be in /site/init.php or /site/ready.php (or equivalent module method). In this example we
* are changing the location (path and URL) of our /site/templates/ to use a new version of the files in /site/dev-templates/
* so that we can test them out with user 'karen', while all other users on the site get our regular templates.
* ~~~~~
* // change templates path and URL to /site/dev-templates/ when user name is 'karen'
* if($user->name == 'karen') {
* $config->setLocation('templates', 'site/dev-templates/');
* }
* ~~~~~
*
* @param string $for Named location from `$config->paths` or `$config->urls`, one of: `cache`, `logs`, `files`, `tmp`, `templates`, or your own.
* @param string $dir Directory or URL to the location. Should be either a path or URL relative to current installation root (recommended),
* or an absolute disk path that resolves somewhere in current installation root. If specifying an absolute path outside of the installation
* root, then youll also want to provide the $url argument since PW wont know it. You may also specify a blank string for this argument
* if you only want to set the $url argument.
* @param string|bool $url If the $dir argument represents both the path and URL relative to site root, you can omit this argument.
* If path and URL cannot be derived from one another, or you only want to modify the $url (leaving $dir blank), you
* can specify the URL in this argument. Specify boolean false if you only want to set the $dir (path) and not detect the $url from it.
* @return self
* @throws WireException If request cannot be accommodated
* @since 3.0.141
*
*/
public function setLocation($for, $dir, $url = '') {
if($for === 'root') throw new WireException('Root path can only be changed at boot');
if(!empty($dir)) {
$rootPath = $this->paths->get('root');
// make sure path uses unix-style slashes
$dir = Paths::normalizeSeparators($dir);
// if given path is inclusive of root path, make path relative to site root
if(strpos($dir, $rootPath) === 0) $dir = substr($dir, strlen($rootPath));
// ensure trailing slash
if(substr($dir, -1) !== '/') $dir .= '/';
}
// now determine the URL to set
if($url === false) {
// arguments say to skip setting URL
} else if(empty($url)) {
// URL and path are the same relative to site root
if(!empty($dir)) $url = $dir;
} else {
// given a custom URL
$rootUrl = $this->urls->get('root');
// if URL begins at PW installation root, remove the root part of the URL
if(strpos($url, $rootUrl) === 0) $url = substr($url, strlen($rootUrl));
// ensure trailing slash
if(substr($url, -1) !== '/' && strpos($url, '?') === false && strpos($url, '#') === false) $url .= '/';
}
if(!empty($dir)) $this->paths->set($for, $dir);
if(!empty($url)) $this->urls->set($for, $url);
return $this;
}
/**
* Change or set just the server disk path for the named location (leaving URL as-is)
*
* - If you want to update both disk path and URL at the same time, or if URL and path are going to be the same relative to
* installation root, use the `setLocation()` method instead.
*
* - Paths relative to PW installation root should omit the leading slash, i.e. use `site/templates/` and NOT `/site/templates/`.
*
* - The `$for` argument can be: `cache`, `logs`, `files`, `tmp`, `templates`, or one of your own. Other named locations may
* also work, but since they can potentially be used before PWs “ready” state, they may not be reliable.
*
* @param string $for Named location from `$config->paths`, one of: `cache`, `logs`, `files`, `tmp`, `templates`, or your own.
* @param string $path Path relative to PW installation root (no leading slash), or absolute path if not.
* @return self
* @throws WireException
* @since 3.0.141
*
*/
public function setPath($for, $path) {
return $this->setLocation($for, $path, false);
}
/**
* Change or set just the URL for the named location (leaving server disk path as-is)
*
* - If you want to update both disk path and URL at the same time, or if URL and path are going to be the same relative to
* installation root, use the `setLocation()` method instead.
*
* - Paths relative to PW installation root should omit the leading slash, i.e. use `site/templates/` and NOT `/site/templates/`.
*
* - The `$for` argument can be: `cache`, `logs`, `files`, `tmp`, `templates`, or one of your own. Other named locations may
* also work, but since they can potentially be used before PWs “ready” state, they may not be reliable.
*
* - **Warning:** anything that changes a system URL may make the URL no longer have the protection of the root .htaccess file.
* As a result, if you modify system URLs for anything on a live server, you should also update your .htaccess file to
* reflect your changes (while leaving existing rules for original URL in place).
*
* The following examples would go in /site/ready.php.
*
* Lets say we created a symbolic link in our web root `/tiedostot/` (Finnish for “files”) that points to /site/assets/files/.
* We want all of our file URLs to appear as “/tiedostot/1234/img.jpg” rather than “/site/assets/files/1234/img.jpg”. We would
* change the URL for ProcessWires `$config->urls->files` to point there like this example below. (Please also see warning above)
* ~~~~~
* if($page->template != 'admin') {
* $config->setUrl('files', 'tiedostot/');
* }
* ~~~~~
* In this next example, we are changing all of our file URLs on the front-end to point a cookieless subdomain that maps all
* requests to the root path of https://files.domain.com to /site/assets/files/. The example works for CDNs as well.
* ~~~~~
* if($page->template != 'admin) {
* $config->setUrl('files', 'https://files.domain.com/');
* }
* ~~~~~
*
* @param string $for Named location from `$config->urls`, one of: `cache`, `logs`, `files`, `tmp`, `templates`, or your own.
* @param string $url URL relative to PW installation root (no leading slash) or absolute URL if not (optionally including scheme and domain).
* @return self
* @throws WireException
* @since 3.0.141
*
*/
public function setUrl($for, $url) {
return $this->setLocation($for, '', $url);
}
/**
* Get disk path for requested resource or module
@@ -203,15 +387,18 @@ class Config extends WireData {
}
/**
* Alias for the path() method
* Get disk path for requested resource or module or get all paths if no argument
*
* #pw-internal
*
* @param string $for Predefined ProcessWire paths property or module name
* @return null|string
* @return null|string|Paths
* @since 3.0.130
*
*/
public function paths($for) { return $this->paths($for); }
public function paths($for = '') {
return $for === '' ? $this->paths : $this->path($for);
}
/**
* List of config keys that are also exported in javascript
@@ -324,6 +511,9 @@ class Config extends WireData {
* 'siteOnly' => true,
* 'cachePath' => $config->paths->root . '.my-cache/'
* ]);
*
* // To unset a property specify null for first argument and property to unset as second argument
* $config->fileCompilerOptions(null, 'siteOnly');
* ~~~~~
*
* #pw-internal
@@ -358,7 +548,14 @@ class Config extends WireData {
}
} else {
// property and value provided
$value[$property] = $arguments[1];
if($property === null && is_string($arguments[1])) {
// unset property
$property = $arguments[1];
unset($value[$property]);
} else {
// set property with value
$value[$property] = $arguments[1];
}
parent::set($method, $value);
}
} else if($numArgs === 1) {
@@ -368,5 +565,98 @@ class Config extends WireData {
return $this;
}
/**
* Return true if current PHP version is equal to or newer than the given version
*
* ~~~~~
* if($config->phpVersion('7.0.0')) {
* // PHP version is 7.x
* }
* ~~~~~
*
* #pw-group-tools
*
* @param string|null $minVersion
* @return bool
* @since 3.0.101
*
*/
public function phpVersion($minVersion) {
return version_compare(PHP_VERSION, $minVersion) >= 0;
}
/**
* Check if current ProcessWire version is equal to or newer than given version, or return current version
*
* If no version argument is given, it simply returns the current ProcessWire version (3.0.130+)
*
* ~~~~~
* if($config->version('3.0.100')) {
* // ProcessWire version is 3.0.100 or newer
* }
* ~~~~~
*
* #pw-group-tools
*
* @param string $minVersion Specify version string if you want to compare against current version
* @return bool|string Returns current version if no argument given (3.0.130+), OR boolean if given a version argument:
* - If given version is older than current, returns false.
* - If given version is equal to or newer than current, returns true.
* @since 3.0.106 with no-argument behavior added in 3.0.130
*
*/
public function version($minVersion = '') {
if($minVersion === '') return $this->version;
return version_compare($this->version, $minVersion) >= 0;
}
/**
* Was this ProcessWire installation installed after a particular date?
*
* #pw-group-tools
*
* @param int|string $date Unix timestamp or strtotime() compatible date string
* @return bool
* @see Config::installedBefore(), Config::installed
* @since 3.0.129
*
*/
public function installedAfter($date) {
if(!ctype_digit("$date")) $date = strtotime($date);
return $this->installed > $date;
}
/**
* Was this ProcessWire installation installed before a particular date?
*
* #pw-group-tools
*
* @param int|string $date Unix timestamp or strtotime() compatible date string
* @return bool
* @see Config::installedAfter(), Config::installed
* @since 3.0.129
*
*/
public function installedBefore($date) {
if(!ctype_digit("$date")) $date = strtotime($date);
return $this->installed < $date;
}
/**
* Set the current ProcessWire instance for this object (PW 3.0)
*
* #pw-internal
*
* @param ProcessWire $wire
*
*/
public function setWire(ProcessWire $wire) {
parent::setWire($wire);
$paths = $this->paths;
if($paths) $paths->setWire($wire);
$urls = $this->urls;
if($urls) $urls->setWire($wire);
}
}

View File

@@ -104,7 +104,7 @@ class Database extends \mysqli implements WireDatabase {
if($this->debug) {
$timerKey = Debug::timer();
if(!$timerFirstStartTime) $timerFirstStartTime = $timerKey;
if(!$timerFirstStartTime) $timerFirstStartTime = (float) $timerKey;
} else $timerKey = null;
$result = @parent::query($sql, $resultmode);
@@ -113,9 +113,9 @@ class Database extends \mysqli implements WireDatabase {
if($this->debug) {
if(isset($result->num_rows)) $sql .= " [" . $result->num_rows . " rows]";
if(!is_null($timerKey)) {
$elapsed = Debug::timer($timerKey);
$elapsed = (float) Debug::timer($timerKey);
$timerTotalQueryTime += $elapsed;
$timerTotalSinceStart = Debug::timer() - $timerFirstStartTime;
$timerTotalSinceStart = ((float) Debug::timer()) - $timerFirstStartTime;
$sql .= " [{$elapsed}s, {$timerTotalQueryTime}s, {$timerTotalSinceStart}s]";
}
$this->queryLog($sql);

View File

@@ -18,7 +18,7 @@
*
* @property array $where
* @property array $bindValues
* @properety array $bindIndex
* @property array $bindIndex
*
*/
abstract class DatabaseQuery extends WireData {
@@ -216,6 +216,9 @@ abstract class DatabaseQuery extends WireData {
/**
* Execute the query with the current database handle
*
* @return \PDOStatement
* @throws WireException|\Exception|\PDOException
*
*/
public function execute() {

View File

@@ -20,7 +20,7 @@
* This file is licensed under the MIT license
* https://processwire.com/about/license/mit/
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2019 by Ryan Cramer
* https://processwire.com
*
*
@@ -74,12 +74,14 @@ class DatabaseQuerySelectFulltext extends Wire {
* @param string $tableName
* @param string $fieldName
* @param string $operator
* @param string $value
* @param string|int|array $value Value to match. Array value support added 3.0.141 (not used by PageFinder)
* @return $this
* @throws WireException If given $operator argument is not implemented here
*
*/
public function match($tableName, $fieldName, $operator, $value) {
if(is_array($value)) return $this->matchArrayValue($tableName, $fieldName, $operator, $value);
$database = $this->wire('database');
$query = $this->query;
@@ -107,11 +109,12 @@ class DatabaseQuerySelectFulltext extends Wire {
break;
case '~=':
case '!~=':
$words = preg_split('/[-\s,]/', $value, -1, PREG_SPLIT_NO_EMPTY);
foreach($words as $word) {
$len = function_exists('mb_strlen') ? mb_strlen($word) : strlen($word);
if(DatabaseStopwords::has($word) || $len < $database->getVariable('ft_min_word_len')) {
$this->matchWordLIKE($tableName, $fieldName, $word);
$this->matchWordLIKE($tableName, $fieldName, $operator, $word);
} else {
$this->matchContains($tableName, $fieldName, $operator, $word);
}
@@ -146,6 +149,37 @@ class DatabaseQuerySelectFulltext extends Wire {
return $this;
}
/**
* Match when given $value is an array
*
* Note: PageFinder uses its own array-to-value conversion, so this case applies only to other usages outside PageFinder,
* such as FieldtypeMulti::getLoadQueryWhere()
*
* @param string $tableName
* @param string $fieldName
* @param string $operator
* @param array $value
* @return $this
* @since 3.0.141
* @throws WireException
*
*/
protected function matchArrayValue($tableName, $fieldName, $operator, $value) {
if($operator === '~=') {
throw new WireException("Operator ~= is not supported for $fieldName with OR value condition");
}
// convert *= operator to %= to make the query possible (avoiding matchContains method)
if($operator === '*=') $operator = '%=';
$query = $this->query;
$this->query = $this->wire(new DatabaseQuerySelect());
foreach($value as $v) {
$this->match($tableName, $fieldName, $operator, "$v");
}
$query->where(implode(" OR ", $this->query->where));
$this->query = $query;
return $this;
}
/**
* @param string $tableName
* @param string $fieldName
@@ -159,26 +193,32 @@ class DatabaseQuerySelectFulltext extends Wire {
$tableField = "$tableName.$fieldName";
$database = $this->wire('database');
$v = $database->escapeStr($value);
$not = strpos($operator, '!') === 0;
if($not) $operator = ltrim($operator, '!');
$n = 0;
do {
$scoreField = "_score_{$tableName}_{$fieldName}" . (++$n);
} while(in_array($scoreField, self::$scoreFields));
self::$scoreFields[] = $scoreField;
$query->select("MATCH($tableField) AGAINST('$v') AS $scoreField");
$match = $not ? 'NOT MATCH' : 'MATCH';
$query->select("$match($tableField) AGAINST('$v') AS $scoreField");
$query->orderby($scoreField . " DESC");
$partial = $operator != '~=';
$partial = $operator != '~=' && $operator != '!~=';
$booleanValue = $database->escapeStr($this->getBooleanQueryValue($value, true, $partial));
if($booleanValue) $j = "MATCH($tableField) AGAINST('$booleanValue' IN BOOLEAN MODE) ";
else $j = '';
if($booleanValue) {
$j = "$match($tableField) AGAINST('$booleanValue' IN BOOLEAN MODE) ";
} else {
$j = '';
}
if($operator == '^=' || $operator == '$=' || ($operator == '*=' && (!$j || preg_match('/[-\s]/', $v)))) {
// if $operator is a ^begin/$end, or if there are any word separators in a *= operator value
if($operator == '^=' || $operator == '$=') {
$type = 'RLIKE';
$type = $not ? 'NOT RLIKE' : 'RLIKE';
$v = $database->escapeStr(preg_quote($value)); // note $value not $v
$like = "[[:space:]]*(<[^>]+>)*[[:space:]]*";
if($operator == '^=') {
@@ -188,7 +228,7 @@ class DatabaseQuerySelectFulltext extends Wire {
}
} else {
$type = 'LIKE';
$type = $not ? 'NOT LIKE' : 'LIKE';
$v = $this->escapeLIKE($v);
$like = "%$v%";
}
@@ -208,15 +248,17 @@ class DatabaseQuerySelectFulltext extends Wire {
*
* @param string $tableName
* @param string $fieldName
* @param string $operator
* @param $word
*
*/
protected function matchWordLIKE($tableName, $fieldName, $word) {
protected function matchWordLIKE($tableName, $fieldName, $operator, $word) {
$tableField = "$tableName.$fieldName";
$database = $this->wire('database');
$v = $database->escapeStr(preg_quote($word));
$regex = "([[[:blank:][:punct:]]|^)$v([[:blank:][:punct:]]|$)";
$where = "($tableField REGEXP '$regex')";
$type = strpos($operator, '!') === 0 ? 'NOT REGEXP' : 'REGEXP';
$where = "($tableField $type '$regex')";
$this->query->where($where);
}
@@ -244,6 +286,8 @@ class DatabaseQuerySelectFulltext extends Wire {
//$a = preg_split('/[-\s,+*!.?()=;]+/', $value);
$a = preg_split('/[-\s,+*!?()=;]+/', $value);
foreach($a as $k => $v) {
$v = trim($v);
if(!strlen($v)) continue;
if(DatabaseStopwords::has($v)) {
continue;
}

View File

@@ -66,7 +66,7 @@ class Debug {
$startTime = -microtime(true);
if(!$key) {
$key = (string) $startTime;
while(isset(self::$timers[$key])) $key .= ".";
while(isset(self::$timers[$key])) $key .= "0";
}
self::$timers[(string) $key] = $startTime;
$value = $key;
@@ -154,4 +154,234 @@ class Debug {
self::$timers = array();
}
/**
* Return a backtrace array that is simpler and more PW-specific relative to PHPs debug_backtrace
*
* @param array $options
* @return array|string
* @since 3.0.136
*
*/
static public function backtrace(array $options = array()) {
$defaults = array(
'limit' => 0, // the limit argument for the debug_backtrace call
'flags' => DEBUG_BACKTRACE_PROVIDE_OBJECT, // flags for PHP debug_backtrace method
'showHooks' => false, // show internal methods for hook calls?
'getString' => false, // get newline separated string rather than array?
'maxCount' => 10, // max size for arrays
'maxStrlen' => 100, // max length for strings
'maxDepth' => 5, // max allowed recursion depth when converting variables to strings
'ellipsis' => ' …', // show this ellipsis when a long value is truncated
'skipCalls' => array(), // method/function calls to skip
);
$options = array_merge($defaults, $options);
if($options['limit']) $options['limit']++;
$traces = @debug_backtrace($options['flags'], $options['limit']);
$config = wire('config');
$rootPath = ProcessWire::getRootPath(true);
$rootPath2 = $config && $config->paths ? $config->paths->root : $rootPath;
array_shift($traces); // shift of the simpleBacktrace call, which is not needed
$apiVars = array();
$result = array();
$cnt = 0;
foreach(wire('all') as $name => $value) {
if(!is_object($value)) continue;
$apiVars[wireClassName($value)] = '$' . $name;
}
foreach($traces as $n => $trace) {
if(!is_array($trace) || !isset($trace['function']) || !isset($trace['file'])) {
continue;
} else if(count($options['skipCalls']) && in_array($trace['function'], $options['skipCalls'])) {
continue;
}
$obj = null;
$class = '';
$type = '';
$args = $trace['args'];
$argStr = '';
$file = $trace['file'];
$basename = basename($file);
$function = $trace['function'];
$isHookableCall = false;
if(isset($trace['object'])) {
$obj = $trace['object'];
$class = wireClassName($obj);
} else if(isset($trace['class'])) {
$class = wireClassName($trace['class']);
}
if($class) {
$type = isset($trace['type']) ? $trace['type'] : '.';
}
if(!$options['showHooks']) {
if($basename === 'Wire.php' && !wireMethodExists('Wire', $function)) continue;
if($class === 'WireHooks' || $basename === 'WireHooks.php') continue;
}
if(strpos($function, '___') === 0) {
$isHookableCall = '___';
} else if($obj && !method_exists($obj, $function) && method_exists($obj, "___$function")) {
$isHookableCall = true;
}
if($type === '->' && isset($apiVars[$class])) {
// use API var name when available
if(strtolower($class) === strtolower(ltrim($apiVars[$class], '$'))) {
$class = $apiVars[$class];
} else {
$class = "$class " . $apiVars[$class];
}
}
if($basename === 'Wire.php' && $class !== 'Wire') {
$ref = new \ReflectionClass($trace['class']);
$file = $ref->getFileName();
}
// rootPath and rootPath2 can be different if one of them represented by a symlink
$file = str_replace($rootPath, '/', $file);
if($rootPath2 !== $rootPath) $file = str_replace($rootPath2, '/', $file);
if(($function === '__call' || $function == '_callMethod') && count($args)) {
$function = array_shift($args);
}
if(!$options['showHooks'] && $isHookableCall === '___') {
$function = substr($function, 3);
}
if(!empty($args)) {
$newArgs = array();
if($isHookableCall && count($args) === 1 && is_array($args[0])) {
$newArgs = $args[0];
}
foreach($args as $arg) {
if(is_object($arg)) {
$arg = wireClassName($arg) . ' $obj';
} else if(is_array($arg)) {
$count = count($arg);
if($count < 4) {
$arg = $count ? self::toStr($arg, array('maxDepth' => 2)) : '[]';
} else {
$arg = 'array(' . count($arg) . ')';
}
} else if(is_string($arg)) {
if(strlen("$arg") > $options['maxStrlen']) $arg = substr($arg, 0, $options['maxStrlen']) . ' …';
$arg = '"' . $arg . '"';
} else if(is_bool($arg)) {
$arg = $arg ? 'true' : 'false';
} else {
// leave as-is
}
$newArgs[] = $arg;
}
$argStr = implode(', ', $newArgs);
if($argStr === '[]') $argStr = '';
}
$call = "$class$type$function($argStr)";
$file = "$file:$trace[line]";
if($options['getString']) {
$result[] = "$cnt. $file » $call";
} else {
$result[] = array(
'file' => $file,
'call' => $call,
);
}
$cnt++;
}
if($options['getString']) $result = implode("\n", $result);
return $result;
}
/**
* Convert value to string for backtrace method
*
* @param $value
* @param array $options
* @return null|string
*
*/
static protected function toStr($value, array $options = array()) {
$defaults = array(
'maxCount' => 10, // max size for arrays
'maxStrlen' => 100, // max length for strings
'maxDepth' => 5,
'ellipsis' => ' …'
);
static $depth = 0;
$options = count($options) ? array_merge($defaults, $options) : $defaults;
$depth++;
if(is_object($value)) {
// object
$str = wireClassName($value);
if($str === 'HookEvent') {
$str .= ' $event';
} else if(method_exists($value, '__toString')) {
$value = (string) $value;
if($value !== $str) {
if(strlen($value) > $options['maxStrlen']) {
$value = substr($value, 0, $options['maxStrlen']) . $options['ellipsis'];
}
$str .= "($value)";
}
}
} else if(is_array($value)) {
// array
if(empty($value)) {
$str = '[]';
} else if($depth >= $options['maxDepth']) {
$str = "array(" . count($value) . ")";
} else {
$suffix = '';
if(count($value) > $options['maxCount']) {
$value = array_slice($value, 0, $options['maxCount']);
$suffix = $options['ellipsis'];
}
foreach($value as $k => $v) {
$value[$k] = self::toStr($v, $options);
}
$str = '[ ' . implode(', ', $value) . $suffix . ' ]';
}
} else if(is_string($value)) {
// string
if(strlen($value) > $options['maxStrlen']) {
$value = substr($value, 0, $options['maxStrlen']) . $options['ellipsis'];
}
$hasDQ = strpos($value, '"') !== false;
$hasSQ = strpos($value, "'") !== false;
if(($hasDQ && $hasSQ) || $hasSQ) {
$value = str_replace('"', '\\"', $value);
$str = '"' . $value . '"';
} else {
$str = "'$value'";
}
} else if(is_bool($value)) {
// true or false
$str = $value ? 'true' : 'false';
} else {
// int, float or other
$str = $value;
}
$depth--;
return $str;
}
}

View File

@@ -8,7 +8,7 @@
* This file is licensed under the MIT license
* https://processwire.com/about/license/mit/
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2018 by Ryan Cramer
* https://processwire.com
*
*/
@@ -20,21 +20,113 @@
class WireException extends \Exception {}
/**
* Triggered when access to a resource is not allowed
* Thrown when access to a resource is not allowed
*
*/
class WirePermissionException extends WireException {}
/**
* Triggered when a requested item does not exist and generates a fatal error
* Thrown when a requested page does not exist, or can be thrown manually to show the 404 page
*
*/
class Wire404Exception extends WireException {}
class Wire404Exception extends WireException {
/**
* 404 is because core determined requested resource by URL does not physically exist
*
* #pw-internal
*
*/
const codeNonexist = 404;
/**
* 404 is a result of a resource that might exist but there is no access
*
* Similar to a WirePermissionException except always still a 404 externally
*
* #pw-internal
*
*/
const codePermission = 4041;
/**
* 404 is a result of a secondary non-file asset that does not exist, even if page does
*
* For example: /foo/bar/?id=123 where /foo/bar/ exists but 123 points to non-existent asset.
*
* #pw-internal
*
*/
const codeSecondary = 4042;
/**
* 404 is a result of content not available in requested language
*
* #pw-internal
*
*/
const codeLanguage = 4043;
/**
* 404 is a result of a physical file that does not exist on the file system
*
* #pw-internal
*
*/
const codeFile = 4044;
/**
* 404 is a result of a front-end wire404() function call
*
* #pw-internal
*
*/
const codeFunction = 4045;
/**
* Anonymous 404 with no code provided
*
* #pw-internal
*
*/
const codeAnonymous = 0;
}
/**
* WireDatabaseException is the exception thrown by the Database class
*
* If you use this class without ProcessWire, change 'extends WireException' below to be just 'extends Exception'
* Thrown when ProcessWire is unable to connect to the database at boot
*
*/
class WireDatabaseException extends WireException {}
/**
* Thrown when cross site request forgery detected by SessionCSRF::validate()
*
*/
class WireCSRFException extends WireException {}
/**
* Thrown when a requested Process or Process method is requested that doesnt exist
*
*/
class ProcessController404Exception extends Wire404Exception { }
/**
* Thrown when the user doesnt have access to execute the requested Process or method
*
*/
class ProcessControllerPermissionException extends WirePermissionException { }
/**
* Thrown by PageFinder when an error occurs trying to find pages
*
*/
class PageFinderException extends WireException { }
/**
* Thrown by PageFinder when it detects an error in the syntax of a given page-finding selector
*
*/
class PageFinderSyntaxException extends PageFinderException { }

View File

@@ -7,10 +7,12 @@
* and is managed by the 'Fields' class.
*
* #pw-summary Field represents a custom field that is used on a Page.
* #pw-var $field
* #pw-instantiate $field = $fields->get('field_name');
* #pw-body Field objects are managed by the `$fields` API variable.
* #pw-use-constants
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2019 by Ryan Cramer
* https://processwire.com
*
* @property int $id Numeric ID of field in the database #pw-group-properties
@@ -18,17 +20,29 @@
* @property string $table Database table used by the field #pw-group-properties
* @property string $prevTable Previously database table (if field was renamed) #pw-group-properties
* @property Fieldtype|null $type Fieldtype module that represents the type of this field #pw-group-properties
* @property Fieldtype $prevFieldtype Previous Fieldtype, of type was changed #pw-group-properties
* @property Fieldtype|null $prevFieldtype Previous Fieldtype, if type was changed #pw-group-properties
* @property int $flags Bitmask of flags used by this field #pw-group-properties
* @property-read string $flagsStr Names of flags used by this field (readonly) #pw-group-properties
* @property string $label Text string representing the label of the field #pw-group-properties
* @property string $description Longer description text for the field #pw-group-properties
* @property string $notes Additional notes text about the field #pw-group-properties
* @property string $icon Icon name used by the field, if applicable #pw-group-properties
* @property string $tags Tags that represent this field, if applicable (space separated string). #pw-group-properties
* @property-read array $tagList Same as $tags property, but as an array. #pw-group-properties
* @property bool $useRoles Whether or not access control is enabled #pw-group-access
* @property array $editRoles Role IDs with edit access, applicable only if access control is enabled. #pw-group-access
* @property array $viewRoles Role IDs with view access, applicable only if access control is enabled. #pw-group-access
* @property array|null $orderByCols Columns that WireArray values are sorted by (default=null), Example: "sort" or "-created". #pw-internal
* @property int|null $paginationLimit Used by paginated WireArray values to indicate limit to use during load. #pw-internal
* @property array $allowContexts Names of settings that are custom configured to be allowed for context. #pw-group-properties
*
* Common Inputfield properties that Field objects store:
* @property int|bool|null $required Whether or not this field is required during input #pw-group-properties
* @property string|null $requiredIf A selector-style string that defines the conditions under which input is required #pw-group-properties
* @property string|null $showIf A selector-style string that defines the conditions under which the Inputfield is shown #pw-group-properties
* @property int|null $columnWidth The Inputfield column width (percent) 10-100. #pw-group-properties
* @property int|null $collapsed The Inputfield 'collapsed' value (see Inputfield collapsed constants). #pw-group-properties
* @property int|null $textFormat The Inputfield 'textFormat' value (see Inputfield textFormat constants). #pw-group-properties
*
* @method bool viewable(Page $page = null, User $user = null) Is the field viewable on the given $page by the given $user? #pw-group-access
* @method bool editable(Page $page = null, User $user = null) Is the field editable on the given $page by the given $user? #pw-group-access
@@ -127,10 +141,10 @@ class Field extends WireData implements Saveable, Exportable {
*/
protected $settings = array(
'id' => 0,
'type' => null,
'flags' => 0,
'name' => '',
'label' => '',
'flags' => 0,
'type' => null,
);
/**
@@ -194,6 +208,14 @@ class Field extends WireData implements Saveable, Exportable {
*/
protected $inputfieldSettings = array();
/**
* Tags assigned to this field, keys are lowercase version of tag, values can possibly contain mixed case
*
* @var null|array
*
*/
protected $tagList = null;
/**
* True if lowercase tables should be enforce, false if not (null = unset). Cached from $config
*
@@ -210,7 +232,7 @@ class Field extends WireData implements Saveable, Exportable {
*
* @param string $key Property name to set
* @param mixed $value
* @return $this
* @return Field|WireData
*
*/
public function set($key, $value) {
@@ -332,17 +354,27 @@ class Field extends WireData implements Saveable, Exportable {
*
*/
public function get($key) {
if($key === 'type' && isset($this->settings['type'])) {
$value = $this->settings['type'];
if($value) $value->setLastAccessField($this);
return $value;
}
if($key == 'viewRoles') return $this->viewRoles;
else if($key == 'editRoles') return $this->editRoles;
else if($key == 'table') return $this->getTable();
else if($key == 'prevTable') return $this->prevTable;
else if($key == 'prevFieldtype') return $this->prevFieldtype;
else if(isset($this->settings[$key])) return $this->settings[$key];
else if($key == 'icon') return $this->getIcon(true);
else if($key == 'useRoles') return ($this->settings['flags'] & self::flagAccess) ? true : false;
else if($key == 'flags') return $this->settings['flags'];
else if($key == 'editRoles') return $this->editRoles;
else if($key == 'table') return $this->getTable();
else if($key == 'prevTable') return $this->prevTable;
else if($key == 'prevFieldtype') return $this->prevFieldtype;
else if(isset($this->settings[$key])) return $this->settings[$key];
else if($key == 'icon') return $this->getIcon(true);
else if($key == 'useRoles') return ($this->settings['flags'] & self::flagAccess) ? true : false;
else if($key == 'flags') return $this->settings['flags'];
else if($key == 'flagsStr') return $this->wire('fields')->getFlagNames($this->settings['flags'], true);
else if($key == 'tagList') return $this->getTags();
else if($key == 'tags') return $this->getTags(true);
$value = parent::get($key);
if($key === 'allowContexts' && !is_array($value)) $value = array();
if(is_array($this->trackGets)) $this->trackGets($key);
return $value;
}
@@ -438,6 +470,20 @@ class Field extends WireData implements Saveable, Exportable {
if(strpos($key, '_') === 0) unset($data[$key]);
}
// convert access roles from IDs to names
if($this->useRoles) {
foreach(array('viewRoles', 'editRoles') as $roleType) {
if(!is_array($data[$roleType])) $data[$roleType] = array();
$roleNames = array();
foreach($data[$roleType] as $key => $roleID) {
$role = $this->wire('roles')->get($roleID);
if(!$role || !$role->id) continue;
$roleNames[] = $role->name;
}
$data[$roleType] = $roleNames;
}
}
return $data;
}
@@ -487,7 +533,11 @@ class Field extends WireData implements Saveable, Exportable {
$this->type = $this->wire('fieldtypes')->get($data['type']);
}
if(!$this->type) $this->type = $this->wire('fieldtypes')->get('FieldtypeText');
if(!$this->type) {
if(!empty($data['type'])) $this->error("Unable to locate field type: $data[type]");
$this->type = $this->wire('fieldtypes')->get('FieldtypeText');
}
$data = $this->type->importConfigData($this, $data);
// populate import data
@@ -566,7 +616,7 @@ class Field extends WireData implements Saveable, Exportable {
} else if(is_string($type)) {
$typeStr = $type;
$fieldtypes = $this->wire('fieldtypes');
$fieldtypes = $this->wire('fieldtypes'); /** @var Fieldtypes $fieldtypes */
if(!$type = $fieldtypes->get($type)) {
$this->error("Fieldtype '$typeStr' does not exist");
return $this;
@@ -622,6 +672,15 @@ class Field extends WireData implements Saveable, Exportable {
$ids[] = (int) $role;
} else if($role instanceof Role) {
$ids[] = (int) $role->id;
} else if(is_string($role) && strlen($role)) {
$rolePage = $this->wire('roles')->get($role);
if($rolePage && $rolePage->id) {
$ids[] = $rolePage->id;
} else {
$this->error("Unknown role '$role'");
}
} else {
// invalid
}
}
if($type == 'view') {
@@ -802,8 +861,10 @@ class Field extends WireData implements Saveable, Exportable {
$inputfield->attr('name', $this->name . $contextStr);
$inputfield->set('label', $this->label);
// just in case an Inputfield needs to know it's Fieldtype context, or lack of it
$inputfield->set('hasFieldtype', $this->type);
// just in case an Inputfield needs to know its Fieldtype/Field context, or lack of it
$inputfield->set('hasFieldtype', $this->type);
$inputfield->set('hasField', $this);
$inputfield->set('hasPage', $page);
// custom field settings
foreach($this->data as $key => $value) {
@@ -906,6 +967,7 @@ class Field extends WireData implements Saveable, Exportable {
if($fieldgroupContext) {
$allowContext = $this->type->getConfigAllowContext($this);
if(!is_array($allowContext)) $allowContext = array();
$allowContext = array_merge($allowContext, $this->allowContexts);
} else {
$allowContext = array();
}
@@ -916,6 +978,8 @@ class Field extends WireData implements Saveable, Exportable {
if(!$fieldgroupContext) $inputfields->head = $this->_('Field type details');
$inputfields->attr('title', $this->_('Details'));
$inputfields->attr('id+name', 'fieldtypeConfig');
$remainingNames = array();
foreach($allowContext as $name) $remainingNames[$name] = $name;
try {
$fieldtypeInputfields = $this->type->getConfigInputfields($this);
@@ -930,7 +994,19 @@ class Field extends WireData implements Saveable, Exportable {
foreach($fieldtypeInputfields as $inputfield) {
if($fieldgroupContext && !in_array($inputfield->name, $allowContext)) continue;
$inputfields->append($inputfield);
unset($remainingNames[$inputfield->name]);
}
// now capture those that may have been stuck in a fieldset
if($fieldgroupContext) {
foreach($remainingNames as $name) {
if($inputfields->getChildByName($name)) continue;
$inputfield = $fieldtypeInputfields->getChildByName($name);
if(!$inputfield) continue;
$inputfields->append($inputfield);
unset($remainingNames[$inputfield->name]);
}
}
} catch(\Exception $e) {
$this->trackException($e, false, true);
}
@@ -945,13 +1021,18 @@ class Field extends WireData implements Saveable, Exportable {
if($inputfield) {
if($fieldgroupContext) {
$allowContext = array('visibility', 'collapsed', 'columnWidth', 'required', 'requiredIf', 'showIf');
$allowContext = array_merge($allowContext, $inputfield->getConfigAllowContext($this));
$allowContext = array_merge($allowContext, $this->allowContexts, $inputfield->getConfigAllowContext($this));
} else {
$allowContext = array();
$inputfields->head = $this->_('Input field settings');
}
$remainingNames = array();
foreach($allowContext as $name) {
$remainingNames[$name] = $name;
}
$inputfields->attr('title', $this->_('Input'));
$inputfields->attr('id+name', 'inputfieldConfig');
/** @var InputfieldWrapper $inputfieldInputfields */
$inputfieldInputfields = $inputfield->getConfigInputfields();
if(!$inputfieldInputfields) $inputfieldInputfields = $this->wire(new InputfieldWrapper());
$configArray = $inputfield->getConfigArray();
@@ -964,6 +1045,16 @@ class Field extends WireData implements Saveable, Exportable {
foreach($inputfieldInputfields as $i) {
if($fieldgroupContext && !in_array($i->name, $allowContext)) continue;
$inputfields->append($i);
unset($remainingNames[$i->name]);
}
if($fieldgroupContext) {
foreach($remainingNames as $name) {
if($inputfields->getChildByName($name)) continue;
$inputfield = $inputfieldInputfields->getChildByName($name);
if(!$inputfield) continue;
$inputfields->append($inputfield);
unset($remainingNames[$inputfield->name]);
}
}
}
@@ -997,6 +1088,8 @@ class Field extends WireData implements Saveable, Exportable {
/**
* Set an override table name, or omit (or null) to restore default table name
*
* #pw-group-advanced
*
* @param null|string $table
*
*/
@@ -1179,6 +1272,104 @@ class Field extends WireData implements Saveable, Exportable {
return $this;
}
/**
* Get tags
*
* @param bool|string $getString Optionally specify true for space-separated string, or delimiter string (default=false)
* @return array|string Returns array of tags unless $getString option is requested
* @since 3.0.106
*
*/
public function getTags($getString = false) {
if($this->tagList === null) {
$tagList = $this->setTags(parent::get('tags'));
} else {
$tagList = $this->tagList;
}
if($getString !== false) {
$delimiter = $getString === true ? ' ' : $getString;
return implode($delimiter, $tagList);
}
return $tagList;
}
/**
* Set all tags
*
* #pw-internal
*
* @param array|string $tagList Array of tags to add (or space-separated string)
* @param bool $reindex Set to false to set given $tagsList exactly as-is (assumes it's already in correct format)
* @return array Array of tags that were set
* @since 3.0.106
*
*/
public function setTags($tagList, $reindex = true) {
if($tagList === null || $tagList === '') {
$tagList = array();
} else if(!is_array($tagList)) {
$tagList = explode(' ', $tagList);
}
if($reindex && count($tagList)) {
$tags = array();
foreach($tagList as $tag) {
$tag = trim($tag);
if(strlen($tag)) $tags[strtolower($tag)] = $tag;
}
$tagList = $tags;
}
if($this->tagList !== $tagList) {
$this->tagList = $tagList;
parent::set('tags', implode(' ', $tagList));
$this->wire('fields')->getTags('reset');
}
return $tagList;
}
/**
* Add one or more tags
*
* @param string $tag
* @return array Returns current tag list
* @since 3.0.106
*
*/
public function addTag($tag) {
$tagList = $this->getTags();
$tagList[strtolower($tag)] = $tag;
$this->setTags($tagList, false);
return $tagList;
}
/**
* Return true if this field has the given tag or false if not
*
* @param string $tag
* @return bool
* @since 3.0.106
*
*/
public function hasTag($tag) {
$tagList = $this->getTags();
return isset($tagList[strtolower(trim(ltrim($tag, '-')))]);
}
/**
* Remove a tag
*
* @param string $tag
* @return array Returns current tag list
* @since 3.0.106
*
*/
public function removeTag($tag) {
$tagList = $this->getTags();
$tag = strtolower($tag);
if(!isset($tagList[$tag])) return $tagList;
unset($tagList[$tag]);
return $this->setTags($tagList, false);
}
/**
* debugInfo PHP 5.6+ magic method
*
@@ -1188,8 +1379,9 @@ class Field extends WireData implements Saveable, Exportable {
*
*/
public function __debugInfo() {
$info = parent::__debugInfo();
$info['settings'] = $this->settings;
$info = $this->settings;
$info['flags'] = $info['flags'] ? "$this->flagsStr ($info[flags])" : "";
$info = array_merge($info, parent::__debugInfo());
if($this->prevTable) $info['prevTable'] = $this->prevTable;
if($this->prevFieldtype) $info['prevFieldtype'] = (string) $this->prevFieldtype;
if(!empty($this->trackGets)) $info['trackGets'] = $this->trackGets;
@@ -1200,5 +1392,14 @@ class Field extends WireData implements Saveable, Exportable {
return $info;
}
public function debugInfoSmall() {
return array(
'id' => $this->id,
'name' => $this->name,
'label' => $this->getLabel(),
'type' => $this->type ? wireClassName($this->type) : '',
);
}
}

View File

@@ -18,6 +18,8 @@
*
* @property int $id Fieldgroup database ID #pw-group-retrieval
* @property string $name Fieldgroup name #pw-group-retrieval
* @property array $fields_id Array of all field IDs in this Fieldgroup
* @property null|FieldsArray $removedFields Null when there are no removed fields, or FieldsArray when there are.
*
*/
class Fieldgroup extends WireArray implements Saveable, Exportable, HasLookupItems {
@@ -133,7 +135,7 @@ class Fieldgroup extends WireArray implements Saveable, Exportable, HasLookupIte
/**
* Remove a field from this fieldgroup
*
* Note that this must be followed up with a `$field->save()` before it does anything destructive.
* Note that this must be followed up with a `$fieldgroup->save()` before it does anything destructive.
* This method does nothing more than queue the removal.
*
* _Technical Details_
@@ -177,7 +179,7 @@ class Fieldgroup extends WireArray implements Saveable, Exportable, HasLookupIte
* #pw-internal
*
* @param Field $field
* @return bool
* @return Fieldgroup|WireArray $this
*
*/
public function finishRemove(Field $field) {
@@ -194,7 +196,7 @@ class Fieldgroup extends WireArray implements Saveable, Exportable, HasLookupIte
* #pw-group-manipulation
*
* @param Field|string|int $field Field object, name or id.
* @return bool
* @return bool|Fieldgroup|WireArray
*
*/
public function softRemove($field) {
@@ -314,7 +316,7 @@ class Fieldgroup extends WireArray implements Saveable, Exportable, HasLookupIte
}
/**
* Does this fieldgroup having the given field?
* Does this fieldgroup have the given field?
*
* #pw-group-retrieval
*
@@ -335,7 +337,7 @@ class Fieldgroup extends WireArray implements Saveable, Exportable, HasLookupIte
* #pw-group-retrieval
*
* @param string|int $key Property name to retrieve, or Field name
* @return Field|string|int|null
* @return Field|string|int|null|array
*
*/
public function get($key) {
@@ -379,7 +381,7 @@ class Fieldgroup extends WireArray implements Saveable, Exportable, HasLookupIte
*
* @param string $key Name of property to set
* @param string|int|object $value Value of property
* @return Fieldgroup $this
* @return Fieldgroup|WireArray $this
* @throws WireException if passed invalid data
*
*/
@@ -448,7 +450,9 @@ class Fieldgroup extends WireArray implements Saveable, Exportable, HasLookupIte
*
*/
public function getExportData() {
return $this->wire('fieldgroups')->getExportData($this);
/** @var Fieldgroups $fieldgroups */
$fieldgroups = $this->wire('fieldgroups');
return $fieldgroups->getExportData($this);
}
/**
@@ -469,7 +473,9 @@ class Fieldgroup extends WireArray implements Saveable, Exportable, HasLookupIte
*
*/
public function setImportData(array $data) {
return $this->wire('fieldgroups')->setImportData($this, $data);
/** @var Fieldgroups $fieldgroups */
$fieldgroups = $this->wire('fieldgroups');
return $fieldgroups->setImportData($this, $data);
}
/**
@@ -520,6 +526,7 @@ class Fieldgroup extends WireArray implements Saveable, Exportable, HasLookupIte
$container = $this->wire(new InputfieldWrapper());
$containers = array();
$inFieldset = false;
$inHiddenFieldset = false;
$inModalGroup = '';
// for multiple named fields
@@ -562,6 +569,14 @@ class Fieldgroup extends WireArray implements Saveable, Exportable, HasLookupIte
continue;
}
}
if($inHiddenFieldset) {
// we are in a modal group that should be skipped since all the inputs require the modal
if($field->name == $inHiddenFieldset . "_END") {
$inHiddenFieldset = false;
} else {
continue;
}
}
if($fieldName) {
// limit to specific field name
@@ -591,6 +606,10 @@ class Fieldgroup extends WireArray implements Saveable, Exportable, HasLookupIte
// field requires modal
$inModalGroup = $field->name;
} else if($field->type instanceof FieldtypeFieldsetOpen && $field->collapsed == Inputfield::collapsedHidden) {
$inHiddenFieldset = $field->name;
continue;
} else if(!$flat && $field->type instanceof FieldtypeFieldsetOpen) {
// new fieldset in non-flat mode
if($field->type instanceof FieldtypeFieldsetClose) {
@@ -612,7 +631,7 @@ class Fieldgroup extends WireArray implements Saveable, Exportable, HasLookupIte
if(!$inputfield) continue;
if($inputfield->collapsed == Inputfield::collapsedHidden) continue;
$inputfield->value = $page->get($field->name);
if(!$page instanceof NullPage) $inputfield->setAttribute('value', $page->get($field->name));
if($multiMode) {
$fieldInputfields[$field->id] = $inputfield;
@@ -640,7 +659,9 @@ class Fieldgroup extends WireArray implements Saveable, Exportable, HasLookupIte
*
*/
public function getTemplates() {
return $this->wire('fieldgroups')->getTemplates($this);
/** @var Fieldgroups $fieldgroups */
$fieldgroups = $this->wire('fieldgroups');
return $fieldgroups->getTemplates($this);
}
/**
@@ -686,7 +707,7 @@ class Fieldgroup extends WireArray implements Saveable, Exportable, HasLookupIte
return $this->fieldContexts[$field_id][$namespace];
}
return array();
} else {
} else if(isset($this->fieldContexts[$field_id])) {
return $this->fieldContexts[$field_id];
}
}
@@ -699,8 +720,8 @@ class Fieldgroup extends WireArray implements Saveable, Exportable, HasLookupIte
* #pw-internal
*
* @param int $field_id Field ID
* @param string $namespace Optional namespace
* @param array $data
* @param string $namespace Optional namespace
*
*/
public function setFieldContextArray($field_id, $data, $namespace = '') {

View File

@@ -3,7 +3,9 @@
/**
* ProcessWire Fieldgroups
*
* Maintains collections of Fieldgroup instances.
* #pw-summary Maintains collections of Fieldgroup object instances and represents the `$fieldgroups` API variable.
* #pw-body For full details on all methods available in a Fieldgroup, be sure to also see the `WireArray` class.
* #pw-var $fieldgroups
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* https://processwire.com
@@ -19,6 +21,8 @@ class Fieldgroups extends WireSaveableItemsLookup {
/**
* Instances of FieldgroupsArray
*
* @var FieldgroupsArray
*
*/
protected $fieldgroupsArray;
@@ -60,6 +64,8 @@ class Fieldgroups extends WireSaveableItemsLookup {
/**
* Per WireSaveableItems interface, return all available Fieldgroup instances
*
* @return FieldgroupsArray
*
*/
public function getAll() {
@@ -68,6 +74,8 @@ class Fieldgroups extends WireSaveableItemsLookup {
/**
* Per WireSaveableItems interface, create a blank instance of a Fieldgroup
*
* @return Fieldgroup
*
*/
public function makeBlankItem() {
@@ -76,6 +84,8 @@ class Fieldgroups extends WireSaveableItemsLookup {
/**
* Per WireSaveableItems interface, return the name of the table that Fieldgroup instances are stored in
*
* @return string
*
*/
public function getTable() {
@@ -84,6 +94,8 @@ class Fieldgroups extends WireSaveableItemsLookup {
/**
* Per WireSaveableItemsLookup interface, return the name of the table that Fields are linked to Fieldgroups
*
* @return string
*
*/
public function getLookupTable() {
@@ -137,21 +149,12 @@ class Fieldgroups extends WireSaveableItemsLookup {
if($item->id && $item->removedFields) {
foreach($this->wire('templates') as $template) {
if($template->fieldgroup->id !== $item->id) continue;
foreach($item->removedFields as $field) {
// make sure the field is valid to delete from this template
if(($field->flags & Field::flagGlobal) && !$template->noGlobal) {
throw new WireException("Field '$field' may not be removed from fieldgroup '{$item->name}' because it is globally required (Field::flagGlobal)");
}
if($field->flags & Field::flagPermanent) {
throw new WireException("Field '$field' may not be removed from fieldgroup '{$item->name}' because it is permanent.");
}
$field->type->deleteTemplateField($template, $field);
$error = $this->isFieldNotRemoveable($field, $item, $template);
if($error !== false) throw new WireException("$error Save of fieldgroup changes aborted.");
if($field->type) $field->type->deleteTemplateField($template, $field);
$item->finishRemove($field);
}
}
@@ -196,7 +199,7 @@ class Fieldgroups extends WireSaveableItemsLookup {
* Also deletes the references in fieldgroups_fields table
*
* @param Saveable|Fieldgroup $item
* @return Fieldgroups $this
* @return bool
* @throws WireException
*
*/
@@ -208,7 +211,10 @@ class Fieldgroups extends WireSaveableItemsLookup {
}
if(count($templates)) {
throw new WireException("Can't delete fieldgroup '{$item->name}' because it is in use by template(s): " . implode(', ', $templates));
throw new WireException(
"Can't delete fieldgroup '{$item->name}' because it is in use by template(s): " .
implode(', ', $templates)
);
}
return parent::___delete($item);
@@ -262,7 +268,8 @@ class Fieldgroups extends WireSaveableItemsLookup {
$contexts = $fieldgroup->getFieldContextArray();
$numSaved = 0;
foreach($contexts as $fieldID => $context) {
$field = $fieldgroup->getFieldContext($fieldID);
$field = $fieldgroup->getFieldContext((int) $fieldID);
if(!$field) continue;
if($this->wire('fields')->saveFieldgroupContext($field, $fieldgroup)) $numSaved++;
}
return $numSaved;
@@ -444,5 +451,40 @@ class Fieldgroups extends WireSaveableItemsLookup {
return $return;
}
/**
* Is the given Field not allowed to be removed from given Template?
*
* #pw-internal
*
* @param Field $field
* @param Template $template
* @param Fieldgroup $fieldgroup
* @return bool|string Returns error message string if not removeable or boolean false if it is removeable
*
*/
public function isFieldNotRemoveable(Field $field, Fieldgroup $fieldgroup, Template $template = null) {
if(is_null($template)) $template = $this->wire('templates')->get($fieldgroup->name);
if(($field->flags & Field::flagGlobal) && (!$template || !$template->noGlobal)) {
if($template && $template->getConnectedField()) {
// if template has a 1-1 relationship with a field, noGlobal is not enforced
return false;
} else {
return
"Field '$field' may not be removed from fieldgroup '$fieldgroup->name' " .
"because it is globally required (Field::flagGlobal).";
}
}
if($field->flags & Field::flagPermanent) {
return
"Field '$field' may not be removed from fieldgroup '$fieldgroup->name' " .
"because it is permanent (Field::flagPermanent).";
}
return false;
}
}

View File

@@ -5,10 +5,19 @@
*
* Manages collection of ALL Field instances, not specific to any particular Fieldgroup
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2018 by Ryan Cramer
* https://processwire.com
*
* #pw-summary Manages all custom fields in ProcessWire
* #pw-summary Manages all custom fields in ProcessWire, independently of any Fieldgroup.
* #pw-var $fields
* #pw-body =
* Each field returned is an object of type `Field`. The $fields API variable is iterable:
* ~~~~~
* foreach($fields as $field) {
* echo "<p>Name: $field->name, Type: $field->type, Label: $field->label</p>";
* }
* ~~~~~
* #pw-body
*
* @method Field|null get($key) Get a field by name or id
* @method bool changeFieldtype(Field $field1, $keepSettings = false)
@@ -16,6 +25,7 @@
* @method bool deleteFieldDataByTemplate(Field $field, Template $template) #pw-hooker
* @method void changedType(Saveable $item, Fieldtype $fromType, Fieldtype $toType) #pw-hooker
* @method void changeTypeReady(Saveable $item, Fieldtype $fromType, Fieldtype $toType) #pw-hooker
* @method bool|Field clone(Field $item, $name = '') Clone a field and return it or return false on fail.
*
*/
@@ -34,60 +44,94 @@ class Fields extends WireSaveableItems {
*
*/
static protected $nativeNamesSystem = array(
'id',
'parent_id',
'parent', // alias
'parents',
'templates_id',
'template', // alias
'name',
'status',
'child',
'children',
'count',
'check_access',
'created_users_id',
'created',
'createdUser',
'createdUser',
'createdUserID',
'createdUsersID',
'created_users_id',
'data',
'description',
'editUrl',
'end',
'fieldgroup',
'fields',
'find',
'flags',
'get',
'has_parent',
'hasParent',
'httpUrl',
'id',
'include',
'modified',
'modifiedUser',
'isNew',
'limit',
'modified_users_id',
'modified',
'modifiedUser',
'modifiedUserID',
'modifiedUsersID',
'modified_users_id',
'published',
'name',
'num_children',
'numChildren',
'sort',
'sortfield',
'flags',
'find',
'get',
'child',
'children',
'siblings',
//'roles',
'url',
'path',
'templatePrevious',
'rootParent',
'fieldgroup',
'fields',
'description',
'data',
'isNew',
);
'numChildren',
'parent_id',
'parent',
'parents',
'path',
'published',
'rootParent',
'siblings',
'sort',
'sortfield',
'start',
'status',
'template',
'templatePrevious',
'templates_id',
'url',
'_custom',
);
protected $flagNames = array();
/**
* Field names that are native/permanent to this instance of ProcessWire (configurable at runtime)
*
* Array indexes are the names and values are all boolean true.
*
*/
protected $nativeNamesLocal = array();
/**
* Cache of all tags for all fields, populated to array when asked for the first time
*
* @var array|null
*
*/
protected $tagList = null;
/**
* Construct
*
*/
public function __construct() {
$this->fieldsArray = new FieldsArray();
$this->flagNames = array(
Field::flagAutojoin => 'autojoin',
Field::flagGlobal => 'global',
Field::flagSystem => 'system',
Field::flagPermanent => 'permanent',
Field::flagAccess => 'access',
Field::flagAccessAPI => 'access-api',
Field::flagAccessEditor => 'access-editor',
Field::flagFieldgroupContext => 'fieldgroup-context',
Field::flagSystemOverride => 'system-override',
);
// convert so that keys are names so that isset() can be used rather than in_array()
if(isset(self::$nativeNamesSystem[0])) self::$nativeNamesSystem = array_flip(self::$nativeNamesSystem);
}
/**
@@ -111,6 +155,45 @@ class Fields extends WireSaveableItems {
return $this->wire(new Field());
}
/**
* Make an item and populate with given data
*
* @param array $a Associative array of data to populate
* @return Saveable|Wire
* @throws WireException
* @since 3.0.146
*
*/
public function makeItem(array $a = array()) {
if(empty($a['type'])) return parent::makeItem($a);
/** @var Fieldtypes $fieldtypes */
$fieldtypes = $this->wire('fieldtypes');
if(!$fieldtypes) return parent::makeItem($a);
/** @var Fieldtype $fieldtype */
$fieldtype = $fieldtypes->get($a['type']);
if(!$fieldtype) return parent::makeItem($a);
$class = $fieldtype->getFieldClass($a);
if(empty($class) || $class === 'Field') return parent::makeItem($a);
if(strpos($class, "\\") === false) $class = wireClassName($class, true);
if(!class_exists($class)) return parent::makeItem($a);
$field = new $class();
$this->wire($field);
foreach($a as $key => $value) {
$field->$key = $value;
}
$field->resetTrackChanges(true);
return $field;
}
/**
* Per WireSaveableItems interface, return all available Field instances
*
@@ -205,6 +288,8 @@ class Fields extends WireSaveableItems {
}
}
}
$this->getTags('reset');
return true;
}
@@ -281,11 +366,10 @@ class Fields extends WireSaveableItems {
/**
* Create and return a cloned copy of the given Field
*
*
* @param Field|Saveable $item Field to clone
* @param string $name Optionally specify name for new cloned item
* @return bool|Saveable $item Returns the new clone on success, or false on failure
* @throws WireException
*
*/
public function ___clone(Saveable $item, $name = '') {
@@ -622,14 +706,14 @@ class Fields extends WireSaveableItems {
if($success) {
$this->message(
sprintf($this->_('Deleted field "%1$s" data in %2$d row(s) from %3$d page(s).'),
$field->name, $numRows, $numPages) . " [$deleteType]",
sprintf($this->_('Deleted field "%1$s" data in %2$d row(s) from %3$d page(s) using template "%4$s".'),
$field->name, $numRows, $numPages, $template->name) . " [$deleteType]",
Notice::log
);
} else {
$this->error(
sprintf($this->_('Error deleting field "%1$s" data, %2$d row(s), %3$d page(s).'),
$field->name, $numRows, $numPages) . " [$deleteType]",
sprintf($this->_('Error deleting field "%1$s" data, %2$d row(s), %3$d page(s) using template "%4$s".'),
$field->name, $numRows, $numPages, $template->name) . " [$deleteType]",
Notice::log
);
}
@@ -676,6 +760,8 @@ class Fields extends WireSaveableItems {
'countPages' => false,
'getPageIDs' => false,
);
if(!$field->type) return 0;
$options = array_merge($defaults, $options);
$database = $this->wire('database');
@@ -802,8 +888,8 @@ class Fields extends WireSaveableItems {
*
*/
public function isNative($name) {
if(in_array($name, self::$nativeNamesSystem)) return true;
if(in_array($name, $this->nativeNamesLocal)) return true;
if(isset(self::$nativeNamesSystem[$name])) return true;
if(isset($this->nativeNamesLocal[$name])) return true;
return false;
}
@@ -816,12 +902,106 @@ class Fields extends WireSaveableItems {
*
*/
public function setNative($name) {
$this->nativeNamesLocal[] = $name;
$this->nativeNamesLocal[$name] = true;
}
/**
* Get list of all tags used by fields
*
* - By default it returns an array of tag names where both keys and values are the tag names.
* - If you specify true for the `$getFields` argument, it returns an array where the keys are
* tag names and the values are arrays of field names in the tag.
* - If you specify "reset" for the `$getFields` argument it returns a blank array and resets
* internal tags cache.
*
* @param bool|string $getFieldNames Specify true to return associative array where keys are tags and values are field names
* …or specify the string "reset" to force getTags() to reset its cache, forcing it to reload on the next call.
* @return array
* @since 3.0.106
*
*/
public function getTags($getFieldNames = false) {
if($getFieldNames === 'reset') {
$this->tagList = null;
return array();
}
if($this->tagList === null) {
$tagList = array();
foreach($this as $field) {
/** @var Field $field */
$fieldTags = $field->getTags();
foreach($fieldTags as $tag) {
if(!isset($tagList[$tag])) $tagList[$tag] = array();
$tagList[$tag][] = $field->name;
}
}
ksort($tagList);
$this->tagList = $tagList;
}
if($getFieldNames) return $this->tagList;
$tagList = array();
foreach($this->tagList as $tag => $fieldNames) {
$tagList[$tag] = $tag;
}
return $tagList;
}
/**
* Return all fields that have the given $tag
*
* Returns an associative array of `['field_name' => 'field_name']` if `$getFieldNames` argument is true,
* or `['field_name => Field instance]` if not (which is the default).
*
* @param string $tag Tag to find fields for
* @param bool $getFieldNames If true, returns array of field names rather than Field objects (default=false).
* @return array Array of Field objects, or array of field names if requested. Array keys are always field names.
* @since 3.0.106
*
*/
public function findByTag($tag, $getFieldNames = false) {
$tags = $this->getTags(true);
$items = array();
if(!isset($tags[$tag])) return $items;
foreach($tags[$tag] as $fieldName) {
$items[$fieldName] = ($getFieldNames ? $fieldName : $this->get($fieldName));
}
ksort($items);
return $items;
}
/**
* Get all flag names or get all flag names for given flags or Field
*
* #pw-internal
*
* @param int|Field|null $flags Specify flags or Field or omit to get all flag names
* @param bool $getString Get a string of flag names rather than array? (default=false)
* @return array|string When array is returned, array is of strings indexed by flag value (int)
*
*/
public function getFlagNames($flags = null, $getString = false) {
if($flags === null) {
$a = $this->flagNames;
} else {
$a = array();
if($flags instanceof Field) $flags = $flags->flags;
foreach($this->flagNames as $flag => $name) {
if($flags & $flag) $a[$flag] = $name;
}
}
return $getString ? implode(' ', $a) : $a;
}
/**
* Overridden from WireSaveableItems to retain keys with 0 values and remove defaults we don't need saved
*
* #pw-internal
*
* @param array $value
* @return string of JSON
*
@@ -906,5 +1086,32 @@ class Fields extends WireSaveableItems {
*/
public function ___changeTypeReady(Saveable $item, Fieldtype $fromType, Fieldtype $toType) { }
/**
* Get Fieldtypes compatible (for type change) with given Field
*
* #pw-internal
*
* @param Field $field
* @return array Array of Fieldtype objects indexed by class name
* @since 3.0.140
*
*/
public function getCompatibleFieldtypes(Field $field) {
$fieldtype = $field->type;
if($fieldtype) {
// ask fieldtype what is compatible
$fieldtypes = $fieldtype->getCompatibleFieldtypes($field);
if(!$fieldtypes || !$fieldtypes instanceof WireArray) {
$fieldtypes = $this->wire(new Fieldtypes());
}
// ensure original is present
$fieldtypes->prepend($fieldtype);
} else {
// allow all
$fieldtypes = $this->wire('fieldtypes');
}
return $fieldtypes;
}
}

View File

@@ -33,6 +33,7 @@
* @method mixed wakeupValue(Page $page, Field $field, $value)
* @method string|int|array sleepValue(Page $page, Field $field, $value)
* @method string|float|int|array exportValue(Page $page, Field $field, $value, array $options = array())
* @method string|float|int|array|object importValue(Page $page, Field $field, $value, array $options = array())
* @method bool createField(Field $field)
* @method array getSelectorInfo(Field $field, array $data = array())
* @method mixed|null loadPageField(Page $page, Field $field)
@@ -51,6 +52,7 @@
* @property bool $_exportMode True when Fieldtype is exporting config data, false otherwise. #pw-internal
* @property string $name Name of Fieldtype module. #pw-group-other
* @property string $shortName Short name of Fieldtype, which excludes the "Fieldtype" prefix. #pw-group-other
* @property string $longName Long name of Fieldtype, which is typically the module title. #pw-group-other
*
*/
abstract class Fieldtype extends WireData implements Module {
@@ -75,6 +77,14 @@ abstract class Fieldtype extends WireData implements Module {
*/
protected $loadPageFieldFilters = null;
/**
* Field that last referenced this Fieldtype from $field->type
*
* @var Field|null
*
*/
protected $lastAccessField = null;
/**
* Construct
*
@@ -89,6 +99,30 @@ abstract class Fieldtype extends WireData implements Module {
*/
public function init() { }
/**
* Set last access field
*
* #pw-internal
*
* @param Field $field
*
*/
public function setLastAccessField(Field $field) {
$this->lastAccessField = $field;
}
/**
* Return field that last accessed this Fieldtype via $field->type
*
* #pw-internal
*
* @return null|Field
*
*/
public function getLastAccessField() {
return $this->lastAccessField;
}
/**
* Fieldtype modules are singular, in that only one instance is needed per request
*
@@ -396,13 +430,11 @@ abstract class Fieldtype extends WireData implements Module {
* need to have some text formatting applied to them, like Markdown, SmartyPants, Textile, etc. As a result,
* Fieldtype modules don't need to implement this unless it's applicable.
*
* Fieldtype modules that implement this do not need to call this parent method, as it doesn't do anything.
*
* #pw-group-formatting
*
* @param Page $page
* @param Field $field
* @param string|int|object $value
* @param Page $page Page that the value lives on
* @param Field $field Field that represents the value
* @param string|int|object $value The value to format
* @return mixed
*
*/
@@ -522,6 +554,7 @@ abstract class Fieldtype extends WireData implements Module {
* @param Field $field
* @param string|int|float|array|object $value
* @return string|int|float|array
* @see Fieldtype::wakeupValue()
*
*/
public function ___sleepValue(Page $page, Field $field, $value) {
@@ -532,24 +565,69 @@ abstract class Fieldtype extends WireData implements Module {
/**
* Given a value originally generated by exportValue() convert it to a live/runtime value.
*
* This is intended for importing from PW-driven web services.
* This is intended for importing from PW-driven web services. If not overridden, it does
* the same thing as the `Fieldtype::wakeupValue()` method.
*
* #pw-internal
*
* @param Page $page
* @param Field $field
* @param string|int|array $value
* @param string|int|float|array|null $value
* @param array $options Additional options if needed/applicable
* @return string|int|array|object $value
* @see Fieldtype::exportValue()
*
public function ___importValue(Page $page, Field $field, $value) {
*/
public function ___importValue(Page $page, Field $field, $value, array $options = array()) {
if($options) {}
$value = $this->wakeupValue($page, $field, $value);
return $value;
}
/**
* Get associative array of options and info (name => value) that Fieldtype supports for importValue
*
* Current recognized options include the following:
*
* - `importable` (bool): Is the field importable (and exportable)? (default=auto-detect)
*
* - `test` (bool): Indicates Fieldtype supports testing import before committing & populates notices to
* returned Wire object. (default=false)
*
* - `returnsPageValue` (bool): True if it returns the value that should set back to Page? False if return
* value should not be set to Page. When false, it indicates the Fieldtype::importValue() handles the
* actual commit to DB of import data. (default=true)
*
* - `requiresExportValue` (bool): Indicates Fieldtype::importValue() requires an 'exportValue' of the
* current value from Page in $options. (default=false)
*
* - `restoreOnException` (bool): Restore previous value if Exception thrown during import (default=false).
*
* #pw-internal
*
* @param Field $field
* @return array
*
*/
public function getImportValueOptions(Field $field) {
$schema = $this->getDatabaseSchema($field);
$options = array(
'importable' => (!isset($schema['xtra']['all']) || $schema['xtra']['all'] !== true) ? false : true,
'test' => false,
'returnsPageValue' => true,
'requiresExportValue' => false,
'restoreOnException' => false,
);
return $options;
}
/**
* Given a value, return an portable version of it as either a string, int, float or array
*
* If an array is returned, it should only contain: strings, ints, floats or more arrays of those types.
* This is intended for web service exports.
*
* When applicable, this method should map things like internal IDs to named equivalents (name, path, etc.).
*
* If not overridden, this takes on the same behavior as `Fieldtype::sleepValue()`. However, if overridden,
* it is intended to be more verbose than wakeupValue, where applicable.
@@ -558,8 +636,10 @@ abstract class Fieldtype extends WireData implements Module {
*
* @param Page $page
* @param Field $field
* @param string|int|array|object $value
* @param array $options Optional settings to shape the exported value, if needed.
* @param string|int|float|array|object|null $value
* @param array $options Optional settings to shape the exported value, if needed:
* - `system` (boolean): Indicates value is being used for a system export via $pages->export() call (default=false).
* - `human` (boolean): When true, Fieldtype may optionally emphasize human readability over importability (default=false).
* @return string|float|int|array
*
*/
@@ -733,6 +813,21 @@ abstract class Fieldtype extends WireData implements Module {
return $schema;
}
/**
* Get class name to use Field objects of this type (must be class that extends Field class)
*
* Return blank if default class (Field) should be used.
*
* @param array $a Field data from DB (if needed)
* @return string Return class name or blank to use default Field class
* @since 3.0.146
*
*/
public function getFieldClass(array $a = array()) {
if($a) {} // ignore
return '';
}
/**
* Returns verbose array of database schema information
*
@@ -1259,7 +1354,12 @@ abstract class Fieldtype extends WireData implements Module {
*/
public function get($key) {
if($key == 'name') return $this->className();
if($key == 'shortName') return str_replace('Fieldtype', '', $this->className());
if($key == 'shortName') {
return str_replace('Fieldtype', '', $this->className());
} else if($key == 'longName' && method_exists($this, 'getModuleInfo')) {
$info = $this->getModuleInfo($this);
return $info['title'];
}
return parent::get($key);
}

View File

@@ -208,8 +208,10 @@ abstract class FieldtypeMulti extends Fieldtype {
public function ___savePageField(Page $page, Field $field) {
if(!$page->id || !$field->id) return false;
/** @var WireDatabasePDO $database */
$database = $this->wire('database');
$useTransaction = $database->allowTransaction();
$values = $page->get($field->name);
$schema = array();
@@ -227,11 +229,19 @@ abstract class FieldtypeMulti extends Fieldtype {
$values = $this->sleepValue($page, $field, $values);
$table = $database->escapeTable($field->table);
$page_id = (int) $page->id;
// use transaction when possible
if($useTransaction) $database->beginTransaction();
// since we don't manage IDs of existing values for multi fields, we delete the existing data and insert all of it again
$query = $database->prepare("DELETE FROM `$table` WHERE pages_id=:page_id"); // QA
$query->bindValue(":page_id", $page_id, \PDO::PARAM_INT);
$query->execute();
try {
// since we don't manage IDs of existing values for multi fields, we delete the existing data and insert all of it again
$query = $database->prepare("DELETE FROM `$table` WHERE pages_id=:page_id"); // QA
$query->bindValue(":page_id", $page_id, \PDO::PARAM_INT);
$query->execute();
} catch(\Exception $e) {
if($useTransaction) $database->rollBack();
throw $e;
}
if(count($values)) {
@@ -284,21 +294,24 @@ abstract class FieldtypeMulti extends Fieldtype {
$sort++;
}
$sql = rtrim($sql, ", ");
$query = $database->prepare($sql);
try {
$query = $database->prepare(rtrim($sql, ", "));
$result = $query->execute();
} catch(\Exception $e) {
if($useTransaction) $database->rollBack();
if($this->wire('config')->allowExceptions) throw $e; // throw original
$msg = $e->getMessage();
if($this->wire('config')->debug && $this->wire('config')->advanced) $msg .= "\n$sql";
throw new WireException($msg); // throw WireException
}
return $result;
} else {
$result = true;
}
return true;
if($useTransaction) $database->commit();
return $result;
}
/**
@@ -798,11 +811,16 @@ abstract class FieldtypeMulti extends Fieldtype {
}
// only allow matches using templates with the requested field
$sql = 'pages.templates_id IN(';
foreach($field->getTemplates() as $template) {
$sql .= ((int) $template->id) . ',';
$templates = $field->getTemplates();
if(count($templates)) {
$sql = 'pages.templates_id IN(';
foreach($templates as $template) {
$sql .= ((int) $template->id) . ',';
}
$sql = rtrim($sql, ',') . ')';
} else {
$sql = 'pages.templates_id=0';
}
$sql = rtrim($sql, ',') . ')';
$query->where($sql); // QA
} else {

View File

@@ -10,6 +10,7 @@
*
*
*/
class Fieldtypes extends WireArray {
/**
@@ -19,12 +20,21 @@ class Fieldtypes extends WireArray {
protected $preloaded = false;
/**
* Construct this Fieldtypes object and load all Fieldtype modules
* Is this the $fieldtypes API var?
*
* @var bool
*
*/
protected $isAPI = false;
/**
* Construct the $fieldtypes API var (load all Fieldtype modules into it)
*
*/
public function init() {
foreach($this->wire('modules') as $module) {
if(strpos($module->className(), 'Fieldtype') === 0) {
$this->isAPI = true;
foreach($this->wire('modules') as $name => $module) {
if(strpos($name, 'Fieldtype') === 0) {
// if($module instanceof ModulePlaceholder) $module = $this->wire('modules')->get($module->className());
$this->add($module);
}
@@ -37,12 +47,13 @@ class Fieldtypes extends WireArray {
*/
protected function preload() {
if($this->preloaded) return;
$debug = $this->wire('config')->debug;
$debug = $this->isAPI && $this->wire('config')->debug;
if($debug) Debug::timer('Fieldtypes.preload');
foreach($this->data as $key => $module) {
$modules = $this->wire('modules'); /** @var Modules $modules */
foreach($this->data as $moduleName => $module) {
if($module instanceof ModulePlaceholder) {
$fieldtype = $this->wire('modules')->get($module->className());
$this->data[$key] = $fieldtype;
$fieldtype = $modules->getModule($moduleName);
$this->data[$moduleName] = $fieldtype;
}
}
if($debug) Debug::saveTimer('Fieldtypes.preload');
@@ -57,11 +68,13 @@ class Fieldtypes extends WireArray {
*
*/
public function isValidItem($item) {
return $item instanceof Fieldtype || $item instanceof ModulePlaceholder;
if($item instanceof Fieldtype) return true;
if($item instanceof ModulePlaceholder && strpos($item->className(), 'Fieldtype') === 0) return true;
return false;
}
/**
* Per the WireArray interface, keys must be strings (field names)
* Per the WireArray interface, keys must be strings (fieldtype class names)
*
* @param string|int $key
* @return bool
@@ -118,12 +131,13 @@ class Fieldtypes extends WireArray {
if(strpos($key, 'Fieldtype') !== 0) $key = "Fieldtype" . ucfirst($key);
if(!$fieldtype = parent::get($key)) {
$fieldtype = $this->wire('modules')->get($key);
$fieldtype = $this->wire('modules')->getModule($key);
if($fieldtype) $this->set($key, $fieldtype);
}
if($fieldtype instanceof ModulePlaceholder) {
$fieldtype = $this->wire('modules')->get($fieldtype->className());
$this->set($key, $fieldtype);
$fieldtype = $this->wire('modules')->getModule($fieldtype->className());
if($fieldtype) $this->set($key, $fieldtype);
}
return $fieldtype;
@@ -146,7 +160,7 @@ class Fieldtypes extends WireArray {
public function eq($num) { $this->preload(); return parent::eq($num); }
public function first() { $this->preload(); return parent::first(); }
public function last() { $this->preload(); return parent::last(); }
public function sort($properties) { $this->preload(); return parent::sort($properties); }
public function sort($properties, $flags = null) { $this->preload(); return parent::sort($properties, $flags); }
protected function filterData($selectors, $not = false) { $this->preload(); return parent::filterData($selectors, $not); }
public function makeCopy() { $this->preload(); return parent::makeCopy(); }
public function makeNew() { $this->preload(); return parent::makeNew(); }

View File

@@ -99,6 +99,22 @@ class FileCompiler extends Wire {
*/
protected $ns = '';
/**
* String with raw PHP blocks only, and with any quoted values removed.
*
* @var string
*
*/
protected $rawPHP = '';
/**
* Same as raw PHP but with all quoted values converted to literal "string"
*
* @var string
*
*/
protected $rawDequotedPHP = '';
/**
* Construct
*
@@ -214,6 +230,52 @@ class FileCompiler extends Wire {
}
}
/**
* Populate the $this->rawPHP data which contains only raw php without quoted values
*
* @param string $data
*
*/
protected function initRawPHP(&$data) {
$this->rawPHP = '';
$this->rawDequotedPHP = '';
$phpOpen = '<' . '?';
$phpClose = '?' . '>';
$phpBlocks = explode($phpOpen, $data);
foreach($phpBlocks as $key => $phpBlock) {
$pos = strpos($phpBlock, $phpClose);
if($pos !== false) {
$closeBlock = substr($phpBlock, strlen($phpClose) + 2);
if(strrpos($closeBlock, '{') && strrpos($closeBlock, '}') && strrpos($closeBlock, '=')
&& strrpos($closeBlock, '(') && strrpos($closeBlock, ')')
&& preg_match('/\sif\s*\(/', $closeBlock)
&& preg_match('/\$[_a-zA-Z][_a-zA-Z0-9]+/', $closeBlock)) {
// closeBlock still looks a lot like PHP, leave $phpBlock as-is
// happens when for example a phpClose is within a PHP string
} else {
$phpBlock = substr($phpBlock, 0, $pos);
}
}
$this->rawPHP .= $phpOpen . $phpBlock . $phpClose . "\n";
}
// remove docblocks/comments
// $this->rawPHP = preg_replace('!/\*.+?\*/!s', '', $this->rawPHP);
// remove escaped quotes
$this->rawDequotedPHP = str_replace(array('\\"', "\\'"), '', $this->rawPHP);
// remove double quoted blocks
$this->rawDequotedPHP = preg_replace('/([\s(.=,])"[^"]*"/s', '$1"string"', $this->rawDequotedPHP);
// remove single quoted blocks
$this->rawDequotedPHP = preg_replace('/([\s(.=,])\'[^\']*\'/s', '$1\'string\'', $this->rawDequotedPHP);
}
/**
* Allow the given filename to be compiled?
*
@@ -326,7 +388,7 @@ class FileCompiler extends Wire {
$targetData = $this->compileData($targetData, $sourcePathname);
if(false !== file_put_contents($targetPathname, $targetData, LOCK_EX)) {
$this->chmod($targetPathname);
touch($targetPathname, filemtime($sourcePathname));
$this->touch($targetPathname, filemtime($sourcePathname));
$targetHash = md5_file($targetPathname);
$cacheData = array(
'source' => array(
@@ -384,9 +446,13 @@ class FileCompiler extends Wire {
// file already declares a namespace and options indicate we shouldn't compile
return $data;
}
$this->initRawPHP($data);
if($this->options['includes']) {
$dataHash = md5($data);
$this->compileIncludes($data, $sourceFile);
if(md5($data) != $dataHash) $this->initRawPHP($data);
}
if($this->options['namespace']) {
@@ -424,11 +490,16 @@ class FileCompiler extends Wire {
}
if(!strlen(__NAMESPACE__)) {
if(strpos($data, "ProcessWire\\")) {
if(strpos($this->rawPHP, "ProcessWire\\")) {
$data = str_replace(array("\\ProcessWire\\", "ProcessWire\\"), "\\", $data);
}
}
if(stripos($data, "FileCompiler=?") !== false) {
// Allow for a token that gets replaced so a file can detect if it's compiled
$data = str_replace("FileCompiler=?", "FileCompiler=Yes", $data);
}
return $data;
}
@@ -507,11 +578,14 @@ class FileCompiler extends Wire {
protected function compileIncludes(&$data, $sourceFile) {
// other related to includes
if(strpos($data, '__DIR__') !== false) {
$rawPHP = $this->rawPHP;
if(strpos($rawPHP, '__DIR__') !== false) {
$data = str_replace('__DIR__', "'" . dirname($sourceFile) . "'", $data);
$rawPHP = str_replace('__DIR__', "'" . dirname($sourceFile) . "'", $rawPHP);
}
if(strpos($data, '__FILE__') !== false) {
if(strpos($rawPHP, '__FILE__') !== false) {
$data = str_replace('__FILE__', "'" . $sourceFile . "'", $data);
$rawPHP = str_replace('__FILE__', "'" . $sourceFile . "'", $rawPHP);
}
$optionsStr = $this->optionsToString($this->options);
@@ -535,10 +609,15 @@ class FileCompiler extends Wire {
'([;\r\n])' . // 5:close, whatever the last character is on the line
'/im';
if(!preg_match_all($re, $data, $matches)) return;
if(!preg_match_all($re, $rawPHP, $matches)) return;
foreach($matches[0] as $key => $fullMatch) {
// if the include statement looks like one of these below then skip compilation for included file
// include(/*NoCompile*/__DIR__ . '/file.php');
// include(__DIR__ . '/file.php'/*NoCompile*/);
if(strpos($fullMatch, 'NoCompile') !== false) continue;
$open = $matches[1][$key];
$funcMatch = $matches[2][$key];
$argOpen = trim($matches[3][$key]);
@@ -550,45 +629,10 @@ class FileCompiler extends Wire {
// only include, include_once, require, require_once can be used without opening parenthesis
continue;
}
if(strpos($fileMatch, '$') === 0) {
// fileMatch stars with a var name
} else if(strpos($fileMatch, '"') !== strrpos($fileMatch, '"')) {
// fileMatch has both open and close double quotes
} else if(strpos($fileMatch, "'") !== strrpos($fileMatch, "'")) {
// fileMatch has both open and close single quotes
} else if(strpos($fileMatch, '(') !== false && strpos($fileMatch, ')') !== false) {
// likely a function call
} else {
// likely NOT a valid file match, as it doesn't have any of the expected characters
continue;
}
if(strlen($open)) {
$skipMatch = false;
$test = $open;
foreach(array('"', "'") as $quote) {
// skip when words like "require" are in a string
if(strpos($test, $quote) === false) continue;
$test = str_replace('\\' . $quote, '', $test); // ignore quotes that are escaped
if(strpos($test, $quote) === false) continue;
if(substr_count($test, $quote) % 2 > 0) {
// there are an uneven number of quotes, indicating that
// our $funcMatch is likely part of a quoted string
$skipMatch = true;
break;
}
if($quote == '"' && strpos($test, "'") !== false) {
// remove quoted apostrophes so they don't confuse the next iteration
$test = preg_replace('/"[^"\']*\'[^"]*"/', '', $test);
}
}
if($skipMatch) continue;
if(preg_match('/^[$_a-zA-Z0-9]+$/', substr($open, -1))) {
// skip things like: something_include(... and $include
continue;
}
}
$fileMatchType = $this->compileIncludesFileMatchType($fileMatch, $funcMatch);
if(!$fileMatchType) continue;
if(!$this->compileIncludesValidLineOpen($open)) continue;
if(strpos($fileMatch, '?' . '>')) {
// move closing PHP tag out of the fileMatch and into the close
@@ -641,14 +685,6 @@ class FileCompiler extends Wire {
// replace absolute root path references with runtime generated versions
$rootPath = $this->wire('config')->paths->root;
if(strpos($data, $rootPath)) {
/*
$data = preg_replace('%([\'"])' . preg_quote($rootPath) . '([^\'"\s\r\n]*[\'"])%',
'(isset($this) && $this instanceof \\ProcessWire\\Wire ? ' .
'$this->wire("config")->paths->root : ' .
'\\ProcessWire\\wire("config")->paths->root' .
') . $1$2',
$data);
*/
$ns = __NAMESPACE__ ? "\\ProcessWire" : "";
$data = preg_replace('%([\'"])' . preg_quote($rootPath) . '([^\'"\s\r\n]*[\'"])%',
$ns . '\\wire("config")->paths->root . $1$2',
@@ -657,6 +693,98 @@ class FileCompiler extends Wire {
}
/**
* Test the given line $open preceding an include statement for validity
*
* @param string $open
* @return bool Returns true if valid, false if not
*
*/
protected function compileIncludesValidLineOpen($open) {
if(!strlen($open)) return true;
$skipMatch = false;
$test = $open;
foreach(array('"', "'") as $quote) {
// skip when words like "require" are in a string
if(strpos($test, $quote) === false) continue;
$test = str_replace('\\' . $quote, '', $test); // ignore quotes that are escaped
if(strpos($test, $quote) === false) continue;
if(substr_count($test, $quote) % 2 > 0) {
// there are an uneven number of quotes, indicating that
// our $funcMatch is likely part of a quoted string
$skipMatch = true;
break;
}
if($quote == '"' && strpos($test, "'") !== false) {
// remove quoted apostrophes so they don't confuse the next iteration
$test = preg_replace('/"[^"\']*\'[^"]*"/', '', $test);
}
}
if(!$skipMatch && preg_match('/^[$_a-zA-Z0-9]+$/', substr($open, -1))) {
// skip things like: something_include(... and $include
$skipMatch = true;
}
return $skipMatch ? false : true;
}
/**
* Returns fileMatch type of 'var', 'file', 'func' or boolean false if not valid
*
* @param string $fileMatch The $fileMatch var from compileIncludes() method
* @param string $funcMatch include function name
* @return string|bool
*
*/
protected function compileIncludesFileMatchType($fileMatch, $funcMatch) {
$fileMatch = trim($fileMatch);
$isValid = false;
$phpVarSign = strpos($fileMatch, '$');
$doubleQuote1 = strpos($fileMatch, '"');
$doubleQuote2 = strrpos($fileMatch, '"');
$singleQuote1 = strpos($fileMatch, "'");
$singleQuote2 = strrpos($fileMatch, "'");
$parenthesis1 = strpos($fileMatch, '(');
$parenthesis2 = strrpos($fileMatch, ')');
$testFile = '';
if($phpVarSign === 0) {
// fileMatch starts with a var name, make sure it at least starts in PHP var format
if(preg_match('/^\$[_a-zA-Z]/', $fileMatch)) $isValid = 'var';
} else if($doubleQuote1 !== false && $doubleQuote2 > $doubleQuote1) {
// fileMatch has both open and close double quotes with possibly a filename, so validate extension
$testFile = substr($fileMatch, $doubleQuote1 + 1, $doubleQuote2 - $doubleQuote1 - 1);
} else if($singleQuote1 !== false && $singleQuote2 > $singleQuote1) {
// fileMatch has both open and close single quotes with possibly a filename, so validate extension
$testFile = substr($fileMatch, $singleQuote1 + 1, $singleQuote2 - $singleQuote1 - 1);
} else if($parenthesis1 > 0 && $parenthesis2 > $parenthesis1) {
// likely a function call, make sure open parenthesis is preceded by PHP name format
if(preg_match('/[_a-zA-Z][_a-zA-Z0-9]+\(/', $fileMatch)) $isValid = 'func';
} else {
// likely NOT a valid file match, as it doesn't have any of the expected characters
$isValid = false;
}
if($testFile) {
if(strrpos($testFile, '.')) {
// test contains a filename that needs extension validated
$parts = explode('.', $testFile);
$testExt = array_pop($parts);
if($testExt && in_array(strtolower($testExt), $this->extensions)) $isValid = 'file';
} else if($funcMatch == 'wireRenderFile' || $funcMatch == 'wireIncludeFile') {
// these methods don't require a file extension
$isValid = 'file';
}
}
return $isValid;
}
/**
* Compile global class/interface/function references to namespaced versions
*
@@ -677,7 +805,6 @@ class FileCompiler extends Wire {
}
}
*/
$classes = get_declared_classes();
$classes = array_merge($classes, get_declared_interfaces());
@@ -703,6 +830,9 @@ class FileCompiler extends Wire {
$classes = array_merge($classes, $files);
if(!__NAMESPACE__) $classes = array_merge($classes, array_keys($this->wire('modules')->getInstallable()));
$rawPHP = $this->rawPHP;
$rawDequotedPHP = $this->rawDequotedPHP;
// update classes and interfaces
foreach($classes as $class) {
@@ -714,7 +844,7 @@ class FileCompiler extends Wire {
$ns = '';
}
if($ns) {}
if(stripos($data, $class) === false) continue; // quick exit if class name not referenced in data
if(stripos($rawDequotedPHP, $class) === false) continue; // quick exit if class name not referenced in data
$patterns = array(
// 1=open 2=close
@@ -727,26 +857,28 @@ class FileCompiler extends Wire {
"instanceof" => '(\sinstanceof\s+)' . $class . '([^_a-zA-Z0-9]|$)', // 'instanceof Page'
"$class " => '(\(\s*|,\s*)' . $class . '(\s+\$)', // type hinted '(Page $something' or '($foo, Page $something'
);
foreach($patterns as $check => $regex) {
if(stripos($data, $check) === false) continue;
if(!preg_match_all('/' . $regex . '/im', $data, $matches)) continue;
//echo "<pre>" . print_r($matches, true) . "</pre>";
if(stripos($rawDequotedPHP, $check) === false) continue;
if(!preg_match_all('/' . $regex . '/im', $rawDequotedPHP, $matches)) continue;
foreach($matches[0] as $key => $fullMatch) {
$open = $matches[1][$key];
$close = $matches[2][$key];
if(substr($open, -1) == '\\') continue; // if last character in open is '\' then skip the replacement
$className = __NAMESPACE__ ? '\\' . __NAMESPACE__ . '\\' . $class : '\\' . $class;
$data = str_replace($fullMatch, $open . $className . $close, $data);
$repl = $open . $className . $close;
$data = str_replace($fullMatch, $repl, $data);
$rawPHP = str_replace($fullMatch, $repl, $rawPHP);
$rawDequotedPHP = str_replace($fullMatch, $repl, $rawDequotedPHP);
}
}
}
// update PW procedural function calls
$functions = get_defined_functions();
$hasFunctionExists = strpos($data, 'function_exists') !== false;
$hasFunctionExists = strpos($rawDequotedPHP, 'function_exists') !== false;
foreach($functions['user'] as $function) {
@@ -761,14 +893,16 @@ class FileCompiler extends Wire {
}
if($ns) {}
/** @noinspection PhpUnusedLocalVariableInspection */
if(stripos($data, $function) === false) continue; // if function name not mentioned in data, quick exit
if(stripos($rawDequotedPHP, $function) === false) continue; // if function name not mentioned in data, quick exit
$n = 0;
while(preg_match_all('/^(.*?[()!;,@\[=\s.])' . $function . '\s*\(/im', $data, $matches)) {
while(preg_match_all('/^(.*?[()!;,@\[=\s.])' . $function . '\s*\(/im', $rawPHP, $matches)) {
foreach($matches[0] as $key => $fullMatch) {
$open = $matches[1][$key];
if(strpos($open, 'function') !== false) continue; // skip function defined with same name
$data = str_replace($fullMatch, $open . $functionName . '(', $data);
$repl = $open . $functionName . '(';
$data = str_replace($fullMatch, $repl, $data);
$rawPHP = str_replace($fullMatch, $repl, $rawPHP);
}
if(++$n > 5) break;
}
@@ -782,10 +916,10 @@ class FileCompiler extends Wire {
// update other function calls
$ns = __NAMESPACE__ ? "\\ProcessWire" : "";
if(strpos($data, 'class_parents(') !== false) {
if(strpos($rawDequotedPHP, 'class_parents(') !== false) {
$data = preg_replace('/\bclass_parents\(/', $ns . '\\wireClassParents(', $data);
}
if(strpos($data, 'class_implements(') !== false) {
if(strpos($rawDequotedPHP, 'class_implements(') !== false) {
$data = preg_replace('/\bclass_implements\(/', $ns . '\\wireClassImplements(', $data);
}
@@ -843,7 +977,7 @@ class FileCompiler extends Wire {
copy($sourceFile, $targetFile);
$this->chmod($targetFile);
touch($targetFile, filemtime($sourceFile));
$this->touch($targetFile, filemtime($sourceFile));
$numCopied++;
}
@@ -926,7 +1060,7 @@ class FileCompiler extends Wire {
// maintenance already run today
return false;
}
touch($lastRunFile);
$this->touch($lastRunFile);
$this->chmod($lastRunFile);
clearstatcache();
@@ -976,14 +1110,14 @@ class FileCompiler extends Wire {
if(!file_exists($sourceFile)) {
// source file has been deleted
unlink($targetFile);
$this->wire('files')->unlink($targetFile, true);
if($useLog) $this->log("Maintenance/Remove target file: $targetURL$basename");
} else if(filemtime($sourceFile) != filemtime($targetFile)) {
} else if(filemtime($sourceFile) > filemtime($targetFile)) {
// source file has changed
copy($sourceFile, $targetFile);
$this->chmod($targetFile);
touch($targetFile, filemtime($sourceFile));
$this->touch($targetFile, filemtime($sourceFile));
if($useLog) $this->log("Maintenance/Copy new version of source file to target file: $sourceURL$basename => $targetURL$basename");
}
}
@@ -1028,5 +1162,29 @@ class FileCompiler extends Wire {
$this->exclusions[] = $pathname;
}
/**
* Same as PHP touch() but with fallbacks for cases where touch() does not work
*
* @param string $filename
* @param null|int $time
* @return bool
*
*/
protected function touch($filename, $time = null) {
if($time === null) {
$result = @touch($filename);
} else {
$result = @touch($filename, $time);
// try again, but without time
if(!$result) $result = @touch($filename);
}
if(!$result) {
// lastly try alternative method which should have same affect as touch without $time
$fp = fopen($filename, 'a');
$result = $fp !== false ? fclose($fp) : false;
}
return $result;
}
}

View File

@@ -11,15 +11,73 @@
*/
class FileLog extends Wire {
const defaultChunkSize = 12288;
const debug = false;
protected $logFilename = false;
protected $itemsLogged = array();
/**
* Default size of chunks used for reading from logs
*
*/
const defaultChunkSize = 12288;
/**
* Debug mode used during development of this class
*
*/
const debug = false;
/**
* Chunk size used when reading from logs and not overridden
*
* @var int
*
*/
protected $chunkSize = self::defaultChunkSize;
/**
* Full path to log file or false when not yet set
*
* @var bool|string
*
*/
protected $logFilename = false;
/**
* Log items saved during this request where array keys are md5 hash of log entries and values ignored
*
* @var array
*
*/
protected $itemsLogged = array();
/**
* Delimiter used in log entries
*
* @var string
*
*/
protected $delimeter = "\t";
/**
* Maximum allowed line length for a single log line
*
* @var int
*
*/
protected $maxLineLength = 8192;
/**
* File extension used for log files
*
* @var string
*
*/
protected $fileExtension = 'txt';
/**
* Path where log files are stored
*
* @var string
*
*/
protected $path = '';
/**
@@ -58,52 +116,159 @@ class FileLog extends Wire {
protected function cleanStr($str) {
$str = str_replace(array("\r\n", "\r", "\n"), ' ', trim($str));
if(strlen($str) > $this->maxLineLength) $str = substr($str, 0, $this->maxLineLength);
if(strpos($str, ' ^+') !== false) $str = str_replace(' ^=', ' ^ +', $str); // disallowed sequence
return $str;
}
/**
* Save the given log entry string
*
* @param $str
* @return bool Success state
* @param string $str
* @param array $options options to modify behavior (Added 3.0.143)
* - `allowDups` (bool): Allow duplicating same log entry in same runtime/request? (default=true)
* - `mergeDups` (int): Merge previous duplicate entries that also appear near end of file?
* To enable, specify int for quantity of bytes to consider from EOF, value of 1024 or higher (default=0, disabled)
* - `maxTries` (int): If log entry fails to save, maximum times to re-try (default=20)
* - `maxTriesDelay` (int): Micro seconds (millionths of a second) to delay between re-tries (default=2000)
* @return bool Success state: true if log written, false if not.
*
*/
public function save($str) {
if(!$this->logFilename) return false;
public function save($str, array $options = array()) {
$defaults = array(
'mergeDups' => 0,
'allowDups' => true,
'maxTries' => 20,
'maxTriesDelay' => 2000,
);
if(!$this->logFilename) return false;
$options = array_merge($defaults, $options);
$hash = md5($str);
// if we've already logged this during this instance, then don't do it again
if(in_array($hash, $this->itemsLogged)) return true;
$ts = date("Y-m-d H:i:s");
$str = $this->cleanStr($str);
$fp = fopen($this->logFilename, "a");
$line = $this->delimeter . $str; // log entry, excluding timestamp
$hasLock = false; // becomes true when lock obtained
$fp = false; // becomes resource when file is open
if($fp) {
$trys = 0;
$stop = false;
// if we've already logged this during this instance, then don't do it again
if(!$options['allowDups'] && isset($this->itemsLogged[$hash])) return true;
while(!$stop) {
if(flock($fp, LOCK_EX)) {
fwrite($fp, "$ts{$this->delimeter}$str\n");
flock($fp, LOCK_UN);
$this->itemsLogged[] = $hash;
$stop = true;
} else {
usleep(2000);
if($trys++ > 20) $stop = true;
}
}
// determine write mode
$mode = file_exists($this->logFilename) ? 'a' : 'w';
if($mode === 'a' && $options['mergeDups']) $mode = 'r+';
fclose($fp);
$this->wire('files')->chmod($this->logFilename);
return true;
} else {
// open the log file
for($tries = 0; $tries <= $options['maxTries']; $tries++) {
$fp = fopen($this->logFilename, $mode);
if($fp) break;
// if unable to open for reading/writing, see if we can open for append instead
if($mode === 'r+' && $tries > ($options['maxTries'] / 2)) $mode = 'a';
usleep($options['maxTriesDelay']);
}
// if unable to open, exit now
if(!$fp) return false;
// obtain a lock
for($tries = 0; $tries <= $options['maxTries']; $tries++) {
$hasLock = flock($fp, LOCK_EX);
if($hasLock) break;
usleep($options['maxTriesDelay']);
}
// if unable to obtain a lock, we cannot write to the log
if(!$hasLock) {
fclose($fp);
return false;
}
// if opened for reading and writing, merge duplicates of $line
if($mode === 'r+' && $options['mergeDups']) {
// do not repeat the same log entry in the same chunk
$chunkSize = (int) $options['mergeDups'];
if($chunkSize < 1024) $chunkSize = 1024;
fseek($fp, -1 * $chunkSize, SEEK_END);
$chunk = fread($fp, $chunkSize);
// check if our log line already appears in the immediate earlier chunk
if(strpos($chunk, $line) !== false) {
// this log entry already appears 1+ times within the last chunk of the file
// remove the duplicates and replace the chunk
$chunkLength = strlen($chunk);
$this->removeLineFromChunk($line, $chunk, $chunkSize);
fseek($fp, 0, SEEK_END);
$oldLength = ftell($fp);
$newLength = $chunkLength > $oldLength ? $oldLength - $chunkLength : 0;
ftruncate($fp, $newLength);
fseek($fp, 0, SEEK_END);
fwrite($fp, $chunk);
}
} else {
// already at EOF because we are appending or creating
}
// add the log line
$result = fwrite($fp, "$ts$line\n");
// release the lock and close the file
flock($fp, LOCK_UN);
fclose($fp);
if($result && !$options['allowDups']) $this->itemsLogged[$hash] = true;
// if we were creating the file, make sure it has the right permission
if($mode === 'w') {
$files = $this->wire('files'); /** @var WireFileTools $files */
$files->chmod($this->logFilename);
}
return (int) $result > 0;
}
/**
* Remove given $line from $chunk and add counter to end of $line indicating quantity that was removed
*
* @param string $line
* @param string $chunk
* @param int $chunkSize
* @since 3.0.143
*
*/
protected function removeLineFromChunk(&$line, &$chunk, $chunkSize) {
$qty = 0;
$chunkLines = explode("\n", $chunk);
foreach($chunkLines as $key => $chunkLine) {
$x = 1;
if($key === 0 && strlen($chunk) >= $chunkSize) continue; // skip first line since its likely a partial line
// check if line appears in this chunk line
if(strpos($chunkLine, $line) === false) continue;
// check if line also indicates a previous quantity that we should add to our quantity
if(strpos($chunkLine, ' ^+') !== false) {
list($chunkLine, $n) = explode(' ^+', $chunkLine, 2);
if(ctype_digit($n)) $x += (int) $n;
}
// verify that these are the same line
if(strpos(trim($chunkLine) . "\n", trim($line) . "\n") === false) continue;
// remove the line
unset($chunkLines[$key]);
// update the quantity
$qty += $x;
}
if($qty) {
// append quantity to line, i.e. “^+2” indicating 2 more indentical lines were above
$chunk = implode("\n", array_values($chunkLines));
$line .= " ^+$qty";
}
}
public function size() {
@@ -145,7 +310,7 @@ class FileLog extends Wire {
*
*/
protected function getChunkArray($chunkNum = 1, $chunkSize = 0, $reverse = true) {
if($chunkSize < 1) $chunkSize = self::defaultChunkSize;
if($chunkSize < 1) $chunkSize = $this->chunkSize;
$lines = explode("\n", $this->getChunk($chunkNum, $chunkSize, $reverse));
foreach($lines as $key => $line) {
$line = trim($line);
@@ -165,15 +330,16 @@ class FileLog extends Wire {
* Returned string is automatically adjusted at the beginning and
* ending to contain only full log lines.
*
* @param int $chunkNum Current pagination number (default=1)
* @param int $chunkSize Number of bytes to retrieve (default=12288)
* @param int $chunkNum Current chunk/pagination number (default=1, first)
* @param int $chunkSize Number of bytes to retrieve (default=0, which assigns default chunk size of 12288)
* @param bool $reverse True=pull from end of file, false=pull from beginning (default=true)
* @param bool $clean Get a clean chunk that starts at the beginning of a line? (default=true)
* @return string
*
*/
protected function getChunk($chunkNum = 1, $chunkSize = 0, $reverse = true) {
protected function getChunk($chunkNum = 1, $chunkSize = 0, $reverse = true, $clean = true) {
if($chunkSize < 1) $chunkSize = self::defaultChunkSize;
if($chunkSize < 1) $chunkSize = $this->chunkSize;
if($reverse) {
$offset = -1 * ($chunkSize * $chunkNum);
@@ -181,7 +347,9 @@ class FileLog extends Wire {
$offset = $chunkSize * ($chunkNum-1);
}
if(self::debug) $this->message("chunkNum=$chunkNum, chunkSize=$chunkSize, offset=$offset, filesize=" . filesize($this->logFilename));
if(self::debug) {
$this->message("chunkNum=$chunkNum, chunkSize=$chunkSize, offset=$offset, filesize=" . filesize($this->logFilename));
}
$data = '';
$totalChunks = $this->getTotalChunks($chunkSize);
@@ -190,23 +358,27 @@ class FileLog extends Wire {
if(!$fp = fopen($this->logFilename, "r")) return $data;
fseek($fp, $offset, ($reverse ? SEEK_END : SEEK_SET));
// make chunk include up to beginning of first line
fseek($fp, -1, SEEK_CUR);
while(ftell($fp) > 0) {
$chr = fread($fp, 1);
if($chr == "\n") break;
fseek($fp, -2, SEEK_CUR);
$data = $chr . $data;
if($clean) {
// make chunk include up to beginning of first line
fseek($fp, -1, SEEK_CUR);
while(ftell($fp) > 0) {
$chr = fread($fp, 1);
if($chr == "\n") break;
fseek($fp, -2, SEEK_CUR);
$data = $chr . $data;
}
fseek($fp, $offset, ($reverse ? SEEK_END : SEEK_SET));
}
// get the big part of the chunk
fseek($fp, $offset, ($reverse ? SEEK_END : SEEK_SET));
$data .= fread($fp, $chunkSize);
// remove last partial line
$pos = strrpos($data, "\n");
if($pos) $data = substr($data, 0, $pos);
if($clean) {
// remove last partial line
$pos = strrpos($data, "\n");
if($pos) $data = substr($data, 0, $pos);
}
fclose($fp);
@@ -221,9 +393,9 @@ class FileLog extends Wire {
*
*/
protected function getTotalChunks($chunkSize = 0) {
if($chunkSize < 1) $chunkSize = self::defaultChunkSize;
if($chunkSize < 1) $chunkSize = $this->chunkSize;
$filesize = filesize($this->logFilename);
return ceil($filesize / $chunkSize);
return $filesize > 0 ? ceil($filesize / $chunkSize) : 0;
}
/**
@@ -234,7 +406,7 @@ class FileLog extends Wire {
*/
public function getTotalLines() {
if(filesize($this->logFilename) < self::defaultChunkSize) {
if(filesize($this->logFilename) < $this->chunkSize) {
$data = file($this->logFilename);
return count($data);
}
@@ -243,7 +415,7 @@ class FileLog extends Wire {
$totalLines = 0;
while(!feof($fp)) {
$data = fread($fp, self::defaultChunkSize);
$data = fread($fp, $this->chunkSize);
$totalLines += substr_count($data, "\n");
}
@@ -323,7 +495,7 @@ class FileLog extends Wire {
$cnt = 0; // number that will be written or returned by this
$n = 0; // number total
$chunkNum = 0;
$totalChunks = $this->getTotalChunks(self::defaultChunkSize);
$totalChunks = $this->getTotalChunks($this->chunkSize);
$stopNow = false;
$chunkLineHashes = array();
@@ -451,11 +623,11 @@ class FileLog extends Wire {
fclose($fpr);
if($cnt) {
unlink($filename);
rename("$filename.new", $filename);
$this->wire('files')->unlink($filename, true);
$this->wire('files')->rename("$filename.new", $filename, true);
$this->wire('files')->chmod($filename);
} else {
@unlink("$filename.new");
$this->wire('files')->unlink("$filename.new", true);
}
return $cnt;
@@ -477,8 +649,8 @@ class FileLog extends Wire {
'dateTo' => time(),
));
if(file_exists($toFile)) {
unlink($this->logFilename);
rename($toFile, $this->logFilename);
$this->wire('files')->unlink($this->logFilename, true);
$this->wire('files')->rename($toFile, $this->logFilename, true);
return $qty;
}
return 0;
@@ -491,7 +663,7 @@ class FileLog extends Wire {
*
*/
public function delete() {
return @unlink($this->logFilename);
return $this->wire('files')->unlink($this->logFilename, true);
}
public function __toString() {
@@ -517,6 +689,19 @@ class FileLog extends Wire {
public function setFileExtension($ext) {
$this->fileExtension = $ext;
}
/**
* Get or set the default chunk size used when reading from logs and not overridden by method argument
*
* @param int $chunkSize Specify chunk size to set, or omit to get
* @return int
* @since 3.0.143
*
*/
public function chunkSize($chunkSize = 0) {
if($chunkSize > 0) $this->chunkSize = (int) $chunkSize;
return $this->chunkSize;
}
}

View File

@@ -10,7 +10,7 @@
* 3. Copy the getModuleInfo() method out of this class and update as appropriate.
* 4. Implement an isValidFile($filename) method, and you are done.
*
* EXAMPLE: /site/modules/FileValidatorSVG.module
* EXAMPLE: /site/modules/FileValidatorHTML.module
*
* class FileValidatorHTML extends FileValidatorModule {
* public static function getModuleInfo() {

View File

@@ -106,8 +106,8 @@ class FilenameArray implements \IteratorAggregate, \Countable {
*
*/
public function remove($filename) {
$key = array_search($filename, $this->data);
if($key !== false) unset($this->data[$key]);
$key = $this->getKey($filename);
unset($this->data[$key]);
return $this;
}

View File

@@ -10,7 +10,7 @@
* This file is licensed under the MIT license
* https://processwire.com/about/license/mit/
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2019 by Ryan Cramer
* https://processwire.com
*
* @property ProcessWire $wire
@@ -36,6 +36,10 @@
* @property Languages $languages If LanguageSupport installed
* @property Config $config
* @property Fuel $fuel
* @property WireProfilerInterface $profiler
* @property WireFileTools $files
* @property WireMailTools $mail
* @property WireDateTime $datetime
*
*/
class Fuel implements \IteratorAggregate {
@@ -56,6 +60,27 @@ class Fuel implements \IteratorAggregate {
*/
protected $lock = array();
/**
* API vars that require specific interfaces
*
* @var array
*
*/
protected $requiredInterfaces = array(
'profiler' => 'WireProfilerInterface'
);
/**
* The most common API variable names, those likely to be called multiple times in any request
*
* @var array
*
*/
static protected $commonNames = array(
'page' => 1, 'pages' => 1, 'session' => 1, 'input' => 1, 'sanitizer' => 1, 'config' => 1,
'user' => 1, 'users' => 1, 'fields' => 1, 'templates' => 1, 'database' => 1, 'modules' => 1,
);
/**
* @param string $key API variable name to set - should be valid PHP variable name.
* @param object|mixed $value Value for the API variable.
@@ -68,6 +93,13 @@ class Fuel implements \IteratorAggregate {
if(isset($this->lock[$key]) && $value !== $this->data[$key]) {
throw new WireException("API variable '$key' is locked and may not be set again");
}
if(isset($this->requiredInterfaces[$key])) {
$requiredInterface = $this->requiredInterfaces[$key];
$hasInterfaces = wireClassImplements($value, false);
if(!isset($hasInterfaces[$requiredInterface]) && !in_array($requiredInterface, $hasInterfaces)) {
throw new WireException("API variable '$key' must implement interface: $requiredInterface");
}
}
$this->data[$key] = $value;
if($lock) $this->lock[$key] = true;
return $this;
@@ -92,6 +124,10 @@ class Fuel implements \IteratorAggregate {
public function __get($key) {
return isset($this->data[$key]) ? $this->data[$key] : null;
}
public function get($key) {
return isset($this->data[$key]) ? $this->data[$key] : null;
}
public function getIterator() {
return new \ArrayObject($this->data);
@@ -101,4 +137,7 @@ class Fuel implements \IteratorAggregate {
return $this->data;
}
public static function isCommon($name) {
return isset(self::$commonNames[$name]);
}
}

File diff suppressed because it is too large Load Diff

683
wire/core/FunctionsAPI.php Normal file
View File

@@ -0,0 +1,683 @@
<?php namespace ProcessWire;
/**
* ProcessWire functions API maps function names to common API variables
*
* #pw-summary-Functions-API =
* Provides an alternative to the API variables by providing functions of the same
* name, with these benefits:
*
* - They are always in scope whether inside a function or outside of it.
* - They are self documenting with your IDE, unlike API $variables.
* - They cannot be accidentally overwritten the way variables can be.
* - They provider greater contrast between what are API-calls and variables.
* - In some cases it makes for shorter API calls.
* - Some of the functions provide arguments for useful shortcuts.
*
* These functions always refer to the current ProcessWire instance, so are intended
* primarily for front-end usage in template files (not for modules).
*
* If these functions are not working for you, you can enable them by setting
* `$config->useFunctionsAPI=true;` in your /site/config.php file.
*
* Regardless of whether the Functions API is enabled or not, you can also access
* any of these functions by prefixing the word `wire` to them and using the format
* `wireFunction()` i.e. `wirePages()`, `wireUser()`, etc.
* Or, if you do not
* #pw-summary-Functions-API
*
*/
/**
* Retrieve or save pages ($pages API variable as a function)
*
* Accessing `pages()` is exactly the same as accessing `$pages`. Though there are a couple of optional
* shortcuts available by providing an argument to this function.
*
* ~~~~
* // A call with no arguments returns the $pages API variable
* $pages = pages();
* $pageArray = pages()->find("selector");
* $page = pages()->get(123);
*
* // Providing selector as argument maps to $pages->find()
* $pageArray = pages("template=basic-page");
*
* // Providing argument of single page ID, path or name maps to $pages->get()
* $page = pages(123);
* $page = pages("/path/to/page/");
* $page = pages("page-name");
* ~~~~
*
* #pw-group-Functions-API
*
* @param string|array|int $selector Specify one of the following:
* - Nothing, makes it return the $pages API variable.
* - Selector (string) to find matching pages, makes function return PageArray - equivalent to $pages->find("selector");
* - Page ID (int) to return a single matching Page - equivalent to $pages->get(123);
* - Page name (string) to return a single page having the given name - equivalent to $pages->get("name");
* @return Pages|PageArray|Page|NullPage
* @see Pages
*
*/
function pages($selector = '') {
return wirePages($selector);
}
/**
* Returns the current Page being viewed ($page API variable as a function)
*
* This function behaves the same as the `$page` API variable, though does support optional
* arguments as shortcuts for getting from the page or setting values to it.
*
* ~~~~
* $page = page(); // Simply get $page API var
*
* // Get the “body” field
* $body = page()->body; // direct syntax
* $body = page()->get('body'); // regular syntax
* $body = page('body'); // shortcut syntax
*
* // Get the “headline” field or fallback to “title
* $headline = page()->get('headline|title'); // regular syntax
* $headline = page('headline|title'); // shortcut syntax
*
* // Set the “headline” field
* page()->headline = 'My headline'; // direct syntax
* page()->set('headline', 'My headline'); // regular syntax
* page('headline', 'My headline'); // shortcut syntax
* ~~~~
*
* #pw-group-Functions-API
*
* @param string $key Optional property to get or set
* @param null $value Optional value to set
* @return Page|mixed
* @see Page
*
*/
function page($key = '', $value = null) {
return wirePage($key, $value);
}
/**
* Access a ProcessWire configuration setting ($config API variable as a function)
*
* This function behaves the same as the `$config` API variable, though does support
* optional shortcut arguments for getting/setting values.
*
* ~~~~~
* $config = config(); // Simply get $config API var
* $debug = config()->debug; // Get value of debug
* $debug = config('debug'); // Same as above, shortcut syntax
* config()->debug = true; // Set value of debug
* config('debug', true); // Same as above, shortcut syntax
* ~~~~~
*
* #pw-group-Functions-API
*
* @param string $key
* @param null $value
* @return Config|mixed
* @see Config
*
*/
function config($key = '', $value = null) {
return wireConfig($key, $value);
}
/**
* Get a module, get module information, and much more ($modules API variable as a function)
*
* This function behaves the same as the `$modules` API variable, though does support
* an optional shortcut argument for getting a module.
*
* ~~~~~
* $modules = modules(); // Simply get $modules API var
* $module = modules()->get('ModuleName'); // Get a module
* $module = modules('ModuleName'); // Shortcut to get a module
* ~~~~~
*
* #pw-group-Functions-API
*
* @param string $name Optionally retrieve the given module name
* @return Modules|Module|ConfigurableModule|null
* @see Modules
*
*/
function modules($name = '') {
return wireModules($name);
}
/**
* Get the currently logged in user ($user API variable as a function)
*
* This function behaves the same as the `$user` API variable, though does support
* optional shortcut arguments for getting or setting values.
*
* #pw-group-Functions-API
*
* @param string $key Optional property to get or set
* @param null $value Optional value to set
* @return User|mixed
* @see User
*
*/
function user($key = '', $value = null) {
return wireUser($key, $value);
}
/**
* Get, find or save users ($users API variable as a function)
*
* This function behaves the same as the `$users` API variable, though does support
* an optional shortcut argument for getting a single user or finding multiple users.
*
* ~~~~~~
* // Get a single user (regular and shortcut syntax)
* $u = users()->get('karen');
* $u = users('karen');
*
* // Find multiple users (regular and shortcut syntax)
* $us = users()->find('roles.name=editor');
* $us = users('roles.name=editor');
* ~~~~~~
*
* #pw-group-Functions-API
*
* @param string|array|int $selector Optional selector to send to find() or get()
* - Specify user name or ID to get and return that User
* - Specify a selector string to find all users matching selector (PageArray)
* @return Users|PageArray|User|mixed
* @see pages(), Users
*
*/
function users($selector = '') {
return wireUsers($selector);
}
/**
* Get or set values in the current user session ($session API variable as a function)
*
* This function behaves the same as the `$session` API variable, though does support
* optional shortcut arguments for getting or setting values.
*
* ~~~~~
* // Get a value from the session
* $foo = session()->foo; // direct syntax
* $foo = session()->get('foo'); // regular syntax
* $foo = session('foo'); // shortcut syntax
*
* // Set a value to the session
* session()->foo = 'bar'; // direct syntax
* session()->set('foo', 'bar'); // regular syntax
* session('foo', 'bar'); // shortcut syntax
* ~~~~~
*
* #pw-group-Functions-API
*
* @param string $key Optional property to get or set
* @param null $value Optional value to set
* @return Session|null|string|array|int|float
* @see Session
*
*/
function session($key = '', $value = null) {
return wireSession($key, $value);
}
/**
* Get or save fields independent of templates ($fields API variable as as function)
*
* This function behaves the same as the `$fields` API variable, though does support
* an optional shortcut argument for getting a single field.
*
* ~~~~~
* $field = fields()->get('title'); // regular syntax
* $field = fields('title'); // shortcut syntax
* ~~~~~
*
* #pw-group-Functions-API
*
* @param string $name Optional field name to retrieve
* @return Fields|Field|null
* @see Fields
*
*/
function fields($name = '') {
return wireFields($name);
}
/**
* Get or save templates ($templates API variable as a function)
*
* This function behaves the same as the `$templates` API variable, though does support
* an optional shortcut argument for getting a single template.
*
* ~~~~~~
* $t = templates()->get('basic-page'); // regular syntax
* $t = templates('basic-page'); // shortcut syntax
* ~~~~~~
*
* #pw-group-Functions-API
*
* @param string $name Optional template to retrieve
* @return Templates|Template|null
* @see Templates
*
*/
function templates($name = '') {
return wireTemplates($name);
}
/**
* Create and execute PDO database queries ($database API variable as a function)
*
* #pw-group-Functions-API
*
* @return WireDatabasePDO
* @see WireDatabasePDO
*
*/
function database() {
return wireDatabase();
}
/**
* Get, find or save permissions ($permissions API variable as a function)
*
* Accessing `permissions()` is exactly the same as accessing `$permissions`. Though there are a couple of optional
* shortcuts available by providing an argument to this function.
*
* ~~~~~
* // Get a permission
* $p = permissions()->get('page-edit'); // regular syntax
* $p = permissions('page-edit'); // shortcut syntax
*
* // Find permissions
* $ps = permissions()->find('name^=page'); // regular syntax
* $ps = permissions('name^=page'); // shortcut syntax
* ~~~~~
*
* #pw-group-Functions-API
*
* @param string|int $selector
* - Specify permission name or ID to retrieve that Permission (Permission)
* - Specify a selector string to return all permissions matching selector (PageArray)
* @return Permissions|Permission|PageArray|null|NullPage
* @see Permissions
*
*/
function permissions($selector = '') {
return wirePermissions($selector);
}
/**
* Get, find or save roles ($roles API variable as a function)
*
* Accessing `roles()` is exactly the same as accessing `$roles`. Though there are a couple of optional
* shortcuts available by providing an argument to this function.
*
* #pw-group-Functions-API
*
* @param string|int $selector
* - Specify name or ID of role to get (Role object)
* - Specify selector string matching roles to find (PageArray object)
* @return Roles|Role|PageArray|null|NullPage
* @see Roles
*
*/
function roles($selector = '') {
return wireRoles($selector);
}
/**
* Sanitize variables and related string functions ($sanitizer API variable as a function)
*
* This behaves the same as the `$sanitizer` API variable but supports arguments as optional shortcuts.
*
* ~~~~~
* $clean = sanitizer()->pageName($dirty); // regular syntax
* $clean = sanitizer('pageName', $dirty); // shortcut syntax
* ~~~~~
*
* #pw-group-Functions-API
*
* @param string $name Optionally enter a sanitizer function name
* @param string $value If $name populated, enter the value to sanitize
* @return Sanitizer|string|int|array|null|mixed
* @see Sanitizer
*
*/
function sanitizer($name = '', $value = '') {
return wireSanitizer($name, $value);
}
/**
* Access date and time related tools ($datetime API variable as a function)
*
* This behaves the same as the `$datetime` API variable except that you can optionally provide
* arguments as a shortcut to the `$datetime->formatDate()` method.
*
* ~~~~~
* $str = datetime()->relativeTimeStr('2016-10-10');
* $str = datetime('Y-m-d'); // shortcut to formatDate method
* $str = datetime('Y-m-d', time()); // shortcut to formatDate method
* ~~~~~
*
* #pw-group-Functions-API
*
* @param string $format Optional date format
* @param string|int $value Optional date to format
* @return WireDateTime|string|int
* @see WireDateTime
*
*/
function datetime($format = '', $value = '') {
return wireDatetime($format, $value);
}
/**
* Access tools for working on the file system ($files API variable as a function)
*
* This behaves identically to the `$files` API variable and as no optional arguments.
*
* #pw-group-Functions-API
*
* @return WireFileTools
* @see WireFileTools
*
*/
function files() {
return wireFiles();
}
/**
* Get and save caches ($cache API variable as a function)
*
* This behaves the same as the $cache API variable but does support arguments as a
* shortcut for the `$cache->get()` method.
*
* - If called with no arguments it returns the $cache API variable.
* - If called with arguments, it can be used the same as `WireCache::get()`.
*
* #pw-group-Functions-API
*
* @param string $name
* @param callable|int|string|null $expire
* @param callable|int|string|null $func
* @return WireCache|string|array|PageArray|null
* @see WireCache, WireCache::get()
*
*/
function cache($name = '', $expire = null, $func = null) {
return wireCache($name, $expire, $func);
}
/**
* Access all installed languages in multi-language environment ($languages API variable as a function)
*
* Returns the `$languages` API variable, or a `Language` object if given a language name or ID.
*
* ~~~~
* $languages = languages(); // Languages if active, null if not
* $en = languages()->getDefault(); // Get default language
* $de = languages()->get('de'); // Get another language
* $de = languages('de'); // Get another language (shorcut syntax)
* ~~~~
*
* #pw-group-Functions-API
*
* @param string|int $name Optional Language name or ID for language to retrieve
* @return Languages|Language|NullPage|null
* @see Languages, Languages::get(), Language
*
*/
function languages($name = '') {
return wireLanguages($name);
}
/**
* Access GET, POST or COOKIE input variables and more ($input API variable as a function)
*
* - Default behavior with no arguments is to return the `$input` API variable.
* - If given just a `$type` argument (like “get” or “post”), it will return a `WireInputData` object for that type.
* - If given a `$type` and `$key` arguments, it will return the value, or null if not present.
* - If `$sanitizer` argument given, the returned value will also be run through the given sanitizer.
* - If the `$sanitizer` argument is an array, the returned input value must be within the given list, or null if not (3.0.125+).
* - If `$fallback` argument given, it will return the fallback value if input value was not present or not valid (3.0.125+).
* - See the `WireInput::get()` method for all options.
*
* ~~~~~
* // Can be used the same way as the $input API variable
* // In examples below the “post” can also be “get” or “cookie”
* $input = input(); // Returns $input API var (WireInput)
* $post = input()->post(); // Returns $input->post (WireInputData instance)
* $foo = input()->post('foo'); // Returns POST variable “foo”
* $bar = input()->post('bar', 'text'); // Returns “bar” after text sanitizer (3.0.125+)
* $s = input()->post('s', ['foo', 'bar', 'baz']); // POST var “s” must match given list (3.0.125+)
*
* // You can also move the arguments all to the function call if you prefer:
* $s = input('get', 'sort'); // Returns GET var “sort”
* $s = input('get', 'sort', 'fieldName'); // Returns “sort” after “fieldName” sanitizer
* $s = input('get', 'sort', ['title', 'created']); // Require sort to be one in given array (3.0.125+)
* $s = input('get', 'sort', ['title', 'created'], 'title'); // Same as above, fallback to 'title' (3.0.125+)
* ~~~~~
*
* #pw-group-Functions-API
*
* @param string $type Optionally indicate "get", "post", "cookie" or "whitelist"
* @param string $key If getting a value, specify name of input property containing value
* @param string $sanitizer Optionally specify sanitizer name to run value through, or in 3.0.125+ may also be an array of allowed values.
* @param string|int|null $fallback Value to fallback to if input not present or invalid
* @return WireInput|WireInputData|array|string|int|null
* @see WireInput
*
*/
function input($type = '', $key = '', $sanitizer = null, $fallback = null) {
return wireInput($type, $key, $sanitizer, $fallback);
}
/**
* Access the $input->get API variable as a function
*
* This is the same as the input() function except that the $type "get" is already implied.
*
* #pw-internal
*
* @param string $key
* @param string $sanitizer
* @return WireInputData|string|int|array|null
*
*/
function inputGet($key = '', $sanitizer = '') {
return wireInputGet($key, $sanitizer);
}
/**
* Access the $input->post API variable as a function
*
* This is the same as the input() function except that the $type "post" is already implied.
*
* #pw-internal
*
* @param string $key
* @param string $sanitizer
* @return WireInputData|string|int|array|null
*
*/
function inputPost($key = '', $sanitizer = '') {
return wireInputPost($key, $sanitizer);
}
/**
* Access the $input->cookie API variable as a function
*
* This is the same as the input() function except that the $type "cookie" is already implied.
*
* #pw-internal
*
* @param string $key
* @param string $sanitizer
* @return WireInputData|string|int|array|null
*
*/
function inputCookie($key = '', $sanitizer = '') {
return wireInputCookie($key, $sanitizer);
}
/**
* Get one of any named system URLs (shortcut to the $config API variable “urls” property)
*
* URLs always have a trailing slash.
*
* ~~~~~
* // you can use either syntax below, where “templates” can be the name for any system URL
* $url = urls()->templates;
* $url = urls('templates');
* ~~~~~
*
* #pw-group-Functions-API
*
* @param string $key
* @return null|Paths|string
* @see Config::urls()
*
*/
function urls($key = '') {
return wireUrls($key);
}
/**
* Get one of any named server disk paths (shortcut to the $config API variable “paths” property)
*
* Paths always have a trailing slash.
*
* ~~~~~
* // you can use either syntax below, where “templates” can be the name for any system URL
* $path = paths()->templates;
* $path = paths('templates');
* ~~~~~
*
* #pw-group-Functions-API
*
* @param string $key
* @return null|Paths|string
* @see Config::paths()
*
*/
function paths($key = '') {
return wirePaths($key);
}
/**
* Start or stop a profiler event or return WireProfilerInterface instance
*
* #pw-internal
*
* @param string|array|object|null $name Name of event to start or event to stop
* @param null|object|string $source If starting an event, optional source of event (object)
* @param array $data Optional extra data as associative array
* @return null|array|object
*
*/
function profiler($name = null, $source = null, $data = array()) {
return wireProfiler($name, $source, $data);
}
/**
* Get or set an output region (primarily for front-end output usage)
*
* This function is an convenience for storing markup that ultimately gets output in a _main.php file
* (or whatever file `$config->appendTemplateFile` is set to). It is an alternative to passing variables
* between included files and provides an interface for setting, appending, prepending and ultimately
* getting markup (or other strings) for output. Its designed for use the the “Delayed Output” strategy,
* though does not necessarily require it.
*
* This function can also be accessed as `wireRegion()`, and that function is always available
* regardless of whether the Functions API is enabled or not.
*
* *Note: unlike other functions in the Functions API, this function is not related to API variables.*
*
* ~~~~~
* // define a region
* region('content', '<p>this is some content</p>');
*
* // prepend some text to region
* region('+content', '<h2>Good morning</h2>');
*
* // append some text to region
* region('content+', '<p><small>Good night</small></p>');
*
* // output a region
* echo region('content');
*
* // get all regions in an array
* $regions = region('*');
*
* // clear the 'content' region
* region('content', '');
*
* // clear all regions
* region('*', '');
* ~~~~~
*
* #pw-group-Functions-API
*
* @param string $key Name of region to get or set.
* - Specify "*" to retrieve all defined regions in an array.
* - Prepend a "+" to the region name to have it prepend your given value to any existing value.
* - Append a "+" to the region name to have it append your given value to any existing value.
* - Prepend a "++" to region name to make future calls without "+" automatically prepend.
* - Append a "++" to region name to make future calls without "+" to automatically append.
* @param null|string $value If setting a region, the text that you want to set.
* @return string|null|bool|array Returns string of text when getting a region, NULL if region not set, or TRUE if setting region.
*
*/
function region($key = '', $value = null) {
return wireRegion($key, $value);
}
/**
* Get or set a runtime site setting
*
* This is a simple helper function for maintaining runtime settings in a site profile.
* It simply sets and gets settings that you define. It is preferable to using ProcessWires
* `$config` or `config()` API var/function because it is not used to store anything else for
* ProcessWire. It is also preferable to using a variable (or variables) because it is always
* in scope and accessible anywhere in your template files, even within existing functions.
*
* *Note: unlike other functions in the Functions API, this function is not related to API variables.*
*
* ~~~~~
* // set a setting named “foo” to value “bar”
* setting('foo', 'bar');
*
* // get a setting named “foo”
* $value = setting('foo');
*
* // set or replace multiple settings
* setting([
* 'foo' => 'value',
* 'bar' => 123,
* 'baz' => [ 'foo', 'bar', 'baz' ]
* ]);
*
* // get all settings in associative array
* $a = setting();
*
* // to unset a setting
* setting(false, 'foo');
* ~~~~~
*
* #pw-group-Functions-API
*
* @param string|array $name Setting name, or array to set multiple
* @param string|int|array|float|mixed $value Value to set, or omit if getting value of $name (default=null)
* @return array|string|int|bool|mixed|null
*
*/
function setting($name = '', $value = null) {
return wireSetting($name, $value);
}

View File

@@ -0,0 +1,597 @@
<?php namespace ProcessWire;
/**
* ProcessWire functions API maps function names to common API variables
*
* Provides an alternative to the API variables by providing functions of the same
* name, with these benefits:
*
* - They are always in scope
* - Makes life simpler in an IDE that recognizes phpdoc, as it can more easily
* recognize the types an return values.
* - In some cases it makes for shorter API calls.
*
* The primary drawback is that the function calls are not mapped to a specific
* instance, so in a multi-instance environment it's possible these function calls
* may not be referring to the correct ProcessWire instance. For this reason, we
* think these functions are primarily useful for front-end/website usages, and
* not as useful for back-end and module development.
*
* Shorter versions of these functions (without the leading "wire") can be found in
* FunctionsAPI.php file, which is used only if $config->useFunctionsAPI is true.
* The functions in this file are always available regardless of that setting.
*
*
*/
/**
* Common helper for API functions dealing with pages
*
* @param $_apiVar
* @param $selector
* @return null|NullPage|Page|PageArray|Pages|PagesType
*
*/
function _wirePagesAPI($_apiVar, $selector) {
/** @var Pages|PagesType $pages */
$pages = is_object($_apiVar) ? $_apiVar : wire($_apiVar);
if(!$pages) return null;
if(!$selector) return $pages;
if(is_array($selector) || is_object($selector)) {
return $pages->find($selector);
} else if(ctype_digit("$selector")) {
// i.e. "123"
return $pages->get((int) $selector);
} else if(wireSanitizer('pageName', $selector) === $selector) {
// i.e. "contact"
return $pages->get("name=$selector");
} else if(strpos($selector, '/') !== false && wireSanitizer('pagePathName', $selector) === $selector) {
// i.e. "/path/to/page/"
return $pages->get($selector);
} else {
return $pages->find($selector);
}
}
/**
* Common helper for API functions dealing with WireData objects
*
* @param $_apiVar
* @param $key
* @param $value
* @return mixed|null|WireData|Page
*
*/
function _wireDataAPI($_apiVar, $key, $value) {
/** @var WireData $item */
$item = wire($_apiVar);
if(!$item) return null;
if(strlen($key)) {
if(is_null($value)) {
return $item->get($key);
} else {
$item->set($key, $value);
}
}
return $item;
}
/**
* Access the $pages API variable as a function
*
* ~~~~
* // A call with no arguments returns the $pages API variable
* $pages = pages();
* $pageArray = pages()->find("selector");
* $page = pages()->get(123);
*
* // Providing selector as argument maps to $pages->find()
* $pageArray = pages("template=basic-page");
*
* // Providing argument of single page ID, path or name maps to $pages->get()
* $page = pages(123);
* $page = pages("/path/to/page/");
* $page = pages("page-name");
* ~~~~
*
* @param string|array $selector Specify one of the following:
* - Nothing, makes it return the $pages API variable.
* - Selector (string) to find matching pages, makes function return PageArray - equivalent to $pages->find("selector");
* - Page ID (int) to return a single matching Page - equivalent to $pages->get(123);
* - Page name (string) to return a single page having the given name - equivalent to $pages->get("name");
* @return Pages|PageArray|Page|NullPage
*
*/
function wirePages($selector = '') {
return _wirePagesAPI('pages', $selector);
}
/**
* Access the $page API variable as a function
*
* ~~~~
* $page = page(); // Simply get $page API var
* $body = page()->body; // Get body field value
* $body = page('body'); // Same as above
* $headline = page('headline|title'); // Get headline or title
* page('headline', 'Setting headline value'); // Set headline
* ~~~~
*
* @param string $key Optional property to get or set
* @param null $value Optional value to set
* @return Page|mixed
*
*/
function wirePage($key = '', $value = null) {
return _wireDataAPI('page', $key, $value);
}
/**
* Access the $config API variable as a function
*
* ~~~~~
* $config = config(); // Simply get $config API var
* $debug = config()->debug; // Get value of debug
* $debug = config('debug'); // Same as above
* config()->debug = true; // Set value of debug
* config('debug', true); // Same as above
* ~~~~~
*
* @param string $key
* @param null $value
* @return Config|mixed
*
*/
function wireConfig($key = '', $value = null) {
return _wireDataAPI('config', $key, $value);
}
/**
* Access the $modules API variable as a function
*
* ~~~~~
* $modules = modules(); // Simply get $modules API var
* $module = modules()->get('ModuleName'); // Get a module
* $module = modules('ModuleName'); // Shortcut to get a module
* ~~~~~
*
* @param string $name Optionally retrieve the given module name
* @return Modules|Module|ConfigurableModule|null
*
*/
function wireModules($name = '') {
/** @var Modules $modules */
$modules = wire('modules');
return strlen($name) ? $modules->getModule($name) : $modules;
}
/**
* Access the $user API variable as a function
*
* @param string $key Optional property to get or set
* @param null $value Optional value to set
* @return User|mixed
*
*/
function wireUser($key = '', $value = null) {
return _wireDataAPI('user', $key, $value);
}
/**
* Access the $users API variable as a function
*
* See the pages() function for full usage details.
*
* @param string|array $selector Optional selector to send to find() or get()
* @return Users|PageArray|User|mixed
* @see pages()
*
*/
function wireUsers($selector = '') {
return _wirePagesAPI('users', $selector);
}
/**
* Access the $session API variable as a function
*
* @param string $key Optional property to get or set
* @param null $value Optional value to set
* @return Session|mixed
*
*/
function wireSession($key = '', $value = null) {
return _wireDataAPI('session', $key, $value);
}
/**
* Access the $fields API variable as a function
*
* @param string $name Optional field name to retrieve
* @return Fields|Field|null
*
*/
function wireFields($name = '') {
/** @var Fields $fields */
$fields = wire('fields');
return strlen($name) ? $fields->get($name) : $fields;
}
/**
* Access the $templates API variable as a function
*
* @param string $name Optional template to retrieve
* @return Templates|Template|null
*
*/
function wireTemplates($name = '') {
/** @var Templates $templates */
$templates = wire('templates');
return strlen($name) ? $templates->get($name) : $templates;
}
/**
* Access the $database API variable as a function
*
* @return WireDatabasePDO
*
*/
function wireDatabase() {
return wire('database');
}
/**
* Access the $permissions API varaible as a function
*
* See the pages() function for usage details.
*
* @param string $selector
* @return Permissions|Permission|PageArray|null|NullPage
*
*/
function wirePermissions($selector = '') {
return _wirePagesAPI('permissions', $selector);
}
/**
* Access the $roles API varaible as a function
*
* See the pages() function for usage details.
*
* @param string $selector
* @return Roles|Role|PageArray|null|NullPage
*
*/
function wireRoles($selector = '') {
return _wirePagesAPI('roles', $selector);
}
/**
* Access the $sanitizer API variable as a function
*
* ~~~~~
* // Example usages
* $clean = sanitizer()->pageName($dirty);
* $clean = sanitizer('pageName', $dirty); // same as above
* ~~~~~
*
* @param string $name Optionally enter a sanitizer function name
* @param string $value If $name populated, enter the value to sanitize
* @return Sanitizer|string|int|array|null|mixed
*
*/
function wireSanitizer($name = '', $value = '') {
$sanitizer = wire('sanitizer');
return strlen($name) ? $sanitizer->$name($value) : $sanitizer;
}
/**
* Access the $datetime API variable as a function
*
* ~~~~~
* // Example usages
* $str = datetime()->relativeTimeStr('2016-10-10');
* $str = datetime('Y-m-d');
* $str = datetime('Y-m-d', time());
* ~~~~~
*
* @param string $format Optional date format
* @param string|int $value Optional date to format
* @return WireDateTime|string|int
*
*/
function wireDatetime($format = '', $value = '') {
/** @var WireDateTime $datetime */
$datetime = wire('datetime');
return strlen($format) ? $datetime->formatDate($value ? $value : time(), $format) : $datetime;
}
/**
* Access the $files API variable as a function
*
* @return WireFileTools
*
*/
function wireFiles() {
return wire('files');
}
/**
* Access the $cache API variable as a function
*
* If called with no arguments it returns the $cache API variable.
* If called with arguments, it can be used the same as `WireCache::get()`.
*
* @param string $name
* @param callable|int|string|null $expire
* @param callable|int|string|null $func
* @return WireCache|string|array|PageArray|null
* @see WireCache::get()
*
*/
function wireCache($name = '', $expire = null, $func = null) {
/** @var WireCache $cache */
$cache = wire('cache');
return strlen($name) ? $cache->get($name, $expire, $func) : $cache;
}
/**
* Access the $languages API variable as a function
*
* Returns the $languages API variable, or a Language object if given a language name.
*
* ~~~~
* // Examples
* $languages = languages(); // Languages if active, null if not
* $en = languages()->getDefault();
* $de = languages('de');
* ~~~~
*
* @param string|int $name Optional Language name or ID for language to retrieve
* @return Languages|Language|NullPage|null
*
*/
function wireLanguages($name = '') {
/** @var Languages $languages */
$languages = wire('languages');
if(!$languages) return null;
if(strlen($name)) return $languages->get($name);
return $languages;
}
/**
* Access the $input API variable as a function
*
* - Default behavior is to return the $input API var.
* - If given just a $type (like "get" or "post"), it will return a WireInputData object for that type.
* - If given a $type and $key it will return the input variable.
* - If all arguments given, the returned value will also be run through the given sanitizer.
*
* ~~~~~
* // Examples
* $input = wireInput(); // Returns $input API var (WireInput)
* $post = wireInput('post'); // Returns $input->post (WireInputData)
* $post = wireInput()->post(); // Same as above
* $value = wireInput('get', 'sort'); // Returns $input->get('sort');
* $value = wireInput('get', 'sort', 'fieldName'); // Returns $input->get('sort') run through $sanitizer->fieldName().
* $value = wireInput('get', 'sort', 'fieldName', 'title'); // Same as above but fallback to 'title' if no sort is present (3.0.125)
* $value = wireInput('get', 'sort', ['title', 'created', 'likes'], 'title'); // Require value to be one given or fallback to 'title' (3.0.125+)
* $value = wireInput()->get('sort', ['title', 'created', 'likes'], 'title'); // Same as above (3.0.125)
* ~~~~~
*
* @param string $type Optionally indicate "get", "post", "cookie" or "whitelist"
* @param string $key If getting a value, specify name of property containing value
* @param string $sanitizer Optionally specify sanitizer name to run value through, or array containing whitelist of allowed values (3.0.125).
* @param mixed $fallback Fallback value to return rather than null if value not present or does not validate (3.0.125+)
* @return WireInput|WireInputData|array|string|int|null
*
*/
function wireInput($type = '', $key = '', $sanitizer = null, $fallback = null) {
/** @var WireInput $input */
$input = wire('input');
if(!strlen($type)) return $input;
$type = strtolower($type);
if(!strlen($key)) return $input->$type;
$value = $input->$type($key, $sanitizer, $fallback);
return $value;
}
/**
* Access the $input->get API variable as a function
*
* This is the same as the input() function except that the $type "get" is already implied.
*
* @param string $key Name of input variable to get
* @param string $sanitizer Optionally specify sanitizer name to run value through, or array containing whitelist of allowed values (3.0.125+).
* @param mixed $fallback Fallback value to return rather than null if value not present or does not validate (3.0.125+)
* @return WireInputData|string|int|array|null
*
*/
function wireInputGet($key = '', $sanitizer = null, $fallback = null) {
return wireInput('get', $key, $sanitizer, $fallback);
}
/**
* Access the $input->post API variable as a function
*
* This is the same as the input() function except that the $type "post" is already implied.
*
* @param string $key Name of input variable to get
* @param string $sanitizer Optionally specify sanitizer name to run value through, or array containing whitelist of allowed values (3.0.125).
* @param mixed $fallback Fallback value to return rather than null if value not present or does not validate (3.0.125+)
* @return WireInputData|string|int|array|null
*
*/
function wireInputPost($key = '', $sanitizer = null, $fallback = null) {
return wireInput('post', $key, $sanitizer, $fallback);
}
/**
* Access the $input->cookie API variable as a function
*
* This is the same as the input() function except that the $type "cookie" is already implied.
*
* @param string $key Name of input variable to get
* @param string $sanitizer Optionally specify sanitizer name to run value through, or array containing whitelist of allowed values (3.0.125+).
* @param mixed $fallback Fallback value to return rather than null if value not present or does not validate (3.0.125+)
* @return WireInputData|string|int|array|null
*
*/
function wireInputCookie($key = '', $sanitizer = null, $fallback = null) {
return wireInput('cookie', $key, $sanitizer, $fallback);
}
/**
* Access the $log API variable as a function
*
* Default behavior is to return the $log API variable.
* If both arguments are provided, it assumes you want to log a message.
*
* @param string $logName If logging a message, specify the name of the log.
* @param string $message If logging a message, specify the message text.
* @return WireLog|bool Returns bool if saving log entry, WireLog otherwise.
*
*/
function wireLog($logName = '', $message = '') {
/** @var WireLog $log */
$log = wire('log');
if(strlen($message)) {
if(!strlen($logName)) $logName = 'unknown';
return $log->save($logName, $message);
}
return $log;
}
/**
* Start or stop a profiler event or return WireProfilerInterface instance
*
* @param string|array|object|null $name Name of event to start or event to stop
* @param null|object|string $source If starting an event, optional source of event (object)
* @param array $data Optional extra data as associative array
* @return null|array|object
*
*/
function wireProfiler($name = null, $source = null, $data = array()) {
$profiler = wire('profiler');
if(is_null($name)) return $profiler;
if(!$profiler) return null;
if(is_string($name)) {
return $profiler->start($name, $source, $data);
} else {
return $profiler->stop($name);
}
}
/**
* Function that returns a $config->urls->[name] value o
*
* @param string $key
* @return null|Paths|string
*
*/
function wireUrls($key = '') {
if(empty($key)) return wire('config')->urls;
return wire('config')->urls($key);
}
/**
* Function that returns a $config->paths->[name] value o
*
* @param string $key
* @return null|Paths|string
*
*/
function wirePaths($key = '') {
if(empty($key)) return wire('config')->paths;
return wire('config')->paths($key);
}
/**
* Get or set a runtime site setting
*
* This is a simple helper function for maintaining runtime settings in a site profile.
* It simply sets and gets settings that you define. It is preferable to using ProcessWires
* `$config` or `config()` API var/function because it is not used to store anything else for
* ProcessWire. It is also preferable to using a variable (or variables) because it is always
* in scope and accessible anywhere in your template files, even within existing functions.
*
* ~~~~~
* // set a setting named “foo” to value “bar”
* setting('foo', 'bar');
*
* // get a setting named “foo”
* $value = setting('foo');
*
* // set or replace multiple settings
* setting([
* 'foo' => 'value',
* 'bar' => 123,
* 'baz' => [ 'foo', 'bar', 'baz' ]
* ]);
*
* // get all settings in associative array
* $a = setting();
*
* // to unset a setting
* setting(false, 'foo');
* ~~~~~
*
* @param string|array $name Setting name, or array to set multiple
* @param string|int|array|float|mixed $value Value to set, or omit if getting value of $name (default=null)
* @return array|string|int|bool|mixed|null
*
*/
function wireSetting($name = '', $value = null) {
static $settings = array();
if($name === '') return $settings;
if(is_array($name)) return $settings = array_merge($settings, $name);
if($name === false) { unset($settings[(string) $value]); return null; }
if($value !== null) $settings[$name] = $value;
return isset($settings[$name]) ? $settings[$name] : null;
}
/**
* Return array of functions available from the functions API
*
* Returned array is shortVersion => longVersion
*
* @return array
*
*/
function _wireFunctionsAPI() {
$names = array(
'cache' => 'wireCache',
'config' => 'wireConfig',
'database' => 'wireDatabase',
'datetime' => 'wireDatetime',
'fields' => 'wireFields',
'files' => 'wireFiles',
'input' => 'wireInput',
'inputGet' => 'wireInputGet',
'inputPost' => 'wireInputPost',
'inputCookie' => 'wireInputCookie',
'languages' => 'wireLanguages',
'modules' => 'wireModules',
'page' => 'wirePage',
'pages' => 'wirePages',
'paths' => 'wirePaths',
'permissions' => 'wirePermissions',
'profiler' => 'wireProfiler',
'region' => 'wireRegion',
'roles' => 'wireRoles',
'sanitizer' => 'wireSanitizer',
'setting' => 'wireSetting',
'session' => 'wireSession',
'templates' => 'wireTemplates',
'urls' => 'wireUrls',
'user' => 'wireUser',
'users' => 'wireUsers',
);
return $names;
}

View File

@@ -9,6 +9,7 @@
* Instances of HookEvent are passed to Hook handlers when their requested method has been called.
*
* #pw-summary HookEvent is a type provided to hook functions with information about the event.
* #pw-var $event
* #pw-body =
* ~~~~~~
* // Example
@@ -23,7 +24,7 @@
*
* @property-read Wire|WireData|WireArray|Module $object Instance of the object where the Hook event originated.
* @property-read string $method The name of the method that was called to generate the Hook event.
* @property-read array $arguments A numerically indexed array of the arguments sent to the above mentioned method.
* @property array $arguments A numerically indexed array of the arguments sent to the above mentioned method.
* @property mixed $return Applicable only for 'after' or ('replace' + 'before' hooks), contains the value returned by the above mentioned method. The hook handling method may modify this return value.
* @property bool $replace Set to boolean true in a 'before' hook if you want to prevent execution of the original hooked function. In such a case, your hook is replacing the function entirely. Not recommended, so be careful with this.
* @property array $options An optional array of user-specified data that gets sent to the hooked function. The hook handling method may access it from $event->data. Also includes all the default hook properties.
@@ -42,17 +43,23 @@ class HookEvent extends WireData {
/**
* Construct the HookEvent and establish default values
*
* @param array $eventData Optional event data to start with
*
*/
public function __construct() {
$this->set('object', null);
$this->set('method', '');
$this->set('arguments', array());
$this->set('return', null);
$this->set('replace', false);
$this->set('options', array());
$this->set('id', '');
$this->set('cancelHooks', false);
public function __construct(array $eventData = array()) {
$data = array(
'object' => null,
'method' => '',
'arguments' => array(),
'return' => null,
'replace' => false,
'options' => array(),
'id' => '',
'cancelHooks' => false
);
if(!empty($eventData)) $data = array_merge($data, $eventData);
$this->data = $data;
}
/**
@@ -201,7 +208,7 @@ class HookEvent extends WireData {
* ~~~~~
*
* @param string|null $hookId
* @return $this
* @return HookEvent|WireData $this
*
*/
public function removeHook($hookId) {
@@ -226,6 +233,5 @@ class HookEvent extends WireData {
return $s;
}
}

View File

@@ -135,7 +135,7 @@ class ImageInspector extends WireData {
if(is_array($additionalInfo) && $parseAppmarker) {
$appmarker = array();
foreach($additionalInfo as $k => $v) {
$appmarker[$k] = substr($v, 0, strpos($v, null));
$appmarker[$k] = substr($v, 0, strpos($v, chr(0)));
}
$this->info['appmarker'] = $appmarker;
if(isset($additionalInfo['APP13'])) {
@@ -158,7 +158,7 @@ class ImageInspector extends WireData {
* Check orientation (@horst)
*
* @param array
* @return bool
* @return array
* @todo there is already a checkOrientation method in ImageSizerEngine - do we need both?
*
*/
@@ -175,7 +175,13 @@ class ImageInspector extends WireData {
'8' => array(90, 0)
);
$result = array('orientation' => 0, 'rotate' => 0, 'flip' => 0);
if(!function_exists('exif_read_data')) return $result;
$supportedExifMimeTypes = array('image/jpeg', 'image/tiff'); // hardcoded by PHP
$mime = isset($this->info['mime']) ? $this->info['mime'] : 'no';
if(!function_exists('exif_read_data') || !in_array($mime, $supportedExifMimeTypes)) {
return $result;
}
$exif = @exif_read_data($filename, 'IFD0');
if(!is_array($exif)
|| !isset($exif['Orientation'])
@@ -228,11 +234,11 @@ class ImageInspector extends WireData {
$i['height'] = $gfh->m_nHeight;
$i['gifversion'] = $gfh->m_lpVer;
$i['animated'] = $gfh->m_bAnimated;
$i['delay'] = $gi->m_nDelay;
$i['trans'] = $gi->m_bTrans;
$i['transcolor'] = $gi->m_nTrans;
$i['delay'] = isset($gi->m_nDelay) ? $gi->m_nDelay : '';
$i['trans'] = isset($gi->m_bTrans) ? $gi->m_bTrans : false;
$i['transcolor'] = isset($gi->m_nTrans) ? $gi->m_nTrans : '';
$i['bgcolor'] = $gfh->m_nBgColor;
$i['numcolors'] = $gfh->m_colorTable->m_nColors;
$i['numcolors'] = isset($gfh->m_colorTable->m_nColors) ? $gfh->m_colorTable->m_nColors : 0;
$i['interlace'] = $gih->m_bInterlace;
$this->info = $i;
unset($gif, $gih, $gfh, $gi, $i);
@@ -273,8 +279,6 @@ class ImageInspector extends WireData {
/**
* parse JPEG Image and collect information into $this->info
*
* @return bool
*
*/
protected function loadImageInfoJpg() {
}

View File

@@ -11,8 +11,10 @@
*
* Other user contributions as noted.
*
* Copyright (C) 2016 by Horst Nogajski and Ryan Cramer
* Copyright (C) 2016-2019 by Horst Nogajski and Ryan Cramer
* This file licensed under Mozilla Public License v2.0 http://mozilla.org/MPL/2.0/
*
* @method bool resize($targetWidth, $targetHeight = 0)
*
*/
class ImageSizer extends Wire {
@@ -53,6 +55,14 @@ class ImageSizer extends Wire {
*/
static protected $knownEngines = null;
/**
* Names of engines that failed the supported() checks
*
* @var array
*
*/
protected $failedEngineNames = array();
/**
* Module/class name of engine that must only be used (for cases where you want to force a specific engine)
*
@@ -82,24 +92,11 @@ class ImageSizer extends Wire {
*
* @param string $filename Filename to resize. Omit only if instantiating class for a getEngines() call.
* @param array $options Initial options to the engine.
* @throws WireException
*
*/
public function __construct($filename = '', $options = array()) {
if(isset($options['forceEngine'])) {
$this->forceEngineName = $options['forceEngine'];
unset($options['forceEngine']);
}
$this->filename = $filename;
$this->initialOptions = $options;
if(strlen($filename)) {
$imageInspector = new ImageInspector($filename);
$this->inspectionResult = $imageInspector->inspect($filename, true);
$this->engine = $this->newImageSizerEngine($filename, $options, $this->inspectionResult);
}
if(!empty($options)) $this->setOptions($options);
if(!empty($filename)) $this->setFilename($filename);
}
/**
@@ -118,15 +115,21 @@ class ImageSizer extends Wire {
self::$knownEngines = array();
$modules = $this->wire('modules');
$engines = $modules->find("className^=ImageSizerEngine");
$engines = $modules->findByPrefix('ImageSizerEngine');
$numEngines = count($engines);
foreach($engines as $module) {
$moduleName = $module->className();
foreach($engines as $moduleName) {
if(!$modules->isInstalled($moduleName)) continue;
if(count($engines) > 1) {
$configData = $modules->getModuleConfigData($moduleName);
if($numEngines > 1) {
$configData = $modules->getConfig($moduleName);
$priority = isset($configData['enginePriority']) ? (int) $configData['enginePriority'] : 0;
while(isset(self::$knownEngines[$priority])) $priority++;
// multiply by 10 to ensure two priority 1 engines don't get mixed up with a priority 2 engine
// for instance, two priority 1 engines become 10 and 11, rather than 1 and 2, as a priority 1
// engine incremented to 2 could otherwise be confused with a priority 2 engine
$priority *= 10;
while(isset(self::$knownEngines[$priority])) {
$priority++;
}
} else {
$priority = 0;
}
@@ -134,9 +137,61 @@ class ImageSizer extends Wire {
}
if(count(self::$knownEngines) > 1) ksort(self::$knownEngines);
self::$knownEngines[] = $this->defaultEngineName;
return self::$knownEngines;
}
/**
* Get array of information for all ImageSizer engines (or optionally a specific ImageSizer engine)
*
* Returns array of arrays indexed by engine name, each with the following:
*
* - `name` (string): engine name
* - `title` (string): engine title
* - `class` (string): PHP class name for engine
* - `summary` (string): Single sentence summary of the engine
* - `author` (string): Authr name (if available)
* - `moduleVersion` (string): Version of the module that powers this engine
* - `libraryVersion` (string): Version of the library that powers this engine
* - `sources` (array): Supported formats for source images it reads (i.e. JPG, JPEG, PNG, PNG24, GIF, GIF87, etc.)
* - `targets` (array): Supported formats for target images it creates (i.e. JPG, PNG, PNG24, WEBP, etc.)
* - `quality` (int): Current quality setting configured with the engine
* - `sharpening` (string): Current sharpening setting configured with the engine
* - `priority` (int): Engine priority (lower is higher priority)
* - `runOrder` (int): Order ImageSizer will try this engine in relative to others (lower runs first), derived from priority.
*
* @param string $name Specify engine name to get info just for that engine or omit to get info for all engines (default)
* @return array Array of arrays indexed by engine name, or if $name specified then just array of info for that engine.
* Returns empty array on error.
* @since 3.0.138
*
*/
public function getEngineInfo($name = '') {
$infos = array();
$engineNames = $name ? array($name) : $this->getEngines();
$prefix = 'ImageSizerEngine';
if($name && stripos($name, $prefix) === 0) {
$name = str_replace($prefix, '', $name);
}
foreach($engineNames as $priority => $engineName) {
$shortName = str_replace($prefix, '', $engineName);
if($name && $shortName !== $name) continue;
$engine = $this->getEngine($engineName);
if(!$engine) continue;
$info = $engine->getEngineInfo();
$info['runOrder'] = $priority;
$infos[$shortName] = $info;
}
// if one engine requested reduce array to just that engine
if($name) $infos = isset($infos[$name]) ? $infos[$name] : array();
return $infos;
}
/**
* Instantiate an ImageSizerEngine
@@ -156,26 +211,49 @@ class ImageSizer extends Wire {
if(empty($inspectionResult) && $filename && is_readable($filename)) {
$imageInspector = new ImageInspector($filename);
$this->wire($imageInspector);
$inspectionResult = $imageInspector->inspect($filename, true);
$this->inspectionResult = $inspectionResult;
}
$engine = null;
$bestFallbackEngine = null; // first engine that was supported but failed webp check
$engineNames = $this->getEngines();
// find first supported engine, according to knownEngines priority
foreach($this->getEngines() as $engineName) {
foreach($engineNames as $engineName) {
if($this->forceEngineName && $engineName != $this->forceEngineName) continue;
$e = $this->wire('modules')->get($engineName);
$e = $this->getEngine($engineName);
if(!$e) continue;
/** @var ImageSizerEngine $e */
$e->prepare($filename, $options, $inspectionResult);
if($e->supported()) {
$supported = $e->supported();
if($supported && !empty($options['webpAdd']) && !$e->supported('webp')) {
// engine does not support requested webp extra image
if(!$bestFallbackEngine) $bestFallbackEngine = $e;
} else if($supported) {
// found supported engine
$engine = $e;
break;
}
}
$this->failedEngineNames[$engineName] = $engineName;
}
if(!$engine) {
// fallback to default
$engine = $this->newDefaultImageSizerEngine($filename, $options, $inspectionResult);
if(!$engine) {
// no engine found
if($bestFallbackEngine) {
// if there is a next best fallback, use it
$engine = $bestFallbackEngine;
} else {
// otherwise fallback to default
$engine = $this->newDefaultImageSizerEngine($filename, $options, $inspectionResult);
}
}
return $engine;
@@ -224,17 +302,8 @@ class ImageSizer extends Wire {
*/
public function ___resize($targetWidth, $targetHeight = 0) {
if(empty($this->filename)) throw new WireException('No file to resize: please call setFilename($file) before resize()');
if(empty($this->engine)) {
// set the engine, and check if the engine is ready to use
$this->engine = $this->newImageSizerEngine();
if(!$this->engine) {
throw new WireException('There seems to be no support for the GD image library on your host?');
}
}
$success = $this->engine->resize($targetWidth, $targetHeight);
$engine = $this->getEngine();
$success = $engine->resize($targetWidth, $targetHeight);
if(!$success) {
// fallback to GD
@@ -295,8 +364,12 @@ class ImageSizer extends Wire {
*
*/
public function setOptions(array $options) {
if(isset($options['forceEngine'])) {
$this->setForceEngine($options['forceEngine']);
unset($options['forceEngine']);
}
$this->initialOptions = array_merge($this->initialOptions, $options);
if($this->engine) $this->engine->setOptions($options);
if($this->engine) $this->engine->setOptions($this->initialOptions);
return $this;
}
@@ -308,11 +381,10 @@ class ImageSizer extends Wire {
*
*/
public function setModified($modified) {
if($this->engine) $this->engine->modified = $modified ? true : false;
if($this->engine) $this->engine->setModified($modified ? true : false);
return $this;
}
// setters (@todo phpdocs)
public function setAutoRotation($value = true) { return $this->setOptions(array('autoRotation', $value)); }
public function setCropExtra($value) { return $this->setOptions(array('cropExtra', $value)); }
public function setCropping($cropping = true) { return $this->setOptions(array('cropping', $cropping)); }
@@ -326,17 +398,61 @@ class ImageSizer extends Wire {
public function setTimeLimit($value = 30) { return $this->setOptions(array('timeLimit', $value)); }
public function setUpscaling($value = true) { return $this->setOptions(array('upscaling', $value)); }
public function setUseUSM($value = true) { return $this->setOptions(array('useUSM', $value)); }
public function getWidth() {
$image = $this->getEngine()->get('image');
return $image['width'];
}
public function getHeight() {
$image = $this->getEngine()->get('image');
return $image['height'];
}
public function getFilename() { return $this->getEngine()->getFilename(); }
public function getExtension() { return $this->getEngine()->getExtension(); }
public function getImageType() { return $this->getEngine()->getImageType(); }
public function isModified() { return $this->getEngine()->getModified(); }
public function getOptions() { return $this->getEngine()->getOptions(); }
public function getFailedEngineNames() { return $this->failedEngineNames; }
// getters (@todo phpdocs)
public function getWidth() { return $this->engine->image['width']; }
public function getHeight() { return $this->engine->image['height']; }
public function getFilename() { return $this->engine->filename; }
public function getExtension() { return $this->engine->extension; }
public function getImageType() { return $this->engine->imageType; }
public function isModified() { return $this->engine->modified; }
public function getOptions() { return $this->engine->getOptions(); }
public function getEngine() { return $this->engine; }
public function __get($key) { return $this->engine->__get($key); }
/**
* Get the current ImageSizerEngine
*
* @param string $engineName Optionally specify a specific engine name to get a new instance of that engine
* When used, returned engine is in an unprepared state (no filename assigned, etc.). Since 3.0.138.
* @return ImageSizerEngine|null Returns ImageSizerEngine or null only if requested $engineName is not found.
* If no $engineName is specified this method may return an existing instance from a previous call.
* @throws WireException
*
*/
public function getEngine($engineName = '') {
if($engineName) {
if($engineName === $this->defaultEngineName) {
$engineClass = __NAMESPACE__ . "\\$engineName";
$engine = $this->wire(new $engineClass());
} else {
$engine = $this->wire('modules')->get($engineName);
}
return $engine;
}
if($this->engine) return $this->engine;
if(empty($this->filename)) {
throw new WireException('No file to process: please call setFilename($file) before calling other methods');
}
$imageInspector = new ImageInspector($this->filename);
$this->inspectionResult = $imageInspector->inspect($this->filename, true);
$this->engine = $this->newImageSizerEngine($this->filename, $this->initialOptions, $this->inspectionResult);
// set the engine, and check if the engine is ready to use
if(!$this->engine) {
throw new WireException('There seems to be no support for the GD image library on your host?');
}
return $this->engine;
}
public function __get($key) { return $this->getEngine()->__get($key); }
/**
* ImageInformation from Image Inspector in short form or full RawInfoData
@@ -346,7 +462,8 @@ class ImageSizer extends Wire {
*
*/
public function getImageInfo($rawData = false) {
$this->getEngine();
if($rawData) return $this->inspectionResult;
$imageType = $this->inspectionResult['info']['imageType'];
$type = '';
@@ -505,16 +622,82 @@ class ImageSizer extends Wire {
*
*/
static public function imageResetIPTC($image) {
$wire = null;
if($image instanceof Pageimage) {
$wire = $image;
$filename = $image->filename;
} else if(is_readable($image)) {
$filename = $image;
} else {
return null;
}
$sizer = new ImageSizerEngineGD($filename);
$sizer = new ImageSizerEngineGD();
if($wire) $wire->wire($sizer);
$result = false !== $sizer->writeBackIPTC($filename) ? true : false;
return $result;
}
/**
* Rotate image by given degrees
*
* @param int $degrees
* @return bool
*
*/
public function rotate($degrees) {
return $this->getEngine()->rotate($degrees);
}
/**
* Flip image vertically
*
* @return bool
*
*/
public function flipVertical() {
return $this->getEngine()->flipVertical();
}
/**
* Flip image horizontally
*
* @return bool
*
*/
public function flipHorizontal() {
return $this->getEngine()->flipHorizontal();
}
/**
* Flip both vertically and horizontally
*
* @return bool
*
*/
public function flipBoth() {
return $this->getEngine()->flipBoth();
}
/**
* Convert image to greyscale (black and white)
*
* @return bool
*
*/
public function convertToGreyscale() {
return $this->getEngine()->convertToGreyscale();
}
/**
* Convert image to sepia tone
*
* @param int $sepia Sepia amount
* @return bool
*
*/
public function convertToSepia($sepia = 55) {
return $this->getEngine()->convertToSepia('', $sepia);
}
}

View File

@@ -3,11 +3,12 @@
/**
* ImageSizer Engine Module (Abstract)
*
* Copyright (C) 2016 by Horst Nogajski and Ryan Cramer
* Copyright (C) 2016-2019 by Horst Nogajski and Ryan Cramer
* This file licensed under Mozilla Public License v2.0 http://mozilla.org/MPL/2.0/
*
* @property bool $autoRotation
* @property bool $upscaling
* @property bool $interlace
* @property array|string|bool $cropping
* @property int $quality
* @property string $sharpening
@@ -17,6 +18,10 @@
* @property string $flip
* @property bool $useUSM
* @property int $enginePriority Priority for use among other ImageSizerEngine modules (0=disabled, 1=first, 2=second, 3=and so on)
* @property bool $webpAdd
* @property int $webpQuality
* @property bool|null $webpResult
* @property bool|null $webpOnly
*
*/
abstract class ImageSizerEngine extends WireData implements Module, ConfigurableModule {
@@ -59,6 +64,46 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
*/
protected $quality = 90;
/**
* WebP Image quality setting, 1..100
*
* @var int
*
*/
protected $webpQuality = 90;
/**
* Also create a WebP Image with this variation?
*
* @var bool
*
*/
protected $webpAdd = false;
/**
* Only create the webp file?
*
* @var bool
*
*/
protected $webpOnly = false;
/**
* webp result (null=not known or not applicable)
*
* @var bool|null
*
*/
protected $webpResult = null;
/**
* Image interlace setting, false or true
*
* @var bool
*
*/
protected $interlace = false;
/**
* Information about the image (width/height)
*
@@ -100,9 +145,11 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
*
* Possible values: northwest, north, northeast, west, center, east, southwest, south, southeast
* or TRUE to crop to center, or FALSE to disable cropping.
* Or array where index 0 is % or px from left, and index 1 is % or px from top. Percent is assumed if
* values are number strings that end with %. Pixels are assumed of values are just integers.
* Default is: TRUE
*
* @var bool
* @var bool|array
*
*/
protected $cropping = true;
@@ -206,7 +253,10 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
'autoRotation',
'upscaling',
'cropping',
'interlace',
'quality',
'webpQuality',
'webpAdd',
'sharpening',
'defaultGamma',
'scale',
@@ -388,6 +438,36 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
*/
abstract protected function processResize($srcFilename, $dstFilename, $fullWidth, $fullHeight, $finalWidth, $finalHeight);
/**
* Process rotate of an image
*
* @param string $srcFilename
* @param string $dstFilename
* @param int $degrees Clockwise degrees, i.e. 90, 180, 270, -90, -180, -270
* @return bool
*
*/
protected function processRotate($srcFilename, $dstFilename, $degrees) {
if($srcFilename && $dstFilename && $degrees) {}
$this->error('rotate not implemented for ' . $this->className());
return false;
}
/**
* Process vertical or horizontal flip of an image
*
* @param string $srcFilename
* @param string $dstFilename
* @param bool $flipVertical True if flip is vertical, false if flip is horizontal
* @return bool
*
*/
protected function processFlip($srcFilename, $dstFilename, $flipVertical) {
if($srcFilename && $dstFilename && $flipVertical) {}
$this->error('flip not implemented for ' . $this->className());
return false;
}
/**
* Get array of image file extensions this ImageSizerModule can process
*
@@ -406,6 +486,64 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
return $this->validSourceImageFormats();
}
/**
* Get an array of image file formats this ImageSizerModule can use as source or target
*
* Unless using the $type argument, returned array contains 'source' and 'target' indexes,
* each an array of image file types/extensions in uppercase.
*
* @param string $type Specify 'source' or 'target' to get just those formats, or omit to get all.
* @return array
* @since 3.0.138
*
*/
public function getSupportedFormats($type = '') {
$a = array(
'source' => $this->validSourceImageFormats(),
'target' => $this->validTargetImageFormats()
);
return $type && isset($a[$type]) ? $a[$type] : $a;
}
/**
* Get array of information about this engine
*
* @return array
* @since 3.0.138
*
*/
public function getEngineInfo() {
$formats = $this->getSupportedFormats();
$moduleName = $this->className();
$className = $this->className(true);
if(is_callable("$className::getModuleInfo")) {
$moduleInfo = $className::getModuleInfo();
} else {
$moduleInfo = $this->wire('modules')->getModuleInfoVerbose($className);
}
if(!is_array($moduleInfo)) $moduleInfo = array();
$info = array(
'name' => str_replace('ImageSizerEngine', '', $moduleName),
'title' => isset($moduleInfo['title']) ? $moduleInfo['title'] : '',
'class' => $moduleName,
'summary' => isset($moduleInfo['summary']) ? $moduleInfo['summary'] : '',
'author' => isset($moduleInfo['author']) ? $moduleInfo['author'] : '',
'moduleVersion' => isset($moduleInfo['version']) ? $moduleInfo['version'] : '',
'libraryVersion' => $this->getLibraryVersion(),
'priority' => $this->enginePriority,
'sources' => $formats['source'],
'targets' => $formats['target'],
'quality' => $this->quality,
'sharpening' => $this->sharpening,
);
return $info;
}
/*************************************************************************************************
* COMMON IMPLEMENTATION METHODS
*
@@ -510,9 +648,9 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
$dest = preg_replace('/\.' . $extension . '$/', '_tmp.' . $extension, $filename);
if(strlen($content) == @file_put_contents($dest, $content, \LOCK_EX)) {
// on success we replace the file
unlink($filename);
rename($dest, $filename);
wireChmod($filename);
$this->wire('files')->unlink($filename);
$this->wire('files')->rename($dest, $filename);
$this->wire('files')->chmod($filename);
return true;
} else {
// it was created a temp diskfile but not with all data in it
@@ -623,28 +761,32 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
$pWidth = $this->getProportionalWidth($targetHeight);
}
if(!$this->upscaling) {
// we are going to shoot for something smaller than the target
// rounding issue fix via @horst-n for #191
if($targetWidth == $originalTargetWidth && 1 + $targetWidth == $pWidth) $pWidth = $pWidth - 1;
if($targetHeight == $originalTargetHeight && 1 + $targetHeight == $pHeight) $pHeight = $pHeight - 1;
while($pWidth > $img['width'] || $pHeight > $img['height']) {
// favor the smallest dimension
if($pWidth > $img['width']) {
$pWidth = $img['width'];
$pHeight = $this->getProportionalHeight($pWidth);
}
if($pHeight > $img['height']) {
$pHeight = $img['height'];
$pWidth = $this->getProportionalWidth($pHeight);
}
if($targetWidth > $pWidth) $targetWidth = $pWidth;
if($targetHeight > $pHeight) $targetHeight = $pHeight;
if(!$this->cropping) {
$targetWidth = $pWidth;
$targetHeight = $pHeight;
}
if(!$this->upscaling && ($img['width'] < $targetWidth || $img['height'] < $targetHeight)) {
// via @horst-n PR #118:
// upscaling is not allowed and we have one or both dimensions to small,
// we scale down the target dimensions to fit within the image dimensions,
// with respect to the target dimensions ratio
$ratioSource = $img['height'] / $img['width'];
$ratioTarget = !$this->cropping ? $ratioSource : $targetHeight / $targetWidth;
if($ratioSource >= $ratioTarget) {
// ratio is equal or target fits into source
$pWidth = $targetWidth = $img['width'];
$pHeight = $img['height'];
$targetHeight = ceil($pWidth * $ratioTarget);
} else {
// target doesn't fit into source
$pHeight = $targetHeight = $img['height'];
$pWidth = $img['width'];
$targetWidth = ceil($pHeight / $ratioTarget);
}
if($this->cropping) {
// we have to disable any sharpening method here,
// as the source will not be resized, only cropped
$this->sharpening = 'none';
}
}
@@ -699,10 +841,29 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
$cropping = strtolower($cropping);
if(strpos($cropping, ',')) {
$cropping = explode(',', $cropping);
if(strpos($cropping[0], '%') !== false) $cropping[0] = round(min(100, max(0, $cropping[0]))) . '%';
else $cropping[0] = (int) $cropping[0];
if(strpos($cropping[1], '%') !== false) $cropping[1] = round(min(100, max(0, $cropping[1]))) . '%';
else $cropping[1] = (int) $cropping[1];
} else if(strpos($cropping, 'x') && preg_match('/^([pd])(\d+)x(\d+)(z\d+)?/', $cropping, $matches)) {
$cropping = array(0 => $matches[1], 1 => $matches[2]);
if(isset($matches[3])) $cropping[2] = (int) $matches[3];
if($matches[1] == 'p') {
$cropping[0] .= '%';
$cropping[0] .= '%';
}
}
}
if(is_array($cropping)) {
if(strpos($cropping[0], '%') !== false) {
$cropping[0] = round(min(100, max(0, $cropping[0]))) . '%';
} else {
$cropping[0] = (int) $cropping[0];
}
if(strpos($cropping[1], '%') !== false) {
$cropping[1] = round(min(100, max(0, $cropping[1]))) . '%';
} else {
$cropping[1] = (int) $cropping[1];
}
if(isset($cropping[2])) { // zoom
$cropping[2] = (int) $cropping[2];
if($cropping[2] < 2 || $cropping[2] > 99) unset($cropping[2]);
}
}
@@ -732,8 +893,12 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
// crop name if custom center point is specified
if(is_array($cropping)) {
// p = percent, d = pixel dimension
$cropping = (strpos($cropping[0], '%') !== false ? 'p' : 'd') . ((int) $cropping[0]) . 'x' . ((int) $cropping[1]);
// p = percent, d = pixel dimension, z = zoom
$zoom = isset($cropping[2]) ? (int) $cropping[2] : 0;
$cropping =
(strpos($cropping[0], '%') !== false ? 'p' : 'd') .
((int) $cropping[0]) . 'x' . ((int) $cropping[1]);
if($zoom > 1 && $zoom < 100) $cropping .= "z$zoom";
}
// if crop is TRUE or FALSE, we don't reflect that in the filename, so make it blank
@@ -750,7 +915,7 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
* disable cropping, specify boolean false. To enable cropping with default (center), you may also specify
* boolean true.
*
* @return $this
* @return self
*
*/
public function setCropping($cropping = true) {
@@ -765,7 +930,7 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
*
* @param array $value containing 4 params (x y w h) indexed or associative
*
* @return $this
* @return self
* @throws WireException when given invalid value
*
*/
@@ -790,11 +955,13 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
if(isset($value[$v])) $$v = $value[$v];
}
}
foreach(array('x', 'y', 'w', 'h') as $k) {
$v = isset($$k) ? $$k : -1;
if(!is_int($v) || $v < 0) throw new WireException("Missing or wrong param $k for ImageSizer-cropExtra!");
if(('w' == $k || 'h' == $k) && 0 == $v) throw new WireException("Wrong param $k for ImageSizer-cropExtra!");
$v = (int) (isset($$k) ? $$k : -1);
if(!$v && $k == 'w' && $h > 0) $v = $this->getProportionalWidth((int) $h);
if(!$v && $k == 'h' && $w > 0) $v = $this->getProportionalHeight((int) $w);
if($v < 0) throw new WireException("Missing or wrong param $k=$v for ImageSizer-cropExtra! " . print_r($value, true));
if(('w' == $k || 'h' == $k) && 0 == $v) throw new WireException("Wrong param $k=$v for ImageSizer-cropExtra! " . print_r($value, true));
}
$this->cropExtra = array($x, $y, $w, $h);
@@ -807,14 +974,51 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
*
* @param int $n
*
* @return $this
* @return self
*
*/
public function setQuality($n) {
$n = (int) $n;
if($n < 1) $n = 1;
if($n > 100) $n = 100;
$this->quality = (int) $n;
$this->quality = $this->getIntegerValue($n, 1, 100);
return $this;
}
/**
* Set the image quality 1-100 for WebP output, where 100 is highest quality
*
* @param int $n
*
* @return self
*
*/
public function setWebpQuality($n) {
$this->webpQuality = $this->getIntegerValue($n, 1, 100);
return $this;
}
/**
* Set flag to also create a webp file or not
*
* @param bool $webpAdd
* @param bool|null $webpOnly
* @return self
*
*/
public function setWebpAdd($webpAdd, $webpOnly = null) {
$this->webpAdd = (bool) $webpAdd;
if(is_bool($webpOnly)) $this->webpOnly = $webpOnly;
return $this;
}
/**
* Set flag to only create a webp file
*
* @param bool value$
* @return self
*
*/
public function setWebpOnly($value) {
$this->webpOnly = (bool) $value;
if($this->webpOnly) $this->webpAdd = true; // webpAdd required for webpOnly
return $this;
}
@@ -857,7 +1061,7 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
*
* @param mixed $value
*
* @return $this
* @return self
* @throws WireException
*
*/
@@ -886,7 +1090,7 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
*
* @param bool $value Whether to auto-rotate or not (default = true)
*
* @return $this
* @return self
*
*/
public function setAutoRotation($value = true) {
@@ -899,20 +1103,33 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
*
* @param bool $value Whether to upscale or not (default = true)
*
* @return $this
* @return self
*
*/
public function setUpscaling($value = true) {
$this->upscaling = $this->getBooleanValue($value);
return $this;
}
/**
* Turn on/off interlace
*
* @param bool $value Whether to upscale or not (default = true)
*
* @return self
*
*/
public function setInterlace($value = true) {
$this->interlace = $this->getBooleanValue($value);
return $this;
}
/**
* Set default gamma value: 0.5 - 4.0 | -1
*
* @param float|int $value 0.5 to 4.0 or -1 to disable
*
* @return $this
* @return self
* @throws WireException when given invalid value
*
*/
@@ -932,7 +1149,7 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
*
* @param int $value 10 to 60 recommended, default is 30
*
* @return $this
* @return self
*
*/
public function setTimeLimit($value = 30) {
@@ -958,7 +1175,7 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
*
* @param float $scale
*
* @return $this
* @return self
*
*/
public function setScale($scale) {
@@ -973,7 +1190,7 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
*
* @param bool $hidpi True or false (default=true)
*
* @return $this
* @return self
*
*/
public function setHidpi($hidpi = true) {
@@ -987,7 +1204,7 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
*
* @param $degrees
*
* @return $this
* @return self
*
*/
public function setRotate($degrees) {
@@ -1005,7 +1222,7 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
*
* @param $flip
*
* @return $this
* @return self
*
*/
public function setFlip($flip) {
@@ -1019,7 +1236,7 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
*
* @param bool $value Whether to USM is used or not (default = true)
*
* @return $this
* @return self
*
*/
public function setUseUSM($value = true) {
@@ -1032,6 +1249,7 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
*
* @param array $options May contain the following (show with default values):
* 'quality' => 90,
* 'webpQuality' => 90,
* 'cropping' => true,
* 'upscaling' => true,
* 'autoRotation' => true,
@@ -1041,7 +1259,7 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
* 'rotate' => 0 (90, 180, 270 or negative versions of those)
* 'flip' => '', (vertical|horizontal)
*
* @return $this
* @return self
*
*/
public function setOptions(array $options) {
@@ -1055,12 +1273,24 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
case 'upscaling':
$this->setUpscaling($value);
break;
case 'interlace':
$this->setInterlace($value);
break;
case 'sharpening':
$this->setSharpening($value);
break;
case 'quality':
$this->setQuality($value);
break;
case 'webpQuality':
$this->setWebpQuality($value);
break;
case 'webpAdd':
$this->setWebpAdd($value);
break;
case 'webpOnly':
$this->webpOnly = (bool) $value;
break;
case 'cropping':
$this->setCropping($value);
break;
@@ -1083,7 +1313,7 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
$this->setFlip($value);
break;
case 'useUSM':
$this->setUseUsm($value);
$this->setUseUSM($value);
break;
default:
@@ -1109,6 +1339,25 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
if(in_array(strtolower($value), array('0', 'off', 'false', 'no', 'n', 'none'))) return false;
return ((int) $value) > 0;
}
/**
* Get integer value within given range
*
* @param int $n Number to require in given range
* @param int $min Minimum allowed number
* @param int $max Maximum allowed number
* @return int
*
*/
protected function getIntegerValue($n, $min, $max) {
$n = (int) $n;
if($n < $min) {
$n = $min;
} else if($n > $max) {
$n = $max;
}
return $n;
}
/**
* Return an array of the current options
@@ -1120,8 +1369,12 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
$options = array(
'quality' => $this->quality,
'webpQuality' => $this->webpQuality,
'webpAdd' => $this->webpAdd,
'webpOnly' => $this->webpOnly,
'cropping' => $this->cropping,
'upscaling' => $this->upscaling,
'interlace' => $this->interlace,
'autoRotation' => $this->autoRotation,
'sharpening' => $this->sharpening,
'defaultGamma' => $this->defaultGamma,
@@ -1159,6 +1412,8 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
'options'
);
if($key === 'webpResult') return $this->webpResult;
if($key === 'webpOnly') return $this->webpOnly;
if(in_array($key, $keys)) return $this->$key;
if(in_array($key, $this->optionNames)) return $this->$key;
if(isset($this->options[$key])) return $this->options[$key];
@@ -1299,7 +1554,7 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
*
* @param bool $modified
*
* @return $this
* @return self
*
*/
public function setModified($modified) {
@@ -1307,6 +1562,16 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
return $this;
}
/**
* Get whether the image was modified
*
* @return bool
*
*/
public function getModified() {
return $this->modified;
}
/**
* Check if cropping is needed, if yes, populate x- and y-position to params $w1 and $h1
*
@@ -1323,7 +1588,7 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
protected function getCropDimensions(&$w1, &$h1, $gdWidth, $targetWidth, $gdHeight, $targetHeight) {
if(is_string($this->cropping)) {
// calculate from 8 named cropping points
switch($this->cropping) {
case 'nw':
$w1 = 0;
@@ -1357,20 +1622,45 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
}
} else if(is_array($this->cropping)) {
// calculate from specific percent or pixels from left and top
// $this->cropping is an array with the following:
// index 0 represents % or pixels from left
// index 1 represents % or pixels from top
// @interrobang + @u-nikos
if(strpos($this->cropping[0], '%') === false) $pointX = (int) $this->cropping[0];
else $pointX = $gdWidth * ((int) $this->cropping[0] / 100);
if(strpos($this->cropping[0], '%') === false) {
$pointX = (int) $this->cropping[0];
} else {
$pointX = $gdWidth * ((int) $this->cropping[0] / 100);
}
if(strpos($this->cropping[1], '%') === false) $pointY = (int) $this->cropping[1];
else $pointY = $gdHeight * ((int) $this->cropping[1] / 100);
if(strpos($this->cropping[1], '%') === false) {
$pointY = (int) $this->cropping[1];
} else {
$pointY = $gdHeight * ((int) $this->cropping[1] / 100);
}
/*
if(isset($this->cropping[2]) && $this->cropping[2] > 1) {
// zoom percent (2-100)
$zoom = (int) $this->cropping[2];
}
*/
if($pointX < $targetWidth / 2) $w1 = 0;
else if($pointX > ($gdWidth - $targetWidth / 2)) $w1 = $gdWidth - $targetWidth;
else $w1 = $pointX - $targetWidth / 2;
if($pointX < $targetWidth / 2) {
$w1 = 0;
} else if($pointX > ($gdWidth - $targetWidth / 2)) {
$w1 = $gdWidth - $targetWidth;
} else {
$w1 = $pointX - $targetWidth / 2;
}
if($pointY < $targetHeight / 2) $h1 = 0;
else if($pointY > ($gdHeight - $targetHeight / 2)) $h1 = $gdHeight - $targetHeight;
else $h1 = $pointY - $targetHeight / 2;
if($pointY < $targetHeight / 2) {
$h1 = 0;
} else if($pointY > ($gdHeight - $targetHeight / 2)) {
$h1 = $gdHeight - $targetHeight;
} else {
$h1 = $pointY - $targetHeight / 2;
}
}
}
@@ -1406,8 +1696,6 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
$this->fullHeight = $this->image['height'];
if(0 == $this->finalWidth && 0 == $this->finalHeight) return false;
if(0 == $this->finalWidth) $this->finalWidth = ceil(($this->finalHeight / $this->fullHeight) * $this->fullWidth);
if(0 == $this->finalHeight) $this->finalHeight = ceil(($this->finalWidth / $this->fullWidth) * $this->fullHeight);
if($this->scale !== 1.0) { // adjust for hidpi
if($this->finalWidth) $this->finalWidth = ceil($this->finalWidth * $this->scale);
@@ -1427,19 +1715,135 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
return false; // fallback or failed
}
// all went well, copy back the temp file, remove the temp file
if(!@copy($this->tmpFile, $this->filename)) return false; // fallback or failed
wireChmod($this->filename);
@unlink($this->tmpFile);
// post processing: IPTC, setModified and reload ImageInfo
$this->writeBackIPTC($this->filename, false);
if($this->webpOnly) {
$this->wire('files')->unlink($this->tmpFile);
} else {
// all went well, copy back the temp file,
if(!@copy($this->tmpFile, $this->filename)) return false; // fallback or failed
$this->wire('files')->chmod($this->filename);
// remove the temp file
$this->wire('files')->unlink($this->tmpFile);
// post processing: IPTC, setModified and reload ImageInfo
$this->writeBackIPTC($this->filename, false);
}
$this->setModified($this->modified);
$this->loadImageInfo($this->filename, true);
return true;
}
/**
* Just rotate image by number of degrees
*
* @param int $degrees
* @param string $dstFilename Optional destination filename. If not present, source will be overwritten.
* @return bool True on success, false on fail
*
*/
public function rotate($degrees, $dstFilename = '') {
$degrees = (int) $degrees;
$srcFilename = $this->filename;
if(empty($dstFilename)) $dstFilename = $srcFilename;
if($degrees > 360) $degrees = 360 - $degrees;
if($degrees < -360) $degrees = $degrees - 360;
if($degrees == 0 || $degrees == 360 || $degrees == -360) {
if($dstFilename != $this->filename) wireCopy($this->filename, $dstFilename);
return true;
}
if($srcFilename == $dstFilename) {
// src and dest are the same, so use a temporary file
$n = 1;
do {
$tmpFilename = dirname($dstFilename) . "/.ise$n-" . basename($dstFilename);
} while(file_exists($tmpFilename) && $n++);
} else {
// src and dest are different files
$tmpFilename = $dstFilename;
}
$result = $this->processRotate($srcFilename, $tmpFilename, $degrees);
if($result) {
// success
if($tmpFilename != $dstFilename) {
if(is_file($dstFilename)) $this->wire('files')->unlink($dstFilename);
$this->wire('files')->rename($tmpFilename, $dstFilename);
}
$this->wire('files')->chmod($dstFilename);
} else {
// fail
if(is_file($tmpFilename)) $this->wire('files')->unlink($tmpFilename);
}
return $result;
}
/**
* Flip vertically
*
* @param string $dstFilename
* @return bool
*
*/
public function flipVertical($dstFilename = '') {
if(empty($dstFilename)) $dstFilename = $this->filename;
return $this->processFlip($this->filename, $dstFilename, 'vertical');
}
/**
* Flip horizontally
*
* @param string $dstFilename
* @return bool
*
*/
public function flipHorizontal($dstFilename = '') {
if(empty($dstFilename)) $dstFilename = $this->filename;
return $this->processFlip($this->filename, $dstFilename, 'horizontal');
}
/**
* Flip both vertically and horizontally
*
* @param string $dstFilename
* @return bool
*
*/
public function flipBoth($dstFilename = '') {
if(empty($dstFilename)) $dstFilename = $this->filename;
return $this->processFlip($this->filename, $dstFilename, 'both');
}
/**
* Convert image to greyscale
*
* @param string $dstFilename If different from source file
* @return bool
*
*/
public function convertToGreyscale($dstFilename = '') {
if($dstFilename) {}
return false;
}
/**
* Convert image to sepia
*
* @param string $dstFilename If different from source file
* @param float|int $sepia Sepia value
* @return bool
*
*/
public function convertToSepia($dstFilename = '', $sepia = 55) {
if($dstFilename && $sepia) {}
return false;
}
/**
* Get an integer representing the resize method to use
*
@@ -1473,6 +1877,121 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
return 4;
}
/**
* Helper function to perform a cropExtra / cropBefore cropping
*
* Intended for use by the getFocusZoomCropDimensions() method
*
* @param string $focus (focus point in percent, like: 54.7%)
* @param int $sourceDimension (source image width or height)
* @param int $cropDimension (target crop-image width or height)
* @param int $zoom
*
* @return int $position (crop position x or y in pixel)
*
*/
protected function getFocusZoomPosition($focus, $sourceDimension, $cropDimension, $zoom) {
$focus = intval($focus); // string with float value and percent char, (needs to be converted to integer)
$scale = 1 + (($zoom / 100) * 2);
$focusPX = ($sourceDimension / 100 * $focus);
$posMinPX = $cropDimension / 2 / $scale;
$posMaxPX = $sourceDimension - ($cropDimension / 2);
// calculate the position in pixel !
if($focusPX >= $posMaxPX) {
$posPX = $sourceDimension - $cropDimension;
} else if($focusPX <= $posMinPX) {
$posPX = 0;
} else {
$posPX = $focusPX - ($cropDimension / 2);
if(0 > $posPX) $posPX = 0;
}
return $posPX;
}
/**
* Get an array of the 4 dimensions necessary to perform a cropExtra / cropBefore cropping
*
* Intended for use by the resize() method
*
* @param int $zoom
* @param int $fullWidth
* @param int $fullHeight
* @param int $finalWidth
* @param int $finalHeight
* @return array
*
*/
protected function getFocusZoomCropDimensions($zoom, $fullWidth, $fullHeight, $finalWidth, $finalHeight) {
// validate & calculate / prepare params
$zoom = $zoom <= 70 ? $zoom : 70; // validate / correct the zoom value, it needs to be between 2 and 70
$zoom = $zoom >= 2 ? $zoom : 2;
// calculate the max crop dimensions
$ratioFinal = $finalWidth / $finalHeight; // get the ratio of the requested crop
$percentW = $finalWidth / $fullWidth * 100; // calculate percentage of the crop width in regard of the original width
$percentH = $finalHeight / $fullHeight * 100; // calculate percentage of the crop height in regard of the original height
if($percentW >= $percentH) { // check wich one is greater
$maxW = $fullWidth; // if percentW is greater, maxW becomes the original Width
$maxH = $fullWidth / $ratioFinal; // ... and maxH gets calculated via the ratio
} else {
$maxH = $fullHeight; // if percentH is greater, maxH becomes the original Height
$maxW = $fullHeight * $ratioFinal; // ... and maxW gets calculated via the ratio
}
// calculate the zoomed dimensions
$cropW = $maxW - ($maxW * $zoom / 100); // to get the final crop Width and Height, the amount for zoom-in
$cropH = $maxH - ($maxH * $zoom / 100); // needs to get stripped out
// validate against the minimal dimensions
if(!$this->upscaling) { // if upscaling isn't allowed, we decrease the zoom, so that we get a crop with the min-Dimensions
if($cropW < $finalWidth) {
$cropW = $finalWidth;
$cropH = $finalWidth / $ratioFinal;
}
if($cropH < $finalHeight) {
$cropH = $finalHeight;
$cropW = $finalHeight * $ratioFinal;
}
}
// calculate the crop positions
$posX = $this->getFocusZoomPosition($this->cropping[0], $fullWidth, $cropW, $zoom); // calculate the x-position
$posY = $this->getFocusZoomPosition($this->cropping[1], $fullHeight, $cropH, $zoom); // calculate the y-position
return array(
0 => (int) $posX,
1 => (int) $posY,
2 => (int) $cropW,
3 => (int) $cropH
);
}
/**
* Get current zoom percentage setting or 0 if not set
*
* Value is determined from the $this->cropping array index 2 and is used only if index 0 and
* index 1 are percentages (and indicated as such with a percent sign).
*
* @return int
*
*/
protected function getFocusZoomPercent() {
// check if we have to proceed a zoomed focal point cropping,
// therefore we need index 0 and 1 to be strings with '%' sign included
// and index 2 to be an integer between 2 and 70
$a = $this->cropping;
if(is_array($a) && isset($a[2]) && strpos($a[0], '%') !== false && strpos($a[1], '%') !== false) {
$zoom = (int) $a[2];
if($zoom < 2) $zoom = 0;
if($zoom > 70) $zoom = 70;
} else {
$zoom = 0;
}
return $zoom;
}
/**
* Module info: not-autoload
*
@@ -1522,6 +2041,17 @@ abstract class ImageSizerEngine extends WireData implements Module, Configurable
return $this->moduleConfigData;
}
/**
* Get library version string
*
* @return string Returns version string or blank string if not applicable/available
* @since 3.0.138
*
*/
public function getLibraryVersion() {
return '';
}
/**
* Module configuration
*

View File

@@ -8,14 +8,25 @@
*
* Other user contributions as noted.
*
* Copyright (C) 2016 by Horst Nogajski and Ryan Cramer
* Copyright (C) 2016-2019 by Horst Nogajski and Ryan Cramer
* This file licensed under Mozilla Public License v2.0 http://mozilla.org/MPL/2.0/
*
* https://processwire.com
*
* @method bool imSaveReady($im, $filename)
*
*/
class ImageSizerEngineGD extends ImageSizerEngine {
public static function getModuleInfo() {
return array(
'title' => 'GD Image Sizer',
'version' => 1,
'summary' => "Uses PHPs built-in GD library to resize images.",
'author' => 'Horst Nogajski',
);
}
/**
* @var string
*
@@ -34,6 +45,14 @@ class ImageSizerEngineGD extends ImageSizerEngine {
*/
protected $gammaLinearized;
/**
* Webp support available?
*
* @var bool|null
*
*/
static protected $webpSupport = null;
/**
* Get formats GD and resize
*
@@ -44,6 +63,30 @@ class ImageSizerEngineGD extends ImageSizerEngine {
return array('JPG', 'JPEG', 'PNG', 'GIF');
}
/**
* Get an array of image file extensions this ImageSizerModule can create
*
* @return array of uppercase file extensions, i.e. ['PNG', 'JPG']
*
*/
protected function validTargetImageFormats() {
$formats = $this->validSourceImageFormats();
if($this->supported('webp')) $formats[] = 'WEBP';
return $formats;
}
/**
* Get library version string
*
* @return string Returns version string or blank string if not applicable/available
* @since 3.0.138
*
*/
public function getLibraryVersion() {
$gd = gd_info();
return isset($gd['GD Version']) ? $gd['GD Version'] : '';
}
/**
* Return whether or not GD can proceed - Is the current image(sub)format supported?
*
@@ -57,6 +100,7 @@ class ImageSizerEngineGD extends ImageSizerEngine {
// and if it passes the mandatory requirements, we check particularly aspects here
switch($action) {
case 'imageformat':
// compare current imagefile infos fetched from ImageInspector
$requested = $this->getImageInfo(false);
@@ -69,7 +113,16 @@ class ImageSizerEngineGD extends ImageSizerEngine {
return true;
}
break;
case 'webp':
if(self::$webpSupport === null) {
// only call it once
$gd = gd_info();
self::$webpSupport = isset($gd['WebP Support']) ? $gd['WebP Support'] : false;
}
return self::$webpSupport;
break;
case 'install':
/*
$gd = gd_info();
@@ -77,6 +130,7 @@ class ImageSizerEngineGD extends ImageSizerEngine {
$png = isset($gd['PNG Support']) ? $gd['PNG Support'] : false;
$gif = isset($gd['GIF Read Support']) && isset($gd['GIF Create Support']) ? $gd['GIF Create Support'] : false;
$freetype = isset($gd['FreeType Support']) ? $gd['FreeType Support'] : false;
$webp = isset($gd['WebP Support']) ? $gd['WebP Support'] : false;
$this->config->gdReady = true;
*/
return true;
@@ -102,11 +156,12 @@ class ImageSizerEngineGD extends ImageSizerEngine {
protected function processResize($srcFilename, $dstFilename, $fullWidth, $fullHeight, $finalWidth, $finalHeight) {
$this->modified = false;
$isModified = false;
if(isset($this->info['bits'])) $this->imageDepth = $this->info['bits'];
$this->imageFormat = strtoupper(str_replace('image/', '', $this->info['mime']));
if(!in_array($this->imageFormat, $this->validSourceImageFormats())) {
throw new WireException(sprintf($this->_("loaded file '%s' is not in the list of valid images", basename($dstFilename))));
throw new WireException(sprintf($this->_("loaded file '%s' is not in the list of valid images"), basename($dstFilename)));
}
$image = null;
@@ -141,6 +196,7 @@ class ImageSizerEngineGD extends ImageSizerEngine {
if($this->rotate || $needRotation) { // @horst
$degrees = $this->rotate ? $this->rotate : $orientations[0];
$image = $this->imRotate($image, $degrees);
$isModified = true;
if(abs($degrees) == 90 || abs($degrees) == 270) {
// we have to swap width & height now!
$tmp = array($this->getWidth(), $this->getHeight());
@@ -155,7 +211,17 @@ class ImageSizerEngineGD extends ImageSizerEngine {
} else if($orientations[1] > 0) {
$vertical = $orientations[1] == 2;
}
if(!is_null($vertical)) $image = $this->imFlip($image, $vertical);
if(!is_null($vertical)) {
$image = $this->imFlip($image, $vertical);
$isModified = true;
}
}
$zoom = $this->getFocusZoomPercent();
if($zoom > 1) {
// we need to configure a cropExtra call to respect the zoom factor
$this->cropExtra = $this->getFocusZoomCropDimensions($zoom, $fullWidth, $fullHeight, $finalWidth, $finalHeight);
$this->cropping = false;
}
// if there is requested to crop _before_ resize, we do it here @horst
@@ -182,9 +248,11 @@ class ImageSizerEngineGD extends ImageSizerEngine {
$this->prepareImageLayer($image, $imageTemp);
imagecopy($image, $imageTemp, 0, 0, $x, $y, $w, $h);
unset($x, $y, $w, $h);
$isModified = true;
// now release the intermediate image and update settings
imagedestroy($imageTemp);
$imageTemp = null;
$this->setImageInfo(imagesx($image), imagesy($image));
// $this->cropping = false; // ?? set this to prevent overhead with the following manipulation ??
}
@@ -202,12 +270,20 @@ class ImageSizerEngineGD extends ImageSizerEngine {
// this is the case if the original size is requested or a greater size but upscaling is set to false
// the current version is allready the desired result, we only may have to apply compression where possible
$this->sharpening = 'none'; // we set sharpening to none
// current version is already the desired result, we only may have to compress JPEGs but leave GIF and PNG as is:
if(!$isModified && !$this->webpOnly && ($this->imageType == \IMAGETYPE_PNG || $this->imageType == \IMAGETYPE_GIF)) {
$result = @copy($srcFilename, $dstFilename);
if(isset($image) && is_resource($image)) @imagedestroy($image); // clean up
if(isset($image)) $image = null;
return $result; // early return !
}
// process JPEGs
if(self::checkMemoryForImage(array(imagesx($image), imagesy($image), 3)) === false) {
throw new WireException(basename($srcFilename) . " - not enough memory to copy the final image");
}
$this->sharpening = 'none'; // we set sharpening to none, as the image only gets compressed, but not resized
$thumb = imagecreatetruecolor(imagesx($image), imagesy($image)); // create the final memory image
$this->prepareImageLayer($thumb, $image);
imagecopy($thumb, $image, 0, 0, 0, 0, imagesx($image), imagesy($image)); // copy our intermediate image into the final one
@@ -224,17 +300,33 @@ class ImageSizerEngineGD extends ImageSizerEngine {
$this->prepareImageLayer($thumb, $image);
imagecopyresampled($thumb, $image, 0, 0, 0, 0, $finalWidth, $finalHeight, $this->image['width'], $this->image['height']);
} else if(4 == $resizeMethod) { // 4 = resize and crop with aspect ratio
} else if(4 == $resizeMethod) { // 4 = resize and crop with aspect ratio, - or crop without resizing ($upscaling == false)
// we have to scale up or down and to _crop_
if(self::checkMemoryForImage(array($bgWidth, $bgHeight, 3)) === false) {
throw new WireException(basename($srcFilename) . " - not enough memory to resize to the intermediate image");
}
$sourceX = 0;
$sourceY = 0;
$sourceWidth = $this->image['width'];
$sourceHeight = $this->image['height'];
$thumb2 = imagecreatetruecolor($bgWidth, $bgHeight);
$this->prepareImageLayer($thumb2, $image);
imagecopyresampled($thumb2, $image, 0, 0, 0, 0, $bgWidth, $bgHeight, $this->image['width'], $this->image['height']);
imagecopyresampled(
$thumb2, // destination image
$image, // source image
0, // destination X
0, // destination Y
$sourceX, // source X
$sourceY, // source Y
$bgWidth, // destination width
$bgHeight, // destination height
$sourceWidth, // source width
$sourceHeight // source height
);
if(self::checkMemoryForImage(array($finalWidth, $finalHeight, 3)) === false) {
throw new WireException(basename($srcFilename) . " - not enough memory to crop to the final image");
@@ -242,7 +334,18 @@ class ImageSizerEngineGD extends ImageSizerEngine {
$thumb = imagecreatetruecolor($finalWidth, $finalHeight);
$this->prepareImageLayer($thumb, $image);
imagecopyresampled($thumb, $thumb2, 0, 0, $bgX, $bgY, $finalWidth, $finalHeight, $finalWidth, $finalHeight);
imagecopyresampled(
$thumb, // destination image
$thumb2, // source image
0, // destination X
0, // destination Y
$bgX, // source X
$bgY, // source Y
$finalWidth, // destination width
$finalHeight, // destination height
$finalWidth, // source width
$finalHeight // source height
);
imagedestroy($thumb2);
}
@@ -273,33 +376,91 @@ class ImageSizerEngineGD extends ImageSizerEngine {
}
}
// write to file
$result = false;
// write to file(s)
if(file_exists($dstFilename)) $this->wire('files')->unlink($dstFilename);
$result = null; // null=not yet known
switch($this->imageType) {
case \IMAGETYPE_GIF:
// correct gamma from linearized 1.0 back to 2.0
$this->gammaCorrection($thumb, false);
$result = imagegif($thumb, $dstFilename);
// save the final GIF image file
if($this->imSaveReady($thumb, $srcFilename)) $result = imagegif($thumb, $dstFilename);
break;
case \IMAGETYPE_PNG:
// optionally correct gamma from linearized 1.0 back to 2.0
if(!$this->hasAlphaChannel()) $this->gammaCorrection($thumb, false);
// always use highest compression level for PNG (9) per @horst
$result = imagepng($thumb, $dstFilename, 9);
// save the final PNG image file and always use highest compression level (9) per @horst
if($this->imSaveReady($thumb, $srcFilename)) $result = imagepng($thumb, $dstFilename, 9);
break;
case \IMAGETYPE_JPEG:
// correct gamma from linearized 1.0 back to 2.0
$this->gammaCorrection($thumb, false);
$result = imagejpeg($thumb, $dstFilename, $this->quality);
if($this->imSaveReady($thumb, $srcFilename)) {
// optionally apply interlace bit to the final image. this will result in progressive JPEGs
if($this->interlace) {
if(0 == imageinterlace($thumb, 1)) {
// log that setting the interlace bit has failed ?
// ...
}
}
// save the final JPEG image file
$result = imagejpeg($thumb, $dstFilename, $this->quality);
}
break;
default:
$result = false;
}
// release the last GD image object
if(isset($thumb) && is_resource($thumb)) @imagedestroy($thumb);
if(isset($thumb)) $thumb = null;
if($result === null) $result = $this->webpResult; // if webpOnly option used
return $result;
}
/**
* Called before saving of image, returns true if save should proceed, false if not
*
* Also Creates a webp file when settings indicate it should.
*
* @param resource $im
* @param string $filename Source filename
* @return bool
*
*/
protected function ___imSaveReady($im, $filename) {
if($this->webpOnly || $this->webpAdd) {
$this->webpResult = $this->imSaveWebP($im, $filename, $this->webpQuality);
}
return $this->webpOnly ? false : true;
}
/**
* Create WebP image (@horst)
* Is requested by image options: ["webpAdd" => true] OR ["webpOnly" => true]
*
* @param resource $im
* @param string $filename
* @param int $quality
*
* @return boolean true | false
*
*/
protected function imSaveWebP($im, $filename, $quality = 90) {
if(!function_exists('imagewebp')) return false;
$path_parts = pathinfo($filename);
$webpFilename = $path_parts['dirname'] . '/' . $path_parts['filename'] . '.webp';
if(file_exists($webpFilename)) $this->wire('files')->unlink($webpFilename);
return imagewebp($im, $webpFilename, $quality);
}
/**
* Rotate image (@horst)
*
@@ -313,7 +474,8 @@ class ImageSizerEngineGD extends ImageSizerEngine {
$degree = (is_float($degree) || is_int($degree)) && $degree > -361 && $degree < 361 ? $degree : false;
if($degree === false) return $im;
if(in_array($degree, array(-360, 0, 360))) return $im;
return @imagerotate($im, $degree, imagecolorallocate($im, 0, 0, 0));
$angle = 360 - $degree; // because imagerotate() expects counterclockwise angle rather than degrees
return @imagerotate($im, $angle, imagecolorallocate($im, 0, 0, 0));
}
/**
@@ -397,7 +559,7 @@ class ImageSizerEngineGD extends ImageSizerEngine {
$amount = intval($amount / 100 * $this->usmValue);
// apply unsharp mask filter
return $this->UnsharpMask($im, $amount, $radius, $threshold);
return $this->unsharpMask($im, $amount, $radius, $threshold);
}
// if we do not use USM, we use our default sharpening method,
@@ -451,7 +613,7 @@ class ImageSizerEngineGD extends ImageSizerEngine {
* with mode = true it linearizes an image to 1
* with mode = false it set it back to the originating gamma value
*
* @param GD -image-resource $image
* @param resource $image
* @param bool $mode
*
*/
@@ -541,12 +703,12 @@ class ImageSizerEngineGD extends ImageSizerEngine {
for($x = 0; $x < $w - 1; $x++) { // each row
for($y = 0; $y < $h; $y++) { // each pixel
$rgbOrig = ImageColorAt($img, $x, $y);
$rgbOrig = imagecolorat($img, $x, $y);
$rOrig = (($rgbOrig >> 16) & 0xFF);
$gOrig = (($rgbOrig >> 8) & 0xFF);
$bOrig = ($rgbOrig & 0xFF);
$rgbBlur = ImageColorAt($imgBlur, $x, $y);
$rgbBlur = imagecolorat($imgBlur, $x, $y);
$rBlur = (($rgbBlur >> 16) & 0xFF);
$gBlur = (($rgbBlur >> 8) & 0xFF);
@@ -565,20 +727,20 @@ class ImageSizerEngineGD extends ImageSizerEngine {
: $bOrig;
if(($rOrig != $rNew) || ($gOrig != $gNew) || ($bOrig != $bNew)) {
$pixCol = ImageColorAllocate($img, $rNew, $gNew, $bNew);
ImageSetPixel($img, $x, $y, $pixCol);
$pixCol = imagecolorallocate($img, $rNew, $gNew, $bNew);
imagesetpixel($img, $x, $y, $pixCol);
}
}
}
} else {
for($x = 0; $x < $w; $x++) { // each row
for($y = 0; $y < $h; $y++) { // each pixel
$rgbOrig = ImageColorAt($img, $x, $y);
$rgbOrig = imagecolorat($img, $x, $y);
$rOrig = (($rgbOrig >> 16) & 0xFF);
$gOrig = (($rgbOrig >> 8) & 0xFF);
$bOrig = ($rgbOrig & 0xFF);
$rgbBlur = ImageColorAt($imgBlur, $x, $y);
$rgbBlur = imagecolorat($imgBlur, $x, $y);
$rBlur = (($rgbBlur >> 16) & 0xFF);
$gBlur = (($rgbBlur >> 8) & 0xFF);
@@ -603,7 +765,7 @@ class ImageSizerEngineGD extends ImageSizerEngine {
$bNew = 0;
}
$rgbNew = ($rNew << 16) + ($gNew << 8) + $bNew;
ImageSetPixel($img, $x, $y, $rgbNew);
imagesetpixel($img, $x, $y, $rgbNew);
}
}
}
@@ -686,8 +848,8 @@ class ImageSizerEngineGD extends ImageSizerEngine {
*
* Intended for use by the resize() method
*
* @param GD -resource $im, destination resource needs to be prepared
* @param GD -resource $image, with GIF we need to read from source resource
* @param resource $im, destination resource needs to be prepared
* @param resource $image, with GIF we need to read from source resource
*
*/
protected function prepareImageLayer(&$im, &$image) {
@@ -721,11 +883,12 @@ class ImageSizerEngineGD extends ImageSizerEngine {
* @param array $sourceDimensions - array with three values: width, height, number of channels
* @param array|bool $targetDimensions - optional - mixed: bool true | false or array with three values:
* width, height, number of channels
* @param int|float Multiply needed memory by this factor
*
* @return bool|null if a calculation was possible (true|false), or null if the calculation could not be done
*
*/
static public function checkMemoryForImage($sourceDimensions, $targetDimensions = false) {
static public function checkMemoryForImage($sourceDimensions, $targetDimensions = false, $factor = 1) {
// with this static we only once need to read from php.ini and calculate phpMaxMem,
// regardless how often this function is called in a request
@@ -755,10 +918,8 @@ class ImageSizerEngineGD extends ImageSizerEngine {
}
// calculate $sourceDimensions
if(!isset($sourceDimensions[0]) || !isset($sourceDimensions[1])
|| !isset($sourceDimensions[2]) || !is_int($sourceDimensions[0])
|| !is_int($sourceDimensions[1]) || !is_int($sourceDimensions[2])
) {
if(!isset($sourceDimensions[0]) || !isset($sourceDimensions[1]) || !isset($sourceDimensions[2]) ||
!is_int($sourceDimensions[0]) || !is_int($sourceDimensions[1]) || !is_int($sourceDimensions[2])) {
return null;
}
@@ -771,10 +932,8 @@ class ImageSizerEngineGD extends ImageSizerEngine {
} else if(is_array($targetDimensions)) {
// we have to add ram for a targetimage
if(!isset($targetDimensions[0]) || !isset($targetDimensions[1])
|| !isset($targetDimensions[2]) || !is_int($targetDimensions[0])
|| !is_int($targetDimensions[1]) || !is_int($targetDimensions[2])
) {
if(!isset($targetDimensions[0]) || !isset($targetDimensions[1]) || !isset($targetDimensions[2]) ||
!is_int($targetDimensions[0]) || !is_int($targetDimensions[1]) || !is_int($targetDimensions[2])) {
return null;
}
@@ -785,7 +944,239 @@ class ImageSizerEngineGD extends ImageSizerEngine {
$curMem = memory_get_usage(true); // memory_get_usage() is always available with PHP since 5.2.1
// check if there is enough RAM loading the image(s), plus 3 MB for GD to use for calculations/transforms
return ($phpMaxMem - $curMem >= $imgMem + (3 * 1048576)) ? true : false;
$extraMem = 3 * 1048576;
$availableMem = $phpMaxMem - $curMem;
$neededMem = ($imgMem + $extraMem) * $factor;
return $availableMem >= $neededMem;
}
/**
* Additional functionality on top of existing checkMemoryForImage function for the flip/rotate actions
*
* @param string $filename Filename to check. Default is whatever was set to this ImageSizer.
* @param bool $double Need enough for both src and dst files loaded at same time? (default=true)
* @param int|float $factor Tweak factor (multiply needed memory by this factor), i.e. 2 for rotate actions. (default=1)
* @param string $action Name of action (if something other than "action")
* @param bool $throwIfNot Throw WireException if not enough memory? (default=false)
* @return bool
* @throws WireException
*
*/
protected function hasEnoughMemory($filename = '', $double = true, $factor = 1, $action = 'action', $throwIfNot = false) {
$error = '';
if(empty($filename)) $filename = $this->filename;
if($filename) {
if($filename != $this->filename || empty($this->info['width'])) {
$this->prepare($filename); // to populate $this->info
}
} else {
$error = 'No filename to check memory for';
}
if(!$error) {
$hasEnough = self::checkMemoryForImage(array(
$this->info['width'],
$this->info['height'],
$this->info['channels']
), $double, $factor);
if($hasEnough === false) {
$error = sprintf($this->_('Not enough memory for “%1$s” on image file: %2$s'), $action, basename($filename));
}
}
if($error) {
if($throwIfNot) {
throw new WireException($error);
} else {
$this->error($error);
return false;
}
}
return true;
}
/**
* Process a rotate or flip action
*
* @param string $srcFilename
* @param string $dstFilename
* @param string $action One of 'rotate' or 'flip'
* @param int|string $value If rotate, specify int of degrees. If flip, specify one of 'vertical', 'horizontal' or 'both'.
* @return bool
* @throws WireException
*
*/
private function processAction($srcFilename, $dstFilename, $action, $value) {
$action = strtolower($action);
$ext = strtolower(pathinfo($srcFilename, PATHINFO_EXTENSION));
$useTransparency = true;
$memFactor = 1;
$img = null;
if(empty($dstFilename)) $dstFilename = $srcFilename;
if($action == 'rotate') $memFactor *= 2;
if(!$this->hasEnoughMemory($srcFilename, true, $memFactor, $action, false)) return false;
if($ext == 'jpg' || $ext == 'jpeg') {
$img = imagecreatefromjpeg($srcFilename);
$useTransparency = false;
} else if($ext == 'png') {
$img = imagecreatefrompng($srcFilename);
} else if($ext == 'gif') {
$img = imagecreatefromgif($srcFilename);
}
if(!$img) {
$this->error("imagecreatefrom$ext failed", Notice::debug);
return false;
}
if($useTransparency) {
imagealphablending($img, true);
imagesavealpha($img, true);
}
$success = true;
$method = '_processAction' . ucfirst($action);
$imgNew = $this->$method($img, $value);
if($imgNew === false) {
// action fail
$success = false;
$this->error($this->className() . ".$method(img, $value) returned fail", Notice::debug);
} else if($imgNew !== $img) {
// a new img object was created
imagedestroy($img);
$img = $imgNew;
if($useTransparency) {
imagealphablending($img, true);
imagesavealpha($img, true);
}
} else {
// existing img object was updated
$img = $imgNew;
}
if($success) {
if($ext == 'png') {
$success = imagepng($img, $dstFilename, 9);
} else if($ext == 'gif') {
$success = imagegif($img, $dstFilename);
} else {
$success = imagejpeg($img, $dstFilename, $this->quality);
}
if(!$success) $this->error("image{$ext}() failed", Notice::debug);
}
imagedestroy($img);
return $success;
}
/**
* Process flip action (internal)
*
* @param resource $img
* @param string $flipType vertical, horizontal or both
* @return bool|resource
*
*/
private function _processActionFlip(&$img, $flipType) {
if(!function_exists('imageflip')) {
$this->error("Image flip requires PHP 5.5 or newer");
return false;
}
if(!in_array($flipType, array('vertical', 'horizontal', 'both'))) {
$this->error("Image flip type must be one of: 'vertical', 'horizontal', 'both'");
return false;
}
$constantName = 'IMG_FLIP_' . strtoupper($flipType);
$flipType = constant($constantName);
if($flipType === null) {
$this->error("Unknown constant for image flip: $constantName");
return false;
}
$success = imageflip($img, $flipType);
return $success ? $img : false;
}
/**
* Process rotate action (internal)
*
* @param resource $img
* @param $degrees
* @return bool|resource
*
*/
private function _processActionRotate(&$img, $degrees) {
$degrees = (int) $degrees;
$angle = 360 - $degrees; // imagerotate is anti-clockwise
$imgNew = imagerotate($img, $angle, 0);
return $imgNew ? $imgNew : false;
}
private function _processActionGreyscale(&$img, $unused) {
if($unused) {}
imagefilter($img, IMG_FILTER_GRAYSCALE);
return $img;
}
private function _processActionSepia(&$img, $sepia = 55) {
imagefilter($img, IMG_FILTER_GRAYSCALE);
imagefilter($img, IMG_FILTER_BRIGHTNESS, -30);
imagefilter($img, IMG_FILTER_COLORIZE, 90, (int) $sepia, 30);
return $img;
}
/**
* Process rotate of an image
*
* @param string $srcFilename
* @param string $dstFilename
* @param int $degrees Clockwise degrees, i.e. 90, 180, 270, -90, -180, -270
* @return bool
*
*/
protected function processRotate($srcFilename, $dstFilename, $degrees) {
return $this->processAction($srcFilename, $dstFilename, 'rotate', $degrees);
}
/**
* Process vertical or horizontal flip of an image
*
* @param string $srcFilename
* @param string $dstFilename
* @param string $flipType Specify vertical, horizontal, or both
* @return bool
*
*/
protected function processFlip($srcFilename, $dstFilename, $flipType) {
return $this->processAction($srcFilename, $dstFilename, 'flip', $flipType);
}
/**
* Convert image to greyscale
*
* @param string $dstFilename If different from source file
* @return bool
*
*/
public function convertToGreyscale($dstFilename = '') {
return $this->processAction($this->filename, $dstFilename, 'greyscale', null);
}
/**
* Convert image to sepia
*
* @param string $dstFilename If different from source file
* @param float|int $sepia Sepia value
* @return bool
*
*/
public function convertToSepia($dstFilename = '', $sepia = 55) {
return $this->processAction($this->filename, $dstFilename, 'sepia', $sepia);
}
}

View File

@@ -45,16 +45,31 @@
* @property mixed $value HTML 'value' attribute for the Inputfield. #pw-group-attribute-properties
* @property string $class HTML 'class' attribute for the Inputfield. #pw-group-attribute-properties
*
* @method string|Inputfield name($name = null) Get or set the name attribute. @since 3.0.110 #pw-group-attribute-methods
* @method string|Inputfield id($id = null) Get or set the id attribute. @since 3.0.110 #pw-group-attribute-methods
* @method string|Inputfield class($class = null) Get class attribute or add a class to the class attribute. @since 3.0.110 #pw-group-attribute-methods
*
* LABELS & CONTENT
* ================
* @property string $label Primary label text that appears above the input. #pw-group-labels
* @property string $description Optional description that appears under label to provide more detailed information. #pw-group-labels
* @property string $notes Optional notes that appear under input area to provide additional notes. #pw-group-labels
* @property string $detail Optional text details that appear under notes. @since 3.0.140 #pw-group-labels
* @property string $icon Optional font-awesome icon name to accompany label (excluding the "fa-") part). #pw-group-labels
* @property string $requiredLabel Optional custom label to display when missing required value. @since 3.0.98 #pw-group-labels
* @property string $head Optional text that appears below label but above description (only used by some Inputfields). #pw-internal
* @property string|null $prependMarkup Optional markup to prepend to the Inputfield content container. #pw-group-other
* @property string|null $appendMarkup Optional markup to append to the Inputfield content container. #pw-group-other
*
* @method string|Inputfield label($label = null) Get or set the 'label' property via method. @since 3.0.110 #pw-group-labels
* @method string|Inputfield description($description = null) Get or set the 'description' property via method. @since 3.0.110 #pw-group-labels
* @method string|Inputfield notes($notes = null) Get or set the 'notes' property via method. @since 3.0.110 #pw-group-labels
* @method string|Inputfield icon($icon = null) Get or set the 'icon' property via method. @since 3.0.110 #pw-group-labels
* @method string|Inputfield requiredLabel($requiredLabel = null) Get or set the 'requiredLabel' property via method. @since 3.0.110 #pw-group-labels
* @method string|Inputfield head($head = null) Get or set the 'head' property via method. @since 3.0.110 #pw-group-labels
* @method string|Inputfield prependMarkup($markup = null) Get or set the 'prependMarkup' property via method. @since 3.0.110 #pw-group-labels
* @method string|Inputfield appendMarkup($markup = null) Get or set the 'appendMarkup' property via method. @since 3.0.110 #pw-group-labels
*
* APPEARANCE
* ==========
* @property int $collapsed Whether the field is collapsed or visible, using one of the "collapsed" constants. #pw-group-appearance
@@ -62,24 +77,42 @@
* @property int $columnWidth Width of column for this Inputfield 10-100 percent. 0 is assumed to be 100 (default). #pw-group-appearance
* @property int $skipLabel Skip display of the label? See the "skipLabel" constants for options. #pw-group-appearance
*
* @method int|Inputfield collapsed($collapsed = null) Get or set collapsed property via method. @since 3.0.110 #pw-group-appearance
* @method string|Inputfield showIf($showIf = null) Get or set showIf selector property via method. @since 3.0.110 #pw-group-appearance
* @method int|Inputfield columnWidth($columnWidth = null) Get or set columnWidth property via method. @since 3.0.110 #pw-group-appearance
* @method int|Inputfield skipLabel($skipLabel = null) Get or set the skipLabel constant property via method. @since 3.0.110 #pw-group-appearance
*
*
* SETTINGS & BEHAVIOR
* ===================
* @property int|bool $required Set to true (or 1) to make input required, or false (or 0) to make not required (default=0). #pw-group-behavior
* @property string $requiredIf Optional conditions under which input is required (selector string). #pw-group-behavior
* @property InputfieldWrapper|null $parent The parent InputfieldWrapper for this Inputfield or null if not set. #pw-internal
* @property null|bool|Fieldtype $hasFieldtype The Fieldtype using this Inputfield, or boolean false when known not to have a Fieldtype, or null when not known. #pw-group-other
* @property null|Field $hasField The Field object associated with this Inputfield, or null when not applicable or not known. #pw-group-other
* @property null|Page $hasPage The Page object associated with this Inputfield, or null when not applicable or not known. #pw-group-other
* @property bool|null $useLanguages When multi-language support active, can be set to true to make it provide inputs for each language, where supported (default=false). #pw-group-behavior
* @property null|bool $entityEncodeLabel Set to boolean false to specifically disable entity encoding of field header/label (default=true). #pw-group-output
* @property null|bool|int $entityEncodeLabel Set to boolean false to specifically disable entity encoding of field header/label (default=true). #pw-group-output
* @property null|bool $entityEncodeText Set to boolean false to specifically disable entity encoding for other text: description, notes, etc. (default=true). #pw-group-output
* @property int $renderValueFlags Options that can be applied to renderValue mode, see "renderValue" constants (default=0). #pw-group-output
* @property string $wrapClass Optional class name (CSS) to apply to the HTML element wrapping the Inputfield. #pw-group-other
* @property string $headerClass Optional class name (CSS) to apply to the InputfieldHeader element #pw-group-other
* @property string $contentClass Optional class name (CSS) to apply to the InputfieldContent element #pw-group-other
* @property int|null $textFormat Text format to use for description/notes text in Inputfield (see textFormat constants) #pw-group-output
*
* @method string|Inputfield required($required = null) Get or set required state. @since 3.0.110 #pw-group-behavior
* @method string|Inputfield requiredIf($requiredIf = null) Get or set required-if selector. @since 3.0.110 #pw-group-behavior
*
* @method string|Inputfield wrapClass($class = null) Get wrapper class attribute or add a class to it. @since 3.0.110 #pw-group-other
* @method string|Inputfield headerClass($class = null) Get header class attribute or add a class to it. @since 3.0.110 #pw-group-other
* @method string|Inputfield contentClass($class = null) Get content class attribute or add a class to it. @since 3.0.110 #pw-group-other
*
*
* HOOKABLE METHODS
* ================
* @method string render()
* @method string renderValue()
* @method void renderReadyHook(Inputfield $parent, $renderValueMode)
* @method Inputfield processInput(WireInputData $input)
* @method InputfieldWrapper getConfigInputfields()
* @method array getConfigArray()
@@ -182,7 +215,7 @@ abstract class Inputfield extends WireData implements Module {
const skipLabelFor = true;
/**
* Don't use a label header element at all (basically, skip the label)
* Don't show a visible header (likewise, do not show the label)
* #pw-group-skipLabel-constants
*
*/
@@ -195,6 +228,14 @@ abstract class Inputfield extends WireData implements Module {
*/
const skipLabelBlank = 4;
/**
* Do not render any markup for the header/label at all
* #pw-group-skipLabel-constants
* @since 3.0.139
*
*/
const skipLabelMarkup = 8;
/**
* Plain text: no type of markdown or HTML allowed
* #pw-group-textFormat-constants
@@ -258,7 +299,7 @@ abstract class Inputfield extends WireData implements Module {
*/
/**
* Attributes specified for the XHTML output, like class, rows, cols, etc.
* Attributes specified for the HTML output, like class, rows, cols, etc.
*
*/
protected $attributes = array();
@@ -301,22 +342,25 @@ abstract class Inputfield extends WireData implements Module {
self::$numInstances++;
$this->set('label', ''); // primary clikable label
$this->set('description', ''); // descriptive copy, below label
$this->set('label', ''); // primary clickable label
$this->set('description', ''); // descriptive copy, below label
$this->set('icon', ''); // optional icon name to accompany label
$this->set('notes', ''); // highlighted descriptive copy, below output of input field
$this->set('head', ''); // below label, above description
$this->set('required', 0); // set to 1 to make value required for this field
$this->set('notes', ''); // highlighted descriptive copy, below output of input field
$this->set('detail', ''); // text details that appear below notes
$this->set('head', ''); // below label, above description
$this->set('required', 0); // set to 1 to make value required for this field
$this->set('requiredIf', ''); // optional conditions to make it required
$this->set('collapsed', ''); // see the collapsed* constants at top of class (use blank string for unset value)
$this->set('showIf', ''); // optional conditions selector
$this->set('columnWidth', ''); // percent width of the field. blank or 0 = 100.
$this->set('collapsed', ''); // see the collapsed* constants at top of class (use blank string for unset value)
$this->set('showIf', ''); // optional conditions selector
$this->set('columnWidth', ''); // percent width of the field. blank or 0 = 100.
$this->set('skipLabel', self::skipLabelNo); // See the skipLabel constants
$this->set('wrapClass', ''); // optional class to apply to the Inputfield wrapper (contains InputfieldHeader + InputfieldContent)
$this->set('headerClass', ''); // optional class to apply to InputfieldHeader wrapper
$this->set('contentClass', ''); // optional class to apply to InputfieldContent wrapper
$this->set('textFormat', self::textFormatBasic); // format applied to description and notes
$this->set('renderValueFlags', 0); // see renderValue* constants, applicable to renderValue mode only
$this->set('prependMarkup', ''); // markup to prepend to Inputfield output
$this->set('appendMarkup', ''); // markup to append to Inputfield output
// default ID attribute if no 'id' attribute set
$this->defaultID = $this->className() . self::$numInstances;
@@ -385,7 +429,7 @@ abstract class Inputfield extends WireData implements Module {
*
* @param string $key Name of property to set
* @param mixed $value Value of property
* @return $this
* @return Inputfield|WireData
*
*/
public function set($key, $value) {
@@ -463,6 +507,18 @@ abstract class Inputfield extends WireData implements Module {
return $this;
}
/**
* Unset any previously set parent
*
* #pw-internal
* @return $this
*
*/
public function unsetParent() {
$this->parent = null;
return $this;
}
/**
* Get this Inputfields parent InputfieldWrapper, or NULL if it doesnt have one.
*
@@ -486,6 +542,7 @@ abstract class Inputfield extends WireData implements Module {
*
*/
public function getParents() {
/** @var InputfieldWrapper|null $parent */
$parent = $this->getParent();
if(!$parent) return array();
$parents = array($parent);
@@ -493,6 +550,80 @@ abstract class Inputfield extends WireData implements Module {
return $parents;
}
/**
* Get or set parent of Inputfield
*
* This convenience method performs the same thing as getParent() and setParent().
*
* To get parent, specify no arguments. It will return null if no parent assigned, or an
* InputfieldWrapper instance of the parent.
*
* To set parent, specify an InputfieldWrapper for the $parent argument. The return value
* is the current Inputfield for fluent interface.
*
* #pw-group-traversal
*
* @param null|InputfieldWrapper $parent
* @return null|Inputfield|InputfieldWrapper
* @since 3.0.110
*
*/
public function parent($parent = null) {
if($parent === null) {
return $this->getParent();
} else {
return $this->setParent($parent);
}
}
/**
* Get array of all parents of this Inputfield
*
* This is identical to and an alias of the getParents() method.
*
* #pw-group-traversal
*
* @return array
* @since 3.0.110
*
*/
public function parents() {
return $this->getParents();
}
/**
* Get the root parent InputfieldWrapper element (farthest parent, commonly InputfieldForm)
*
* This returns null only if Inputfield it is called from has not yet been added to an InputfieldWrapper.
*
* #pw-group-traversal
*
* @return InputfieldForm|InputfieldWrapper|null
* @since 3.0.106
*
*/
public function getRootParent() {
$parents = $this->getParents();
return count($parents) ? end($parents) : null;
}
/**
* Get the InputfieldForm element that contains this field or null if not yet defined
*
* This is the same as the `getRootParent()` method except that it returns null if root parent
* is not an InputfieldForm.
*
* #pw-group-traversal
*
* @return InputfieldForm|null
* @since 3.0.106
*
*/
public function getForm() {
$form = $this instanceof InputfieldForm ? $this : $this->getRootParent();
return ($form instanceof InputfieldForm ? $form : null);
}
/**
* Set an attribute
*
@@ -520,21 +651,46 @@ abstract class Inputfield extends WireData implements Module {
* - Name of attribute (string)
* - Names of attributes (array)
* - String with names of attributes split by "+" or "|"
* @param string|int|array $value Value of attribute to set.
* @param string|int|array|bool $value Value of attribute to set.
* @return $this
* @see Inputfield::attr(), Inputfield::removeAttr(), Inputfield::addClass()
*
*/
public function setAttribute($key, $value) {
if(is_array($key)) $keys = $key;
else if(strpos($key, '+') !== false) $keys = explode('+', $key);
else if(strpos($key, '|') !== false) $keys = explode('|', $key);
else $keys = array($key);
if(is_array($key)) {
$keys = $key;
} else if(strpos($key, '+') !== false) {
$keys = explode('+', $key);
} else if(strpos($key, '|') !== false) {
$keys = explode('|', $key);
} else {
$keys = array($key);
}
if(is_bool($value) && !in_array($key, array('name', 'id', 'class', 'value', 'type'))) {
$booleanValue = $value;
} else {
$booleanValue = null;
}
foreach($keys as $key) {
if($key == 'name' && strlen($value)) {
if(!ctype_alpha("$key")) $key = $this->wire('sanitizer')->attrName($key);
if(empty($key)) continue;
if($booleanValue !== null) {
if($booleanValue === true) {
// boolean true attribute sets value as attribute name (i.e. checked='checked')
$value = $key;
} else if($booleanValue === false) {
// boolean false attribute implies remove attribute
$this->removeAttribute($key);
continue;
}
}
if($key === 'name' && strlen($value)) {
$idAttr = $this->getAttribute('id');
$nameAttr = $this->getAttribute('name');
if($idAttr == $this->defaultID || $idAttr == $nameAttr || $idAttr == "Inputfield_$nameAttr") {
@@ -543,7 +699,9 @@ abstract class Inputfield extends WireData implements Module {
}
}
if(!array_key_exists($key, $this->attributes)) $this->attributes[$key] = '';
if(!array_key_exists($key, $this->attributes)) {
$this->attributes[$key] = '';
}
if(is_array($this->attributes[$key]) && !is_array($value)) {
@@ -641,8 +799,8 @@ abstract class Inputfield extends WireData implements Module {
* - Aassociative array to set multiple attributes.
* - String with attributes split by "+" or "|" to set them all to have the same value.
* - Specify boolean true to get all attributes in an associative array.
* @param string|int|null $value Value to set (if setting), omit otherwise.
* @return mixed|$this If setting an attribute, it returns this instance. If getting an attribute, the attribute is returned.
* @param string|int|bool|null $value Value to set (if setting), omit otherwise.
* @return Inputfield|array|string|int|object|float If setting an attribute, it returns this instance. If getting an attribute, the attribute is returned.
* @see Inputfield::removeAttr(), Inputfield::addClass(), Inputfield::removeClass()
*
*/
@@ -658,6 +816,58 @@ abstract class Inputfield extends WireData implements Module {
}
return $this->setAttribute($key, $value);
}
/**
* Shortcut for getting or setting “value” attribute
*
* When setting a value, it returns $this (for fluent interface).
*
* ~~~~~
* $value = $inputfield->val(); * // Getting
* $inputfield->val('foo'); * // Setting
* ~~~~~
*
* @param string|null $value
* @return string|int|float|array|object|Wire|WireData|WireArray|Inputfield
*
*/
public function val($value = null) {
if($value === null) return $this->getAttribute('value');
return $this->setAttribute('value', $value);
}
/**
* If method call resulted in no handler, this hookable method is called.
*
* We use this to allow for attributes and properties to be set via method, useful primarily
* for fluent interface calls.
*
* #pw-internal
*
* @param string $method Requested method name
* @param array $arguments Arguments provided
* @return null|mixed Return value of method (if applicable)
* @throws WireException
* @since 3.0.110
*
*/
protected function ___callUnknown($method, $arguments) {
$arg = isset($arguments[0]) ? $arguments[0] : null;
if(isset($this->attributes[$method])) {
// get or set an attribute
return $arg === null ? $this->getAttribute($method) : $this->setAttribute($method, $arg);
} else if(($value = $this->getSetting($method)) !== null) {
// get or set a setting
if($arg === null) return $value;
if(stripos($method, 'class') !== false) {
// i.e. class, wrapClass, contentClass, etc.
return $this->addClass($arg, $method);
} else {
return $this->set($method, $arg);
}
}
return parent::___callUnknown($method, $arguments);
}
/**
* Get all attributes specified for this Inputfield
@@ -702,7 +912,7 @@ abstract class Inputfield extends WireData implements Module {
* - Omit if getting an attribute.
* - Value to set for $key of setting.
* - Boolean false to remove the attribute specified for $key.
* @return string|array|$this Returns one of the following:
* @return Inputfield|string|array|null Returns one of the following:
* - If getting, returns attribute value of NULL if not present.
* - If setting, returns $this.
* @see Inputfield::attr(), Inputfield::addClass()
@@ -788,12 +998,13 @@ abstract class Inputfield extends WireData implements Module {
foreach($addClasses as $addClass) {
$addClass = trim($addClass);
if(!strlen($addClass)) continue;
if(in_array($addClass, $classes)) continue; // if already present, don't add it
$classes[] = $addClass;
}
$classes = array_unique($classes);
// convert back to string
$value = implode(' ', $classes);
$value = trim(implode(' ', $classes));
// set back to Inputfield
if($property == 'class') {
@@ -882,7 +1093,7 @@ abstract class Inputfield extends WireData implements Module {
*
* #pw-group-attribute-methods
*
* @param string $class Class name you want to remove or specify one of the following:
* @param string|array $class Class name you want to remove or specify one of the following:
* - Single class name to remove.
* - Space-separated class names you want to remove (Since 3.0.16).
* - Array of class names you want to remove (Since 3.0.16).
@@ -958,12 +1169,16 @@ abstract class Inputfield extends WireData implements Module {
}
foreach($attributes as $attr => $value) {
// skip over empty attributes
if(!is_array($value) && !strlen("$value") && (!$value = $this->attr($attr))) continue;
// if an attribute has multiple values (like class), then bundle them into a string separated by spaces
if(is_array($value)) $value = implode(' ', $value);
if(is_array($value)) {
// if an attribute has multiple values (like class), then bundle them into a string separated by spaces
$value = implode(' ', $value);
} else if(!strlen("$value") && strpos($attr, 'data-') !== 0) {
// skip over empty non-data attributes that are not arrays
// if(!$value = $this->attr($attr))) continue; // was in 3.0.132 and earlier
continue;
}
$str .= "$attr=\"" . htmlspecialchars($value, ENT_QUOTES, "UTF-8") . '" ';
}
@@ -1030,9 +1245,24 @@ abstract class Inputfield extends WireData implements Module {
public function renderReady(Inputfield $parent = null, $renderValueMode = false) {
if($parent) {}
if($renderValueMode) {}
return $this->wire('modules')->loadModuleFileAssets($this) > 0;
$result = $this->wire('modules')->loadModuleFileAssets($this) > 0;
if($this->wire('hooks')->isMethodHooked($this, 'renderReadyHook')) {
$this->renderReadyHook($parent, $renderValueMode);
}
return $result;
}
/**
* Hookable version of renderReady(), not called unless 'renderReadyHook' is hooked
*
* Hook this method instead if you want to hook renderReady().
*
* @param Inputfield $parent
* @param bool $renderValueMode
*
*/
public function ___renderReadyHook(Inputfield $parent = null, $renderValueMode = false) { }
/**
* This hook was replaced by renderReady
*
@@ -1047,7 +1277,7 @@ abstract class Inputfield extends WireData implements Module {
/**
* Process input for this Inputfield directly from the POST (or GET) variables
*
* This method should pull the value from the given `$input` ragument, sanitize/validate it, and
* This method should pull the value from the given `$input` argument, sanitize/validate it, and
* populate it to the `value` attribute of this Inputfield.
*
* Inputfield modules should implement this method if the built-in one here doesn't solve their need.
@@ -1237,7 +1467,7 @@ abstract class Inputfield extends WireData implements Module {
$field = $this->modules->get('InputfieldInteger');
$value = (int) $this->getSetting('columnWidth');
if($value < 10 || $value >= 100) $value = 100;
$field->label = sprintf($this->_("Column Width (%d%%)"), $value);
$field->label = sprintf($this->_('Column width (%d%%)'), $value);
$field->icon = 'arrows-h';
$field->attr('id+name', 'columnWidth');
$field->attr('type', 'text');
@@ -1419,7 +1649,9 @@ abstract class Inputfield extends WireData implements Module {
$errors[] = $text;
$this->wire('session')->set($key, $errors);
}
$text .= $this->name ? " ($this->name)" : "";
$label = $this->getSetting('label');
if(empty($label)) $label= $this->attr('name');
if(strlen($label)) $text .= " - $label";
return parent::error($text, $flags);
}
@@ -1502,7 +1734,7 @@ abstract class Inputfield extends WireData implements Module {
* @param string $what Name of property that changed
* @param mixed $old Previous value before change
* @param mixed $new New value
* @return $this
* @return Inputfield|WireData $this
*
*/
public function trackChange($what, $old = null, $new = null) {
@@ -1530,9 +1762,12 @@ abstract class Inputfield extends WireData implements Module {
*/
public function entityEncode($str, $markdown = false) {
/** @var Sanitizer $sanitizer */
$sanitizer = $this->wire('sanitizer');
// if already encoded, then un-encode it
if(strpos($str, '&') !== false && preg_match('/&(#\d+|[a-z]+);/', $str)) {
$str = html_entity_decode($str, ENT_QUOTES, "UTF-8");
if(strpos($str, '&') !== false && preg_match('/&(#\d+|[a-zA-Z]+);/', $str)) {
$str = $sanitizer->unentities($str);
}
if($markdown && $markdown !== self::textFormatNone) {
@@ -1544,17 +1779,17 @@ abstract class Inputfield extends WireData implements Module {
if(!$textFormat) $textFormat = self::textFormatBasic;
if($textFormat & self::textFormatBasic) {
// only basic markdown allowed (default behavior)
$str = $this->wire('sanitizer')->entitiesMarkdown($str, array('allowBrackets' => true));
$str = $sanitizer->entitiesMarkdown($str, array('allowBrackets' => true));
} else if($textFormat & self::textFormatMarkdown) {
// full markdown, plus HTML is also allowed
$str = $this->wire('sanitizer')->entitiesMarkdown($str, array('fullMarkdown' => true));
$str = $sanitizer->entitiesMarkdown($str, array('fullMarkdown' => true));
} else {
// nothing allowed, text fully entity encoded regardless of $markdown request
$str = $this->wire('sanitizer')->entities($str);
$str = $sanitizer->entities($str);
}
} else {
$str = $this->wire('sanitizer')->entities($str);
$str = $sanitizer->entities($str);
}
return $str;

View File

@@ -24,6 +24,7 @@
* @property InputfieldsArray|null $children Inputfield instances that are direct children of this InputfieldWrapper. #pw-group-properties
*
* @method string renderInputfield(Inputfield $inputfield, $renderValueMode = false) #pw-group-output
* @method Inputfield new($typeName, $name = '', $label = '', array $settings = array()) #pw-group-manipulation
*
*/
@@ -42,15 +43,16 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
*
*/
static protected $defaultMarkup = array(
'list' => "\n<ul {attrs}>\n{out}\n</ul>\n",
'item' => "\n\t<li {attrs}>\n{out}\n\t</li>",
'item_label' => "\n\t\t<label class='InputfieldHeader ui-widget-header{class}' for='{for}'>{out}</label>",
'item_label_hidden' => "\n\t\t<label class='InputfieldHeader InputfieldHeaderHidden ui-widget-header{class}'><span>{out}</span></label>",
'item_content' => "\n\t\t<div class='InputfieldContent ui-widget-content{class}'>\n{out}\n\t\t</div>",
'item_error' => "\n<p class='InputfieldError ui-state-error'><i class='fa fa-fw fa-flash'></i><span>{out}</span></p>",
'item_description' => "\n<p class='description'>{out}</p>",
'item_head' => "\n<h2>{out}</h2>",
'item_notes' => "\n<p class='notes'>{out}</p>",
'list' => "<ul {attrs}>{out}</ul>",
'item' => "<li {attrs}>{out}</li>",
'item_label' => "<label class='InputfieldHeader ui-widget-header{class}' for='{for}'>{out}</label>",
'item_label_hidden' => "<label class='InputfieldHeader InputfieldHeaderHidden ui-widget-header{class}'><span>{out}</span></label>",
'item_content' => "<div class='InputfieldContent ui-widget-content{class}'>{out}</div>",
'item_error' => "<p class='InputfieldError ui-state-error'><i class='fa fa-fw fa-flash'></i><span>{out}</span></p>",
'item_description' => "<p class='description'>{out}</p>",
'item_head' => "<h2>{out}</h2>",
'item_notes' => "<p class='notes'>{out}</p>",
'item_detail' => "<p class='detail'>{out}</p>",
'item_icon' => "<i class='fa fa-fw fa-{name}'></i> ",
'item_toggle' => "<i class='toggle-icon fa fa-fw fa-angle-down' data-to='fa-angle-down fa-angle-right'></i>",
// ALSO:
@@ -101,6 +103,14 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
*/
protected $requiredLabel = '';
/**
* Whether or not column width is handled internally
*
* @var bool
*
*/
protected $useColumnWidth = true;
/**
* Construct the Inputfield, setting defaults for all properties
*
@@ -119,6 +129,8 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
if(is_array($settings)) foreach($settings as $key => $value) {
if($key == 'requiredLabel') {
$this->requiredLabel = $value;
} else if($key == 'useColumnWidth') {
$this->useColumnWidth = $value;
} else {
$this->set($key, $value);
}
@@ -175,23 +187,87 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
/**
* Add an Inputfield item as a child (also accepts array definition)
*
* Since 3.0.110: If given a string value, it is assumed to be an Inputfield type that you
* want to add. In that case, it will create the Inputfield and return it instead of $this.
*
* #pw-group-manipulation
*
* @param Inputfield|array $item
* @return $this
* @param Inputfield|array|string $item
* @return Inputfield|InputfieldWrapper|$this
* @see InputfieldWrapper::import()
*
*/
public function add($item) {
if(is_array($item)) {
if(is_string($item)) {
return $this->___new($item);
} else if(is_array($item)) {
$this->importArray($item);
} else {
$item->setParent($this);
$this->children->add($item);
$item->setParent($this);
}
return $this;
}
/**
* Create a new Inputfield, add it to this InputfieldWrapper, and return the new Inputfield
*
* - Only the $typeName argument is required.
* - You may optionally substitute the $settings argument for the $name or $label arguments.
* - You may optionally substitute Inputfield “description” property for $settings argument.
*
* #pw-group-manipulation
*
* @param string $typeName Inputfield type, i.e. “InputfieldCheckbox” or just “checkbox” for short.
* @param string|array $name Name of input (or substitute $settings here).
* @param string|array $label Label for input (or substitute $settings here).
* @param array|string $settings Settings to add to Inputfield (optional). Or if string, assumed to be “description”.
* @return Inputfield|InputfieldSelect|InputfieldWrapper An Inputfield instance ready to populate with additional properties/attributes.
* @throws WireException If you request an unknown Inputfield type
* @since 3.0.110
*
*/
public function ___new($typeName, $name = '', $label = '', $settings = array()) {
if(is_array($name)) {
$settings = $name;
$name = '';
} else if(is_array($label)) {
$settings = $label;
$label = '';
}
if(strpos($typeName, 'Inputfield') !== 0) {
$typeName = "Inputfield" . ucfirst($typeName);
}
/** @var Inputfield|InputfieldSelect|InputfieldWrapper $inputfield */
$inputfield = $this->wire('modules')->getModule($typeName);
if(!$inputfield && wireClassExists($typeName)) {
$inputfield = $this->wire(new $typeName());
}
if(!$inputfield || !$inputfield instanceof Inputfield) {
throw new WireException("Unknown Inputfield type: $typeName");
}
if(strlen($name)) $inputfield->attr('name', $name);
if(strlen($label)) $inputfield->label = $label;
if(is_array($settings)) {
foreach($settings as $key => $value) {
$inputfield->set($key, $value);
}
} else if(is_string($settings)) {
$inputfield->description = $settings;
}
$this->add($inputfield);
return $inputfield;
}
/**
* Import the given Inputfield items as children
*
@@ -341,12 +417,89 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
array_push($wrappers, $inputfield);
}
$inputfield->unsetParent();
$wrapper->add($inputfield);
}
return $children;
}
/**
* Cached class parents indexed by Inputfield class name
*
* @var array
*
*/
static protected $classParents = array();
/**
* Get array of parent Inputfield classes for given Inputfield (excluding the base Inputfield class)
*
* @param Inputfield|string $inputfield
* @return array
*
*/
protected function classParents($inputfield) {
$p = &self::$classParents;
$c = is_object($inputfield) ? $inputfield->className() : $inputfield;
if(!isset($p[$c])) {
$p[$c] = array();
foreach(wireClassParents($inputfield) as $parentClass) {
if(strpos($parentClass, 'Inputfield') !== 0 || $parentClass === 'Inputfield') break;
$p[$c][] = $parentClass;
}
}
return $p[$c];
}
/**
* Prepare Inputfield for attributes used during rendering
*
* #pw-internal
*
* @param Inputfield $inputfield
* @param array $markup
* @since 3.0.144
*
*/
private function attributeInputfield(Inputfield $inputfield, &$markup) {
$inputfieldClass = $inputfield->className();
$markupTemplate = array('attr' => array(), 'wrapAttr' => array());
$classParents = $this->classParents($inputfield);
$classParents[] = $inputfieldClass;
$classKeys = array('class', 'wrapClass', 'headerClass', 'contentClass');
$classes = array();
$attr = array();
$wrapAttr = array();
foreach($classParents as $classParent) {
if(!isset($markup[$classParent])) continue;
$markupParent = array_merge($markupTemplate, $markup[$classParent]);
foreach($classKeys as $classKey) {
if(!empty($markupParent[$classKey])) {
$classes[$classKey] = $markupParent[$classKey];
}
}
foreach($markupParent['attr'] as $k => $v) {
$attr[$k] = $v;
}
foreach($markupParent['wrapAttr'] as $k => $v) {
$wrapAttr[$k] = $v;
}
}
foreach($attr as $attrName => $attrVal) {
$inputfield->attr($attrName, $attrVal);
}
foreach($wrapAttr as $attrName => $attrVal) {
$inputfield->wrapAttr($attrName, $attrVal);
}
foreach($classes as $classKey => $class) {
$inputfield->addClass($class, $classKey);
}
}
/**
* Render this Inputfield and the output of its children.
*
@@ -368,10 +521,10 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
$_classes = array_merge(self::$defaultClasses, self::$classes);
$markup = array();
$classes = array();
$useColumnWidth = true;
$useColumnWidth = $this->useColumnWidth;
$renderAjaxInputfield = $this->wire('config')->ajax ? $this->wire('input')->get('renderInputfieldAjax') : null;
if(isset($_classes['form']) && strpos($_classes['form'], 'InputfieldFormNoWidths') !== false) {
if($useColumnWidth === true && isset($_classes['form']) && strpos($_classes['form'], 'InputfieldFormNoWidths') !== false) {
$useColumnWidth = false;
}
@@ -406,9 +559,11 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
if($collapsed == Inputfield::collapsedHidden) continue;
if($collapsed == Inputfield::collapsedNoLocked || $collapsed == Inputfield::collapsedYesLocked) $renderValueMode = true;
$this->attributeInputfield($inputfield, $_markup);
$ffOut = $this->renderInputfield($inputfield, $renderValueMode);
if(!strlen($ffOut)) continue;
$collapsed = (int) $inputfield->getSetting('collapsed'); // retrieve again after render
$entityEncodeText = $inputfield->getSetting('entityEncodeText') === false ? false : true;
$errorsOut = '';
@@ -420,11 +575,16 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
}
} else $errors = array();
foreach(array('error', 'description', 'head', 'notes') as $property) {
foreach(array('error', 'description', 'head', 'notes', 'detail') as $property) {
$text = $property == 'error' ? $errorsOut : $inputfield->getSetting($property);
if($property === 'detail' && !is_string($text)) continue; // may not be necessary
if(!empty($text) && !$quietMode) {
$text = nl2br($entityEncodeText ? $inputfield->entityEncode($text, true) : $text);
$text = str_replace('{out}', $text, $markup["item_$property"]);
if($entityEncodeText) {
$text = $inputfield->entityEncode($text, true);
}
if($inputfield->textFormat != Inputfield::textFormatMarkdown) {
$text = str_replace('{out}', nl2br($text), $markup["item_$property"]);
}
} else {
$text = '';
}
@@ -433,7 +593,7 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
$markup['item_content'] = str_replace($_property, $text, $markup['item_content']);
} else if(strpos($markup['item_label'], $_property) !== false) {
$markup['item_label'] = str_replace($_property, $text, $markup['item_label']);
} else if($text && $property == 'notes') {
} else if($text && ($property == 'notes' || $property == 'detail')) {
$ffOut .= $text;
} else if($text) {
$ffOut = $text . $ffOut;
@@ -447,7 +607,7 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
if($appendMarkup) $ffOut .= $appendMarkup;
}
// The inputfield's classname is always used in it's LI wrapper
// The inputfield classname is always used in its wrapping element
$ffAttrs = array(
'class' => str_replace(
array('{class}', '{name}'),
@@ -469,9 +629,9 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
$ffAttrs['class'] .= ' ' . $classes['item_required_if'];
}
if($collapsed) {
if($collapsed && $collapsed !== Inputfield::collapsedNever) {
$isEmpty = $inputfield->isEmpty();
if(($isEmpty && $inputfield instanceof InputfieldWrapper) ||
if(($isEmpty && $inputfield instanceof InputfieldWrapper && $collapsed !== Inputfield::collapsedPopulated) ||
$collapsed === Inputfield::collapsedYes ||
$collapsed === Inputfield::collapsedYesLocked ||
$collapsed === true ||
@@ -492,89 +652,116 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
}
}
}
// if inputfield produced no output, then move to next
if(!$ffOut) continue;
// if the inputfield resulted in output, wrap it in an LI
if($ffOut) {
$attrs = '';
$label = $inputfield->getSetting('label');
if(!strlen($label) && $inputfield->getSetting('skipLabel') != Inputfield::skipLabelBlank) {
$label = $inputfield->attr('name');
// wrap the inputfield output
$attrs = '';
$label = $inputfield->getSetting('label');
$skipLabel = $inputfield->getSetting('skipLabel');
$skipLabel = is_bool($skipLabel) || empty($skipLabel) ? (bool) $skipLabel : (int) $skipLabel; // force as bool or int
if(!strlen($label) && $skipLabel !== Inputfield::skipLabelBlank && $inputfield->className() != 'InputfieldWrapper') {
$label = $inputfield->attr('name');
}
if(($label || $quietMode) && $skipLabel !== Inputfield::skipLabelMarkup) {
$for = $skipLabel || $quietMode ? '' : $inputfield->attr('id');
// if $inputfield has a property of entityEncodeLabel with a value of boolean FALSE, we don't entity encode
$entityEncodeLabel = $inputfield->getSetting('entityEncodeLabel');
if(is_int($entityEncodeLabel) && $entityEncodeLabel >= Inputfield::textFormatBasic) {
// uses an Inputfield::textFormat constant
$label = $inputfield->entityEncode($label, $entityEncodeLabel);
} else if($entityEncodeLabel !== false) {
$label = $inputfield->entityEncode($label);
}
if($label || $quietMode) {
$for = $inputfield->getSetting('skipLabel') || $quietMode ? '' : $inputfield->attr('id');
// if $inputfield has a property of entityEncodeLabel with a value of boolean FALSE, we don't entity encode
if($inputfield->getSetting('entityEncodeLabel') !== false) $label = $inputfield->entityEncode($label);
$icon = $inputfield->getSetting('icon');
$icon = $icon ? str_replace('{name}', $this->wire('sanitizer')->name(str_replace(array('icon-', 'fa-'), '', $icon)), $markup['item_icon']) : '';
$toggle = $collapsed == Inputfield::collapsedNever ? '' : $markup['item_toggle'];
if($toggle && strpos($toggle, 'title=') === false) {
$toggle = str_replace("class=", "title='" . $this->_('Toggle open/close') . "' class=", $toggle);
}
if($inputfield->getSetting('skipLabel') === Inputfield::skipLabelHeader || $quietMode) {
// label only shows when field is collapsed
$label = str_replace('{out}', $icon . $label . $toggle, $markup['item_label_hidden']);
$icon = $inputfield->getSetting('icon');
$icon = $icon ? str_replace('{name}', $this->wire('sanitizer')->name(str_replace(array('icon-', 'fa-'), '', $icon)), $markup['item_icon']) : '';
$toggle = $collapsed == Inputfield::collapsedNever ? '' : $markup['item_toggle'];
if($toggle && strpos($toggle, 'title=') === false) {
$toggle = str_replace("class=", "title='" . $this->_('Toggle open/close') . "' class=", $toggle);
}
if($skipLabel === Inputfield::skipLabelHeader || $quietMode) {
// label only shows when field is collapsed
$label = str_replace('{out}', $icon . $label . $toggle, $markup['item_label_hidden']);
} else {
// label always visible
$label = str_replace(array('{for}', '{out}'), array($for, $icon . $label . $toggle), $markup['item_label']);
}
$headerClass = trim($inputfield->getSetting('headerClass') . " $classes[item_label]");
if($headerClass) {
if(strpos($label, '{class}') !== false) {
$label = str_replace('{class}', ' ' . $headerClass, $label);
} else {
// label always visible
$label = str_replace(array('{for}', '{out}'), array($for, $icon . $label . $toggle), $markup['item_label']);
$label = preg_replace('/( class=[\'"][^\'"]+)/', '$1 ' . $headerClass, $label, 1);
}
$headerClass = trim($inputfield->getSetting('headerClass') . " $classes[item_label]");
if($headerClass) {
if(strpos($label, '{class}') !== false) {
$label = str_replace('{class}', ' ' . $headerClass, $label);
} else {
$label = preg_replace('/( class=[\'"][^\'"]+)/', '$1 ' . $headerClass, $label, 1);
}
} else if(strpos($label, '{class}') !== false) {
$label = str_replace('{class}', '', $label);
} else if(strpos($label, '{class}') !== false) {
$label = str_replace('{class}', '', $label);
}
} else if($skipLabel === Inputfield::skipLabelMarkup) {
// no header and no markup for header
$label = '';
} else {
// no header
// $inputfield->addClass('InputfieldNoHeader', 'wrapClass');
}
$columnWidth = (int) $inputfield->getSetting('columnWidth');
$columnWidthAdjusted = $columnWidth;
if($columnWidthSpacing) {
$columnWidthAdjusted = $columnWidth + ($columnWidthTotal ? -1 * $columnWidthSpacing : 0);
}
if($columnWidth >= 9 && $columnWidth <= 100) {
$ffAttrs['class'] .= ' ' . $classes['item_column_width'];
if(!$columnWidthTotal) {
$ffAttrs['class'] .= ' ' . $classes['item_column_width_first'];
}
$columnWidthTotal += $columnWidth;
if(!$useColumnWidth || $useColumnWidth > 1) {
if($columnWidthTotal >= 95 && $columnWidthTotal < 100) {
$columnWidthAdjusted += (100 - $columnWidthTotal);
$columnWidthTotal = 100;
}
$ffAttrs['data-colwidth'] = "$columnWidthAdjusted%";
}
if($useColumnWidth) {
$columnWidth = (int) $inputfield->getSetting('columnWidth');
$columnWidthAdjusted = $columnWidth + ($columnWidthTotal ? -1 * $columnWidthSpacing : 0);
if($columnWidth >= 9 && $columnWidth <= 100) {
$ffAttrs['class'] .= ' ' . $classes['item_column_width'];
if(!$columnWidthTotal) $ffAttrs['class'] .= ' ' . $classes['item_column_width_first'];
$ffAttrs['style'] = "width: $columnWidthAdjusted%;";
$columnWidthTotal += $columnWidth;
//if($columnWidthTotal >= 100 && !$requiredIf) $columnWidthTotal = 0; // requiredIf meant to be a showIf?
if($columnWidthTotal >= 100) $columnWidthTotal = 0;
} else {
$columnWidthTotal = 0;
}
$ffAttrs['style'] = "width: $columnWidthAdjusted%;";
}
if(!isset($ffAttrs['id'])) $ffAttrs['id'] = 'wrap_' . $inputfield->attr('id');
$ffAttrs['class'] = str_replace('Inputfield_ ', '', $ffAttrs['class']);
$wrapClass = $inputfield->getSetting('wrapClass');
if($wrapClass) $ffAttrs['class'] .= " " . $wrapClass;
foreach($inputfield->wrapAttr() as $k => $v) {
if(!empty($ffAttrs[$k])) {
$ffAttrs[$k] .= " $v";
} else {
$ffAttrs[$k] = $v;
}
//if($columnWidthTotal >= 100 && !$requiredIf) $columnWidthTotal = 0; // requiredIf meant to be a showIf?
if($columnWidthTotal >= 100) $columnWidthTotal = 0;
} else {
$columnWidthTotal = 0;
}
if(!isset($ffAttrs['id'])) $ffAttrs['id'] = 'wrap_' . $inputfield->attr('id');
$ffAttrs['class'] = str_replace('Inputfield_ ', '', $ffAttrs['class']);
$wrapClass = $inputfield->getSetting('wrapClass');
if($wrapClass) $ffAttrs['class'] .= " " . $wrapClass;
foreach($inputfield->wrapAttr() as $k => $v) {
if(!empty($ffAttrs[$k])) {
$ffAttrs[$k] .= " $v";
} else {
$ffAttrs[$k] = $v;
}
foreach($ffAttrs as $k => $v) {
$k = $this->entityEncode($k);
$v = $this->entityEncode(trim($v));
$attrs .= " $k='$v'";
}
foreach($ffAttrs as $k => $v) {
$k = $this->entityEncode($k);
$v = $this->entityEncode(trim($v));
$attrs .= " $k='$v'";
}
$markupItemContent = $markup['item_content'];
$contentClass = trim($inputfield->getSetting('contentClass') . " $classes[item_content]");
if($contentClass) {
if(strpos($markupItemContent, '{class}') !== false) {
$markupItemContent = str_replace('{class}', ' ' . $contentClass, $markupItemContent);
} else {
$markupItemContent = preg_replace('/( class=[\'"][^\'"]+)/', '$1 ' . $contentClass, $markupItemContent, 1);
}
$markupItemContent = $markup['item_content'];
$contentClass = trim($inputfield->getSetting('contentClass') . " $classes[item_content]");
if($contentClass) {
if(strpos($markupItemContent, '{class}') !== false) {
$markupItemContent = str_replace('{class}', ' ' . $contentClass, $markupItemContent);
} else {
$markupItemContent = preg_replace('/( class=[\'"][^\'"]+)/', '$1 ' . $contentClass, $markupItemContent, 1);
}
} else if(strpos($markupItemContent, '{class}') !== false) {
$markupItemContent = str_replace('{class}', '', $markupItemContent);
}
if($inputfield->className() != 'InputfieldWrapper') $ffOut = str_replace('{out}', $ffOut, $markupItemContent);
$out .= str_replace(array('{attrs}', '{out}'), array(trim($attrs), $label . $ffOut), $markup['item']);
$lastInputfield = $inputfield;
} // if($ffOut)
}
} else if(strpos($markupItemContent, '{class}') !== false) {
$markupItemContent = str_replace('{class}', '', $markupItemContent);
}
if($inputfield->className() != 'InputfieldWrapper') $ffOut = str_replace('{out}', $ffOut, $markupItemContent);
$out .= str_replace(array('{attrs}', '{out}'), array(trim($attrs), $label . $ffOut), $markup['item']);
$lastInputfield = $inputfield;
} // foreach($children as $inputfield)
if($out) {
$ulClass = $classes['list'];
@@ -603,6 +790,7 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
*
*/
public function ___renderValue() {
if(!count($this->children)) return '';
$this->addClass('InputfieldRenderValueMode');
$this->set('renderValueMode', true);
$out = $this->render();
@@ -626,9 +814,10 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
*/
public function ___renderInputfield(Inputfield $inputfield, $renderValueMode = false) {
$inputfieldID = $inputfield->attr('id');
$collapsed = $inputfield->getSetting('collapsed');
$ajaxInputfield = $collapsed == Inputfield::collapsedYesAjax ||
($collapsed == Inputfield::collapsedBlankAjax && $inputfield->isEmpty());
$ajaxInputfield = $collapsed == Inputfield::collapsedYesAjax || ($collapsed == Inputfield::collapsedBlankAjax && $inputfield->isEmpty());
$ajaxHiddenInput = "<input type='hidden' name='processInputfieldAjax[]' value='$inputfieldID' />";
$ajaxID = $this->wire('config')->ajax ? $this->wire('input')->get('renderInputfieldAjax') : '';
$required = $inputfield->getSetting('required');
@@ -638,6 +827,8 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
$ajaxInputfield = false;
if($collapsed == Inputfield::collapsedYesAjax) $inputfield->collapsed = Inputfield::collapsedYes;
if($collapsed == Inputfield::collapsedBlankAjax) $inputfield->collapsed = Inputfield::collapsedBlank;
// indicate to next processInput that this field can be processed
$inputfield->appendMarkup .= $ajaxHiddenInput;
}
$restoreValue = null; // value to restore, if we happen to modify it before render (renderValueMode only)
@@ -667,8 +858,6 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
if($ajaxInputfield) {
$inputfieldID = $inputfield->attr('id');
if($ajaxID && $ajaxID == $inputfieldID) {
// render ajax inputfield
$editable = $inputfield->editable();
@@ -676,7 +865,7 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
echo $inputfield->renderValue();
} else {
echo $inputfield->render();
echo "<input type='hidden' name='processInputfieldAjax[]' value='$inputfieldID' />";
echo $ajaxHiddenInput;
}
exit;
@@ -687,23 +876,8 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
return $this->renderInputfield($in, $renderValueMode);
} else {
// do not render ajax inputfield
$url = $this->wire('input')->url();
$queryString = $this->wire('input')->queryString();
if(strpos($queryString, 'renderInputfieldAjax=') !== false) {
// in case nested ajax request
$queryString = preg_replace('/&?renderInputfieldAjax=[^&]+/', '', $queryString);
}
$url .= $queryString ? "?$queryString&" : "?";
$url .= "renderInputfieldAjax=$inputfieldID";
$out = "<div class='renderInputfieldAjax'><input type='hidden' value='$url' /></div>";
if($inputfield instanceof InputfieldWrapper) {
// load assets they will need
foreach($inputfield->getAll() as $in) {
$in->renderReady($inputfield, $renderValueMode);
}
}
return $out;
// do not render ajax inputfield now, instead render placeholder
return $this->renderInputfieldAjaxPlaceholder($inputfield, $renderValueMode);
}
}
@@ -716,10 +890,49 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
$inputfield->resetTrackChanges();
}
if(is_null($out)) return '';
if(!strlen($out)) $out = '&nbsp;'; // prevent output from being skipped over
if(!strlen($out) && !$inputfield instanceof InputfieldWrapper) $out = '&nbsp;'; // prevent output from being skipped over
return $out;
}
/**
* Render a placeholder for an ajax-loaded Inputfield
*
* @param Inputfield $inputfield
* @param bool $renderValueMode
* @return string
*
*/
protected function renderInputfieldAjaxPlaceholder(Inputfield $inputfield, $renderValueMode) {
$inputfieldID = $inputfield->attr('id');
$url = $this->wire('input')->url();
$queryString = $this->wire('input')->queryString();
if(strpos($queryString, 'renderInputfieldAjax=') !== false) {
// in case nested ajax request
$queryString = preg_replace('/&?renderInputfieldAjax=[^&]+/', '', $queryString);
}
$url .= $queryString ? "?$queryString&" : "?";
$url .= "renderInputfieldAjax=$inputfieldID";
$out = "<div class='renderInputfieldAjax'><input type='hidden' value='$url' /></div>";
if($inputfield instanceof InputfieldWrapper) {
// load assets they will need
foreach($inputfield->getAll() as $in) {
$in->renderReady($inputfield, $renderValueMode);
}
}
// ensure that Inputfield::render() hooks are still called
if($inputfield->hasHook('render()')) {
$inputfield->runHooks('render', array(), 'before');
}
return $out;
}
/**
* Process input for all children
*
@@ -749,7 +962,9 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
// check if a value is required and field is empty, trigger an error if so
if($child->name && $child->getSetting('required') && $child->isEmpty()) {
$child->error($this->requiredLabel);
$requiredLabel = $child->getSetting('requiredLabel');
if(empty($requiredLabel)) $requiredLabel = $this->requiredLabel;
$child->error($requiredLabel);
}
}
@@ -837,6 +1052,34 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
return $empty;
}
/**
* Return Inputfields in this wrapper that are required and have empty values
*
* This method includes all children up through the tree, not just direct children.
*
* #pw-internal
*
* @param bool $required Only include empty Inputfields that are required? (default=true)
* @return array of Inputfield instances indexed by name attributes
*
*/
public function getEmpty($required = true) {
$a = array();
static $n = 0;
foreach($this->children as $child) {
if($child instanceof InputfieldWrapper) {
$a = array_merge($a, $child->getEmpty($required));
} else {
if($required && !$child->getSetting('required')) continue;
if(!$child->isEmpty()) continue;
$name = $child->attr('name');
if(empty($name)) $name = "_unknown" . (++$n);
$a[$name] = $child;
}
}
return $a;
}
/**
* Return an array of errors that occurred on any of the children during input processing.
*
@@ -878,6 +1121,61 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
}
}
/**
* Find an Inputfield below this one that has the given name
*
* This is an alternative to the `getChildByName()` method, with more options for when you need it.
* For instance, it can also accept a selector string or numeric index for the $name argument, and you
* can optionally disable the $recursive behavior.
*
* #pw-group-retrieval-and-traversal
*
* @param string|int $name Name or selector string of child to find, omit for first child, or specify zero-based index of child to return.
* @param bool $recursive Find child recursively? Looks for child in this wrapper, and all other wrappers below it. (default=true)
* @return Inputfield|null Returns Inputfield instance if found, or null if not.
* @since 3.0.110
*
*/
public function child($name = '', $recursive = true) {
$child = null;
if(!$this->children->count()) {
// no child possible
} else if(empty($name)) {
// first child
$child = $this->children->first();
} else if(is_int($name)) {
// number index
$child = $this->children->eq($name);
} else if($this->wire('sanitizer')->name($name) === $name) {
// child by name
$wrappers = array();
foreach($this->children as $f) {
if($f->getAttribute('name') === $name) {
$child = $f;
break;
} else if($recursive && $f instanceof InputfieldWrapper) {
$wrappers[] = $f;
}
}
if(!$child && $recursive && count($wrappers)) {
foreach($wrappers as $wrapper) {
$child = $wrapper->child($name, $recursive);
if($child) break;
}
}
} else if(Selectors::stringHasSelector($name)) {
// first child matching selector string
$child = $this->children("$name, limit=1")->first();
}
return $child;
}
/**
* Return all children Inputfields (alias of children method)
*
@@ -899,7 +1197,7 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
* #pw-internal
*
* @param bool $clear Set to true in order to clear the delayed children list.
* @return array
* @return array|Inputfield[]
*
*/
public function _getDelayedChildren($clear = false) {
@@ -1009,7 +1307,7 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
* #pw-internal
*
* @param bool $trackChanges
* @return $this
* @return Inputfield|InputfieldWrapper
*
*/
public function setTrackChanges($trackChanges = true) {
@@ -1023,7 +1321,7 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
* #pw-internal
*
* @param bool $trackChanges
* @return $this
* @return Inputfield|InputfieldWrapper
*
*/
public function resetTrackChanges($trackChanges = true) {

View File

@@ -19,6 +19,7 @@
* Item must have a gettable/settable 'id' property for this interface as well
*
* @property int $id
* @property string $name
*
*/
interface Saveable {
@@ -443,9 +444,115 @@ interface LanguagesValueInterface {
}
/**
* Interface used to indicate that the Fieldtype supports multiple languages
*
*/
interface FieldtypeLanguageInterface {
/*
* This interface is symbolic only and doesn't require any additional methods,
* however you do need to add an 'implements FieldtypeLanguageInterface' when defining your class.
*
*/
}
/**
* Interface for objects that carry a Field value for a Page
*
* Optional, but enables Page to do some of the work rather than the Fieldtype
*
*/
interface PageFieldValueInterface {
/**
* Get or set formatted state
*
* @param bool|null $set Specify bool to set formatted state or omit to retrieve formatted state
* @return bool
*
*/
public function formatted($set = null);
/**
* Set the Page
*
* @param Page $page
*
*/
public function setPage(Page $page);
/**
* Set the Field
*
* @param Field $field
*
*/
public function setField(Field $field);
/**
* Get the page or null if not set
*
* @return Page|null
*
*/
public function getPage();
/**
* Get the field or null if not set
*
* @return Field|null
*
*/
public function getField();
}
/**
* Interface for tracking runtime events
*
*/
interface WireProfilerInterface {
/**
* Start profiling an event
*
* Return the event array to be used for stop profiling
*
* @param string $name Name of event in format "method" or "method.id" or "something"
* @param Wire|object|string|null Source of event (may be object instance)
* @param array $data
* @return mixed Event to be used for stop call
*
*/
public function start($name, $source = null, $data = array());
/**
* Stop profiling an event
*
* @param array|object|string $event Event returned by start()
* @return void
*
*/
public function stop($event);
/**
* End of request maintenance
*
* @return void
*
*/
public function maintenance();
}
/**
* Inputfields that implement this interface always have a $value attribute that is an array
*
*/
interface InputfieldHasArrayValue { }
/**
* Inputfield that has a sortable value (usually in addition to InputfieldHasArrayValue)
*
*/
interface InputfieldHasSortableValue { }

View File

@@ -3,31 +3,156 @@
/**
* ProcessWire Language Functions
*
* Provide GetText like language translation functions to ProcessWire
* #pw-summary-translation Provides GetText-like language translation functions to ProcessWire.
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2019 by Ryan Cramer
* https://processwire.com
*
*
*/
/**
* Perform a language translation
*
* @param string $text Text for translation.
* @param string $textdomain Textdomain for the text, may be class name, filename, or something made up by you. If omitted, a debug backtrace will attempt to determine it automatically.
* @param string $context Name of context - DO NOT USE with this function for translation as it won't be parsed for translation. Use only with the _x() function, which will be parsed.
* @return string Translated text or original text if translation not available.
* This function enables you to specify static text as the argument, and that text becomes translatable (for each language)
* with ProcessWires built-in language translation tools. This function also works just fine if you do not have multi-language
* support installed, though in that case it just returns the text that it is given.
*
* For full documentation, please see the [Code Internationalization (i18n)](https://processwire.com/docs/multi-language-support/code-i18n/)
* documentation page at ProcessWire.com.
*
* This function is very similar to the GNU gettext `_('text');` function and essentially does the same thing, except that it is
* native to ProcessWire and not using GNU gettext.
*
* Use this `__('text')` function for common translations, use the `_x('text', 'context')` function for translations
* that also require additional context, and use the `_n('singular', 'plural', $n)` function for translations that should
* changed based on whether a value `$n` would require a singular or plural tense.
*
* ### Additional behaviors
*
* - You can optionally specify a “textdomain” as the second argument. The textdomain represents the source file of the
* translation. Most often it would be the current file, so the argument can be omitted, or you can specify `__FILE__`.
* But in some cases you may want to use a translation from another file, and the textdomain argument enables you to.
*
* - When in a class (or module) it is preferable to use `$this->_('text');` rather than `__('text');`, as it is slightly
* more efficient to do so.
*
* - A PHP comment, i.e. `// comment` that appears somewhere after a translation call (on the same line) is used as an
* additional description for the person translating text. Another comment after that, i.e. `// comment1 // comment2`
* is used as an additional secondary note for the person translating the text.
*
* ### Limitations
*
* - The function call (and translatable text within it) cannot span more than one line. If your translatable text is long
* enough to require multiple lines, split them into multiple calls (like one per sentence).
*
* - There cannot be more than one `__('text')` function call per line in the PHP code.
*
* - The provided text argument must be one string of static text. It cannot contain PHP variables or concatenation. To populate
* dynamic values you should use PHPs `sprintf()` (see examples below).
*
* ### Advanced features
*
* *The following features are for specific cases and supported in ProcessWire 3.0.125+ only.*
*
* You can use this function to get or set specific options that affect future calls by specifying boolean true
* for the first argument, option name for the second argument, and option value for the third argument.
* Currently there are just two options, “entityEncode” and “translations”. The “entityEncode” option enables
* you to control whether future calls return entity encoded text or not (see advanced examples).
*
* If given an array for the second argument then it is assumed that it is a list of predefined fallback
* translations you want to define. These translations will be used if the text is not translated in the
* admin. The translations are not specific to any textdomain and thus can serve as a fallback for any file.
* These are intended for front-end site usage and not recommended for use in modules. As such, they are not
* supported by the `$this->_('text');` function either. The array you provide should be associative, where the
* keys contain the text to translate, and the values contain the translation (see advanced examples).
*
*
* ~~~~~~
* // COMMON USAGE EXAMPLES --------------------------------------------------------------
*
* echo __('This is translatable text');
* echo __('Translatable with current file as textdomain (optional)', __FILE__);
* echo __('Translatable with other file as textdomain', '/site/templates/_init.php');
*
* // using placeholders to populate dynamic values in translatable text:
* echo sprintf(__('You are reading the %s page'), $page->title);
* echo sprintf(__('%d is the current page ID'), $page->id);
* echo sprintf(__('Today is %1$s and the time is %2$s'), date('l'), date('g:i a'));
*
* // providing a note via PHP comment to the person doing translation
* echo __('Welcome friend!'); // A friendly welcome message for new users
*
* // ADVANCED EXAMPLES (3.0.125+) -------------------------------------------------------
*
* // using the entityEncode option
* // true=always encode, 1=encode only if not already, false=never encode, null=undefined
* __(true, 'entityEncode', true);
*
* // get current entityEncode option value
* $val = __(true, 'entityEncode');
*
* // Setting predefined translations
* if($user->language->name == 'es') {
* __(true, [
* 'Hello' => 'Hola',
* 'World' => 'Mundo'
* ]);
* }
*
* // Setting predefined translations with context
* __(true, [
* // would apply only to a _x('Search', 'nav'); call (context)
* 'Search' => [ 'Buscar', 'nav' ]
* ]);
* ~~~~~~
*
* #pw-group-translation
*
* @param string|bool $text Text for translation.
* @param string|array $textdomain Textdomain for the text, may be class name, filename, or something made up by you.
* If omitted, a debug backtrace will attempt to determine it automatically.
* @param string|bool|array $context Name of context - DO NOT USE with this function for translation as it will not be parsed for translation.
* Use only with the `_x()` function, which will be parsed.
* @return string|array Translated text or original text if translation not available. Returns array only if getting/setting options.
* @see _x(), _n()
* @link https://processwire.com/docs/multi-language-support/code-i18n/
*
*/
function __($text, $textdomain = null, $context = '') {
if(!wire('languages')) return $text;
if(!$language = wire('user')->language) return $text;
static $options = array(
'entityEncode' => null, // true=always, false=never, 1=only if not already encoded, null=undefined (backwards compatible behavior)
'translations' => array(), // fallback translations to use when live translation not available ['Original text' => 'Translated text']
'_useLimit' => null, // internal use: use limit argument for debug_backtrace call
);
if($text === true) {
// set and get options
if(is_array($textdomain)) {
// translations specified as array in $textdomain argument
$context = $textdomain;
$textdomain = 'translations';
}
// merge existing translations if specified
if($textdomain == 'translations' && is_array($context)) $context = array_merge($options['translations'], $context);
if($context !== '') $options[$textdomain] = $context;
return $options[$textdomain];
}
if(!wire('languages') || (!$language = wire('user')->language) || !$language->id) {
// multi-language not installed or not available
return $options['entityEncode'] ? htmlspecialchars($text, ENT_QUOTES, 'UTF-8', $options['entityEncode'] === true) : $text;
}
/** @var Language $language */
if(!$language->id) return $text;
if(is_null($textdomain)) {
if(defined('DEBUG_BACKTRACE_IGNORE_ARGS')) {
if($options['_useLimit'] === null) {
$options['_useLimit'] = version_compare(PHP_VERSION, '5.4.0') >= 0;
}
if($textdomain === null) {
if($options['_useLimit']) {
// PHP 5.4.0 or newer
$traces = @debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
} else if(defined('DEBUG_BACKTRACE_IGNORE_ARGS')) {
// PHP 5.3.6 or newer
$traces = @debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
} else {
// older PHP (deprecated)
$traces = @debug_backtrace();
}
if(isset($traces[0]) && $traces[0]['file'] != __FILE__) {
@@ -36,21 +161,55 @@ function __($text, $textdomain = null, $context = '') {
$textdomain = $traces[1]['file'];
}
if(is_null($textdomain)) $textdomain = 'site';
} else if($textdomain === 'common') {
// common translation
$textdomain = 'wire/modules/LanguageSupport/LanguageTranslator.php';
}
$value = htmlspecialchars($language->translator()->getTranslation($textdomain, $text, $context), ENT_QUOTES, 'UTF-8');
if($value === "=") $value = $text;
$value = $language->translator()->getTranslation($textdomain, $text, $context);
$encode = $options['entityEncode'];
if($value === "=") {
// translated value should be same as source value
$value = $text;
} else if($value === "+") {
// use common translation value if available
$v = $language->translator()->commonTranslation($text);
$value = empty($v) ? $text : $v;
} else {
// regular translation
// if translated value same as original check if alternate available in pre-defined translations
if($value === $text && isset($options['translations']["$text"])) {
$value = $options['translations']["$text"];
// array for translation means only apply to named context, ie. 'old text' => [ 'new text', 'context' ]
if(is_array($value)) $value = isset($value[1]) && $value[1] === $context ? $value[0] : $text;
}
// force original behavior fallback if encode mode not set (i.e. encode when translation available)
if($encode === null) $encode = 1;
}
if($encode) $value = htmlspecialchars($value, ENT_QUOTES, 'UTF-8', $encode === true);
return $value;
}
/**
* Perform a language translation in a specific context
*
* Used when to text strings might be the same in English, but different in other languages.
* Used when two or more text strings might be the same in default language, but different in other languages.
* This enables you to limit the context of the translation to a named context, like "button" or "headline" or
* whatever name you decide to use.
*
* ~~~~~
* echo _x('Click for more', 'button');
* echo _x('Click for more', 'text-link');
* ~~~~~
*
* #pw-group-translation
*
* @param string $text Text for translation.
* @param string $context Name of context
* @param string $textdomain Textdomain for the text, may be class name, filename, or something made up by you. If omitted, a debug backtrace will attempt to determine automatically.
* @param string $textdomain Textdomain for the text, may be class name, filename, or something made up by you.
* If omitted, a debug backtrace will attempt to determine automatically.
* @return string Translated text or original text if translation not available.
* @see __(), _n()
* @link https://processwire.com/docs/multi-language-support/code-i18n/
*
*/
function _x($text, $context, $textdomain = null) {
@@ -60,11 +219,23 @@ function _x($text, $context, $textdomain = null) {
/**
* Perform a language translation with singular and plural versions
*
* ~~~~~
* $items = array(...);
* $qty = count($items);
* echo _n('Found one item', 'Found multiple items', $qty);
* echo sprintf(_n('Found one item', 'Found %d items', $qty), $qty);
* ~~~~~
*
* #pw-group-translation
*
* @param string $textSingular Singular version of text (when there is 1 item)
* @param string $textPlural Plural version of text (when there are multiple items or 0 items)
* @param int $count Quantity of items, should be 0 or more.
* @param string $textdomain Textdomain for the text, may be class name, filename, or something made up by you. If omitted, a debug backtrace will attempt to determine automatically.
* @param string $textdomain Textdomain for the text, may be class name, filename, or something made up by you.
* If omitted, a debug backtrace will attempt to determine automatically.
* @return string Translated text or original text if translation not available.
* @see __(), _x()
* @link https://processwire.com/docs/multi-language-support/code-i18n/
*
*/
function _n($textSingular, $textPlural, $count, $textdomain = null) {

View File

@@ -56,7 +56,17 @@ class MarkupFieldtype extends WireData implements Module {
* @var bool
*
*/
protected $renderIsUseless = false;
protected $renderIsUseless = false;
/**
* Properties that are potentially linkable to source page in markup
*
* @var array
*
*/
protected $linkableProperties = array(
'name', 'url', 'httpUrl', 'path', 'title',
);
/**
* Construct the MarkupFieldtype
@@ -102,7 +112,22 @@ class MarkupFieldtype extends WireData implements Module {
$valid = false;
if($value instanceof PageArray) {
// PageArray object: get array of property value from each item
$value = $value->explode($property, array('getMethod' => 'getFormatted'));
$field = $this->wire('fields')->get($property);
if(is_object($field) && $field->type) {
$a = array();
foreach($value as $page) {
$v = $page->getFormatted($property);
$v = $field->type->markupValue($page, $field, $v);
if($this->isLinkablePageProperty($page, $property)) {
$a[] = "<a href='$page->url'>$v</a>";
} else {
$a[] = $v;
}
}
return $this->arrayToString($a, false);
} else {
$value = $value->explode($property, array('getMethod' => 'getFormatted'));
}
$valid = true;
} else if($value instanceof WireArray) {
@@ -115,8 +140,24 @@ class MarkupFieldtype extends WireData implements Module {
$page = $value;
$value = $page->getFormatted($property);
$field = $this->wire('fields')->get($property);
if($field && $field->type) return $field->type->markupValue($page, $field, $value);
if(is_object($field) && $field->type) return $field->type->markupValue($page, $field, $value);
$valid = true;
} else if($value instanceof LanguagesValueInterface) {
/** @var LanguagesValueInterface $value */
/** @var Languages $languages */
$languages = $this->wire('languages');
if($property) {
if($property === 'data') {
$languageID = $languages->getDefault()->id;
} else if(is_string($property) && preg_match('/^data(\d+)$/', $property, $matches)) {
$languageID = (int) $matches[1];
} else {
$languageID = 0;
}
$value = $languageID ? $value->getLanguageValue($languageID) : (string) $value;
} else {
$value = (string) $value;
}
} else if($value instanceof WireData) {
// WireData object
@@ -222,10 +263,11 @@ class MarkupFieldtype extends WireData implements Module {
* Convert any value to a string
*
* @param mixed $value
* @param bool $encode
* @return string
*
*/
protected function valueToString($value) {
protected function valueToString($value, $encode = true) {
if(is_object($value) && ($value instanceof Pagefiles || $value instanceof Pagefile)) {
return $this->objectToString($value);
} else if(WireArray::iterable($value)) {
@@ -233,7 +275,7 @@ class MarkupFieldtype extends WireData implements Module {
} else if(is_object($value)) {
return $this->objectToString($value);
} else {
return $this->wire('sanitizer')->entities1($value);
return $encode ? $this->wire('sanitizer')->entities1($value) : $value;
}
}
@@ -241,14 +283,15 @@ class MarkupFieldtype extends WireData implements Module {
* Render an unknown array or WireArray to a string
*
* @param array|WireArray $value
* @param bool $encode
* @return string
*
*/
protected function arrayToString($value) {
protected function arrayToString($value, $encode = true) {
if(!count($value)) return '';
$out = "<ul class='MarkupFieldtype'>";
foreach($value as $v) {
$out .= "<li>" . $this->valueToString($v) . "</li>";
$out .= "<li>" . $this->valueToString($v, $encode) . "</li>";
}
$out .= "</ul>";
return $out;
@@ -262,8 +305,16 @@ class MarkupFieldtype extends WireData implements Module {
*
*/
protected function objectToString($value) {
if($value instanceof WireArray && !$value->count()) return '';
if($value instanceof Page) return $value->get('title|name');
if($value instanceof WireArray) {
if(!$value->count()) return '';
}
if($value instanceof Page) {
if($value->viewable()) {
return "<a href='$value->url'>" . $value->get('title|name') . "</a>";
} else {
return $value->get('title|name');
}
}
if($value instanceof Pagefiles || $value instanceof Pagefile) {
$out = $this->renderInputfieldValue($value);
} else {
@@ -315,6 +366,20 @@ class MarkupFieldtype extends WireData implements Module {
return $out;
}
/**
* Is the given page property/field name one that should be linked to the source page in output?
*
* @param Page $page
* @param $property
* @return bool
*
*/
protected function isLinkablePageProperty(Page $page, $property) {
if(!in_array($property, $this->linkableProperties)) return false;
if(!$page->viewable($property)) return false;
return true;
}
/**
* The string value of a MarkupFieldtype is always the fully rendered field
*

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