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

102 Commits

Author SHA1 Message Date
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
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
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
208 changed files with 6794 additions and 2667 deletions

View File

@@ -1039,7 +1039,9 @@ class Installer {
$superuserRole = $wire->roles->get("name=superuser");
$user = $wire->users->get($wire->config->superUserPageID);
if(!$user->id) {
if($user->id) {
$user->of(false);
} else {
$user = new User();
$user->id = $wire->config->superUserPageID;
}

View File

@@ -26,6 +26,8 @@ if(!defined("PROCESSWIRE")) die();
/*** SITE CONFIG *************************************************************************/
/** @var Config $config */
/**
* Enable debug mode?
*

View File

@@ -26,6 +26,8 @@ if(!defined("PROCESSWIRE")) die();
/*** SITE CONFIG *************************************************************************/
/** @var Config $config */
/**
* Enable debug mode?
*

View File

@@ -26,6 +26,8 @@ if(!defined("PROCESSWIRE")) die();
/*** SITE CONFIG *************************************************************************/
/** @var Config $config */
/**
* Enable debug mode?
*

View File

@@ -26,6 +26,8 @@ if(!defined("PROCESSWIRE")) die();
/*** SITE CONFIG *************************************************************************/
/** @var Config $config */
/**
* Enable debug mode?
*

View File

@@ -26,6 +26,8 @@ if(!defined("PROCESSWIRE")) die();
/*** SITE CONFIG *************************************************************************/
/** @var Config $config */
/**
* Enable debug mode?
*

View File

@@ -111,9 +111,22 @@ $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;
/*** 2. DATES & TIMES *************************************************************************/
@@ -261,6 +274,12 @@ $config->sessionFingerprint = 1;
*/
$config->sessionCookieSecure = 1;
/**
* Cookie domain
*
*/
$config->sessionCookieDomain = null;
/**
* Number of session history entries to record.
*
@@ -820,6 +839,37 @@ $config->dbHost = '';
*/
$config->dbPort = 3306;
/**
* Database init command (PDO::MYSQL_ATTR_INIT_COMMAND)
*
* Note: Placeholder "{charset}" gets automatically replaced with $config->dbCharset.
*
* @var string
*
*/
$config->dbInitCommand = "SET NAMES '{charset}'";
/**
* 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->dbSqlModes = array(
"5.7.0" => "remove:STRICT_TRANS_TABLES,ONLY_FULL_GROUP_BY"
);
/**
* Optional DB socket config for sites that need it (for most you should exclude this)
*

View File

@@ -1,4 +1,4 @@
<?php
<?php
/**
* ProcessWire PhpStorm Meta
*
@@ -14,64 +14,64 @@ 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,
'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,
'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

@@ -17,6 +17,7 @@
*
* @method void install()
* @method void uninstall()
* @method array getExtraMarkup()
*
*/

View File

@@ -20,6 +20,7 @@
* @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
@@ -99,6 +100,8 @@
* @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 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 array $dbSqlModes Set, add or remove SQL mode based on MySQL version. See default in /wire/config.php for details. #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
@@ -114,6 +117,7 @@
* @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 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
@@ -211,7 +215,7 @@ class Config extends WireData {
* @return null|string
*
*/
public function paths($for) { return $this->paths($for); }
public function paths($for) { return $this->path($for); }
/**
* List of config keys that are also exported in javascript

View File

@@ -7,6 +7,8 @@
* 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
*
@@ -29,6 +31,12 @@
* @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
*
* 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
*
* @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
@@ -997,6 +1005,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
*
*/

View File

@@ -19,6 +19,8 @@ class Fieldgroups extends WireSaveableItemsLookup {
/**
* Instances of FieldgroupsArray
*
* @var FieldgroupsArray
*
*/
protected $fieldgroupsArray;
@@ -60,6 +62,8 @@ class Fieldgroups extends WireSaveableItemsLookup {
/**
* Per WireSaveableItems interface, return all available Fieldgroup instances
*
* @return FieldgroupsArray
*
*/
public function getAll() {
@@ -68,6 +72,8 @@ class Fieldgroups extends WireSaveableItemsLookup {
/**
* Per WireSaveableItems interface, create a blank instance of a Fieldgroup
*
* @return Fieldgroup
*
*/
public function makeBlankItem() {
@@ -76,6 +82,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 +92,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() {

View File

@@ -396,13 +396,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
*
*/

View File

@@ -36,6 +36,7 @@
* @property Languages $languages If LanguageSupport installed
* @property Config $config
* @property Fuel $fuel
* @property WireProfilerInterface $profiler
*
*/
class Fuel implements \IteratorAggregate {
@@ -56,6 +57,16 @@ class Fuel implements \IteratorAggregate {
*/
protected $lock = array();
/**
* API vars that require specific interfaces
*
* @var array
*
*/
protected $requiredInterfaces = array(
'profiler' => 'WireProfilerInterface'
);
/**
* @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 +79,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;

View File

@@ -167,7 +167,7 @@ function wireRmdir($path, $recursive = false) {
* @param string $path May be a directory or a filename
* @param bool $recursive If set to true, all files and directories in $path will be recursively set as well.
* @param string $chmod If you want to set the mode to something other than PW's chmodFile/chmodDir settings,
you may override it by specifying it here. Ignored otherwise. Format should be a string, like "0755".
* you may override it by specifying it here. Ignored otherwise. Format should be a string, like "0755".
* @return bool Returns true if all changes were successful, or false if at least one chmod failed.
* @throws WireException when it receives incorrect chmod format
*
@@ -770,3 +770,76 @@ function wireIsCallable($var, $syntaxOnly = false, &$callableName = '') {
if(is_string($var)) $var = wireClassName($var, true);
return is_callable($var, $syntaxOnly, $callableName);
}
/**
* Get or set an output region (primarily for front-end output usage)
*
* ~~~~~
* // 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('*', '');
* ~~~~~
*
* @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.
* @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 wireRegion($key, $value = null) {
static $regions = array();
if(empty($key) || $key === '*') {
// all regions
if($value === '') $regions = array(); // clear
return $regions;
}
if(is_null($value)) {
// get region
$result = isset($regions[$key]) ? $regions[$key] : null;
} else {
// set region
$pos = strpos($key, '+');
if($pos !== false) $key = trim($key, '+');
if(!isset($regions[$key])) $regions[$key] = '';
if($pos === 0) {
// prepend
$regions[$key] = $value . $regions[$key];
} else if($pos) {
// append
$regions[$key] .= $value;
} else if($value === '') {
// clear region
unset($regions[$key]);
} else {
// insert/replace
$regions[$key] = $value;
}
$result = true;
}
return $result;
}

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

@@ -0,0 +1,432 @@
<?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.
*
* Note: This file is only used if $config->useFunctionsAPI == true;
*
*/
/**
* 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 pages($selector = '') {
return wirePages($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 page($key = '', $value = null) {
return wirePage($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 config($key = '', $value = null) {
return wireConfig($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 modules($name = '') {
return wireModules($name);
}
/**
* 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 user($key = '', $value = null) {
return wireUser($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 users($selector = '') {
return wireUsers($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 session($key = '', $value = null) {
return wireSession($key, $value);
}
/**
* Access the $fields API variable as a function
*
* @param string $name Optional field name to retrieve
* @return Fields|Field|null
*
*/
function fields($name = '') {
return wireFields($name);
}
/**
* Access the $templates API variable as a function
*
* @param string $name Optional template to retrieve
* @return Templates|Template|null
*
*/
function templates($name = '') {
return wireTemplates($name);
}
/**
* Access the $database API variable as a function
*
* @return WireDatabasePDO
*
*/
function database() {
return wireDatabase();
}
/**
* 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 permissions($selector = '') {
return wirePermissions($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 roles($selector = '') {
return wireRoles($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 sanitizer($name = '', $value = '') {
return wireSanitizer($name, $value);
}
/**
* 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 datetime($format = '', $value = '') {
return wireDatetime($format, $value);
}
/**
* Access the $files API variable as a function
*
* @return WireFileTools
*
*/
function files() {
return wireFiles();
}
/**
* 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 cache($name = '', $expire = null, $func = null) {
return wireCache($name, $expire, $func);
}
/**
* 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 languages($name = '') {
return wireLanguages($name);
}
/**
* 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 = input(); // Returns $input API var (WireInput)
* $post = input('post'); // Returns $input->post (WireInputData)
* $value = input('get', 'sort'); // Returns $input->get('sort');
* $value = input('get', 'sort', 'fieldName'); // Returns $input->get('sort') run through $sanitizer->fieldName().
* ~~~~~
*
* @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
* @return WireInput|WireInputData array|string|int|null
*
*/
function input($type = '', $key = '', $sanitizer = '') {
return wireInput($type, $key, $sanitizer);
}
/**
* 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
* @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.
*
* @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.
*
* @param string $key
* @param string $sanitizer
* @return WireInputData|string|int|array|null
*
*/
function inputCookie($key = '', $sanitizer = '') {
return wireInputCookie($key, $sanitizer);
}
/**
* Function that returns a $config->urls->[name] value o
*
* @param string $key
* @return null|Paths|string
*
*/
function urls($key = '') {
return wireUrls($key);
}
/**
* Function that returns a $config->paths->[name] value o
*
* @param string $key
* @return null|Paths|string
*
*/
function paths($key = '') {
return wirePaths($key);
}
/**
* 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 profiler($name = null, $source = null, $data = array()) {
return wireProfiler($name, $source, $data);
}
/**
* Get or set a region for front-end output
*
* ~~~~~
* // 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('*', '');
*
* ~~~~~
*
* @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.
* @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);
}

View File

@@ -0,0 +1,506 @@
<?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 = 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 = input(); // Returns $input API var (WireInput)
* $post = input('post'); // Returns $input->post (WireInputData)
* $value = input('get', 'sort'); // Returns $input->get('sort');
* $value = input('get', 'sort', 'fieldName'); // Returns $input->get('sort') run through $sanitizer->fieldName().
* ~~~~~
*
* @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
* @return WireInput|WireInputData array|string|int|null
*
*/
function wireInput($type = '', $key = '', $sanitizer = '') {
/** @var WireInput $input */
$input = wire('input');
if(!strlen($type)) return $input;
$type = strtolower($type);
if(!strlen($key)) return $input->$type;
$value = $input->$type($key);
if(strlen($sanitizer)) $value = wireSanitizer($sanitizer, $value);
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
* @param string $sanitizer
* @return WireInputData|string|int|array|null
*
*/
function wireInputGet($key = '', $sanitizer = '') {
return wireInput('get', $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.
*
* @param string $key
* @param string $sanitizer
* @return WireInputData|string|int|array|null
*
*/
function wireInputPost($key = '', $sanitizer = '') {
return wireInput('post', $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.
*
* @param string $key
* @param string $sanitizer
* @return WireInputData|string|int|array|null
*
*/
function wireInputCookie($key = '', $sanitizer = '') {
return wireInput('cookie', $key, $sanitizer);
}
/**
* 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);
}

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.

View File

@@ -228,9 +228,9 @@ 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['interlace'] = $gih->m_bInterlace;

View File

@@ -13,6 +13,8 @@
*
* Copyright (C) 2016 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 {

View File

@@ -42,15 +42,15 @@ 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_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:
@@ -409,6 +409,7 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
$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 = '';
@@ -899,7 +900,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) {

View File

@@ -443,6 +443,43 @@ interface LanguagesValueInterface {
}
/**
* 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
*

View File

@@ -38,7 +38,12 @@ function __($text, $textdomain = null, $context = '') {
if(is_null($textdomain)) $textdomain = 'site';
}
$value = htmlspecialchars($language->translator()->getTranslation($textdomain, $text, $context), ENT_QUOTES, 'UTF-8');
if($value === "=") $value = $text;
if($value === "=") {
$value = $text;
} else if($value === "+") {
$v = $language->translator()->commonTranslation($text);
$value = empty($v) ? $text : $v;
}
return $value;
}

View File

@@ -14,6 +14,17 @@
* https://processwire.com
*
* #pw-summary Loads and manages all modules in ProcessWire.
* #pw-body =
* The `$modules` API variable is most commonly used for getting individual modules to use their API.
* ~~~~~
* // Getting a module by name
* $m = $modules->get('MarkupPagerNav');
*
* // Getting a module by name (alternate)
* $m = $modules->MarkupPagerNav;
* ~~~~~
*
* #pw-body
*
* @todo Move all module information methods to a ModulesInfo class
* @todo Move all module loading methods to a ModulesLoad class
@@ -65,7 +76,13 @@ class Modules extends WireArray {
* When combined with flagsAutoload, indicates that the module's autoload state is temporarily disabled
*
*/
const flagsDisabled = 16;
const flagsDisabled = 16;
/**
* Indicates module that maintains a configurable interface but with no interactive Inputfields
*
*/
const flagsNoUserConfig = 32;
/**
* Filename for module info cache file
@@ -497,6 +514,22 @@ class Modules extends WireArray {
// attempt 2.x module in dedicated namespace or root namespace
$className = $this->getModuleNamespace($moduleName) . $moduleName;
}
if(ProcessWire::getNumInstances() > 1) {
// in a multi-instance environment, ensures that anything happening during
// the module __construct is using the right instance. necessary because the
// construct method runs before the wire instance is set to the module
$wire1 = ProcessWire::getCurrentInstance();
$wire2 = $this->wire();
if($wire1 !== $wire2) {
ProcessWire::setCurrentInstance($wire2);
} else {
$wire1 = null;
}
} else {
$wire1 = null;
$wire2 = null;
}
try {
$module = $this->wire(new $className());
@@ -505,6 +538,7 @@ class Modules extends WireArray {
$module = null;
}
if($this->debug) $this->debugTimerStop($debugKey);
if($wire1) ProcessWire::setCurrentInstance($wire1);
return $module;
}
@@ -647,8 +681,11 @@ class Modules extends WireArray {
$module = $this->newModule($className);
if($module) {
$this->set($className, $module);
$this->initModule($module);
if($this->debug) $this->message("Conditional autoload: $className LOADED");
if($this->initModule($module)) {
if($this->debug) $this->message("Conditional autoload: $className LOADED");
} else {
if($this->debug) $this->warning("Failed conditional autoload: $className");
}
}
} else {
@@ -1130,7 +1167,7 @@ class Modules extends WireArray {
* Get the requested Module (with options)
*
* This is the same as `$modules->get()` except that you can specify additional options to modify default behavior.
* These are the options you can speicfy in the `$options` array argument:
* These are the options you can specify in the `$options` array argument:
*
* - `noPermissionCheck` (bool): Specify true to disable module permission checks (and resulting exception).
* - `noInstall` (bool): Specify true to prevent a non-installed module from installing from this request.
@@ -1205,7 +1242,9 @@ class Modules extends WireArray {
if($module && $needsInit) {
// if the module is configurable, then load it's config data
// and set values for each before initializing the module
if(empty($options['noInit'])) $this->initModule($module, false);
if(empty($options['noInit'])) {
if(!$this->initModule($module, false)) $module = null;
}
}
return $module;
@@ -2428,7 +2467,8 @@ class Modules extends WireArray {
* $moduleInfo = $modules->getModuleInfoVerbose('MarkupAdminDataTable');
* ~~~~~
*
* @param string|Module|int $class May be class name, module instance, or module ID
* @param string|Module|int $class May be class name, module instance, or module ID.
* Specify "*" or "all" to retrieve module info for all modules.
* @param array $options Optional options to modify behavior of what gets returned
* - `verbose` (bool): Makes the info also include additional properties (they will be usually blank without this option specified).
* - `noCache` (bool): prevents use of cache to retrieve the module info.
@@ -2516,11 +2556,22 @@ class Modules extends WireArray {
if(!count($info)) $info = $this->getModuleInfoInternal($module);
}
} else if($module == 'PHP' || $module == 'ProcessWire') {
} else if($module == 'PHP' || $module == 'ProcessWire') {
// module is a system
$info = $this->getModuleInfoSystem($module);
$info = $this->getModuleInfoSystem($module);
return array_merge($infoTemplate, $info);
} else if($module === '*' || $module === 'all') {
if(empty($this->moduleInfoCache)) $this->loadModuleInfoCache();
$modulesInfo = $this->moduleInfoCache;
if($options['verbose']) {
if(empty($this->moduleInfoCacheVerbose)) $this->loadModuleInfoCacheVerbose();
foreach($this->moduleInfoCacheVerbose as $moduleID => $moduleInfoVerbose) {
$modulesInfo[$moduleID] = array_merge($modulesInfo[$moduleID], $moduleInfoVerbose);
}
}
return $modulesInfo;
} else {
// module is a class name or ID
@@ -2904,34 +2955,38 @@ class Modules extends WireArray {
* #pw-changelog 3.0.16 Changed from more verbose name `getModuleConfigData()`, which can still be used.
*
* @param string|Module $class
* @param string $property Optionally just get value for a specific property (omit to get all config)
* @return array Module configuration data
* @see Modules::saveConfig()
* @since 3.0.16 Use method getModuleConfigData() with same arguments for prior versions (can also be used on any version).
*
*/
public function getConfig($class) {
public function getConfig($class, $property = '') {
$emptyReturn = $property ? null : array();
$className = $class;
if(is_object($className)) $className = wireClassName($className->className(), false);
if(!$id = $this->moduleIDs[$className]) return array();
if(!isset($this->configData[$id])) return array(); // module has no config data
if(is_array($this->configData[$id])) return $this->configData[$id];
// first verify that module doesn't have a config file
$configurable = $this->isConfigurable($className);
if(!$configurable) return array();
if(!$id = $this->moduleIDs[$className]) return $emptyReturn;
if(!isset($this->configData[$id])) return $emptyReturn; // module has no config data
$database = $this->wire('database');
$query = $database->prepare("SELECT data FROM modules WHERE id=:id", "modules.getConfig($className)"); // QA
$query->bindValue(":id", (int) $id, \PDO::PARAM_INT);
$query->execute();
$data = $query->fetchColumn();
$query->closeCursor();
if(is_array($this->configData[$id])) {
$data = $this->configData[$id];
} else {
// first verify that module doesn't have a config file
$configurable = $this->isConfigurable($className);
if(!$configurable) return $emptyReturn;
$database = $this->wire('database');
$query = $database->prepare("SELECT data FROM modules WHERE id=:id", "modules.getConfig($className)"); // QA
$query->bindValue(":id", (int) $id, \PDO::PARAM_INT);
$query->execute();
$data = $query->fetchColumn();
$query->closeCursor();
if(strlen($data)) $data = wireDecodeJSON($data);
if(empty($data)) $data = array();
$this->configData[$id] = $data;
}
if(empty($data)) $data = array();
else $data = wireDecodeJSON($data);
if(empty($data)) $data = array();
$this->configData[$id] = $data;
if($property) return isset($data[$property]) ? $data[$property] : null;
return $data;
}
@@ -3421,18 +3476,35 @@ class Modules extends WireArray {
* #pw-changelog 3.0.16 Changed name from the more verbose saveModuleConfigData(), which will still work.
*
* @param string|Module $class Module or module name
* @param array $data Associative array of configuration data
* @param array|string $data Associative array of configuration data, or name of property you want to save.
* @param mixed|null $value If you specified a property in previous arg, the value for the property.
* @return bool True on success, false on failure
* @throws WireException
* @see Modules::getConfig()
* @since 3.0.16 Use method saveModuleConfigData() with same arguments for prior versions (can also be used on any version).
*
*/
public function ___saveConfig($class, array $data) {
public function ___saveConfig($class, $data, $value = null) {
$className = $class;
if(is_object($className)) $className = $className->className();
$moduleName = wireClassName($className, false);
if(!$id = $this->moduleIDs[$moduleName]) throw new WireException("Unable to find ID for Module '$moduleName'");
if(is_string($data)) {
// a property and value have been provided
$property = $data;
$data = $this->getConfig($class);
if(is_null($value)) {
// remove the property
unset($data[$property]);
} else {
// populate the value for the property
$data[$property] = $value;
}
} else {
// data must be an associative array of configuration data
if(!is_array($data)) return false;
}
// ensure original duplicates info is retained and validate that it is still current
$data = $this->duplicates()->getDuplicatesConfigData($moduleName, $data);
@@ -3445,6 +3517,7 @@ class Modules extends WireArray {
$query->bindValue(":id", (int) $id, \PDO::PARAM_INT);
$result = $query->execute();
$this->log("Saved module '$moduleName' config data");
return $result;
}
@@ -3521,60 +3594,88 @@ class Modules extends WireArray {
// check for file-based config
$file = $this->isConfigurable($moduleName, "file");
if(!$file || !is_string($file) || !is_file($file)) return $form;
$config = null;
$ns = $this->getModuleNamespace($moduleName);
$configClass = $ns . $moduleName . "Config";
if(!class_exists($configClass)) {
$configFile = $this->compile($moduleName, $file, $ns);
if($configFile) {
/** @noinspection PhpIncludeInspection */
include_once($configFile);
}
}
$configModule = null;
if(wireClassExists($configClass)) {
// file contains a ModuleNameConfig class
$configModule = $this->wire(new $configClass());
if(!$file || !is_string($file) || !is_file($file)) {
// config is not file-based
} else {
if(is_null($config)) {
// file-based config
$config = null;
$ns = $this->getModuleNamespace($moduleName);
$configClass = $ns . $moduleName . "Config";
if(!class_exists($configClass)) {
$configFile = $this->compile($moduleName, $file, $ns);
// if(!$configFile) $configFile = $compile ? $this->wire('files')->compile($file) : $file;
if($configFile) {
/** @noinspection PhpIncludeInspection */
include($configFile); // in case of previous include_once
include_once($configFile);
}
}
if(is_array($config)) {
// file contains a $config array
$configModule = $this->wire(new ModuleConfig());
$configModule->add($config);
$configModule = null;
if(wireClassExists($configClass)) {
// file contains a ModuleNameConfig class
$configModule = $this->wire(new $configClass());
} else {
if(is_null($config)) {
$configFile = $this->compile($moduleName, $file, $ns);
// if(!$configFile) $configFile = $compile ? $this->wire('files')->compile($file) : $file;
if($configFile) {
/** @noinspection PhpIncludeInspection */
include($configFile); // in case of previous include_once
}
}
if(is_array($config)) {
// file contains a $config array
$configModule = $this->wire(new ModuleConfig());
$configModule->add($config);
}
}
}
if($configModule && $configModule instanceof ModuleConfig) {
$defaults = $configModule->getDefaults();
$data = array_merge($defaults, $data);
$configModule->setArray($data);
$fields = $configModule->getInputfields();
if($fields instanceof InputfieldWrapper) {
foreach($fields as $field) {
$form->append($field);
}
foreach($data as $key => $value) {
$f = $form->getChildByName($key);
if(!$f) continue;
if($f instanceof InputfieldCheckbox && $value) {
$f->attr('checked', 'checked');
} else {
$f->attr('value', $value);
}
}
} else {
$this->error("$configModule.getInputfields() did not return InputfieldWrapper");
}
}
} // file-based config
if($configModule && $configModule instanceof ModuleConfig) {
$defaults = $configModule->getDefaults();
$data = array_merge($defaults, $data);
$configModule->setArray($data);
$fields = $configModule->getInputfields();
if($fields instanceof InputfieldWrapper) {
foreach($fields as $field) {
$form->append($field);
}
foreach($data as $key => $value) {
$f = $form->getChildByName($key);
if(!$f) continue;
if($f instanceof InputfieldCheckbox && $value) {
$f->attr('checked', 'checked');
} else {
$f->attr('value', $value);
if($form) {
// determine how many visible Inputfields there are in the module configuration
// for assignment or removal of flagsNoUserConfig flag when applicable
$numVisible = 0;
foreach($form->getAll() as $inputfield) {
if($inputfield instanceof InputfieldHidden || $inputfield instanceof InputfieldWrapper) continue;
$numVisible++;
}
$flags = $this->getFlags($moduleName);
if($numVisible) {
if($flags & self::flagsNoUserConfig) {
$info = $this->getModuleInfoVerbose($moduleName);
if(empty($info['addFlag']) || !($info['addFlag'] & self::flagsNoUserConfig)) {
$this->setFlag($moduleName, self::flagsNoUserConfig, false); // remove flag
}
}
} else {
$this->error("$configModule.getInputfields() did not return InputfieldWrapper");
if(!($flags & self::flagsNoUserConfig)) {
if(empty($info['removeFlag']) || !($info['removeFlag'] & self::flagsNoUserConfig)) {
$this->setFlag($moduleName, self::flagsNoUserConfig, true); // add flag
}
}
}
}
@@ -4298,6 +4399,23 @@ class Modules extends WireArray {
if($flags & self::flagsSingular) $this->setFlag($moduleID, self::flagsSingular, false);
}
// handle addFlag and removeFlag moduleInfo properties
foreach(array(0 => 'removeFlag', 1 => 'addFlag') as $add => $flagsType) {
if(empty($info[$flagsType])) continue;
if($flags & $info[$flagsType]) {
// already has the flags
if(!$add) {
// remove the flag(s)
$this->setFlag($moduleID, $info[$flagsType], false);
}
} else {
// does not have the flags
if($add) {
// add the flag(s)
$this->setFlag($moduleID, $info[$flagsType], true);
}
}
}
}
/**
@@ -4610,6 +4728,8 @@ class Modules extends WireArray {
/**
* Compile and return the given file for module, if allowed to do so
*
* #pw-internal
*
* @param Module|string $moduleName
* @param string $file Optionally specify the module filename as an optimization
* @param string|null $namespace Optionally specify namespace as an optimization

View File

@@ -3,6 +3,10 @@
/**
* ProcessWire Notices
*
* #pw-summary Manages notifications in the ProcessWire admin, primarily for internal use.
* #pw-use-constants
* #pw-use-constructor
*
* Base class that holds a message, source class, and timestamp.
* Contains notices/messages used by the application to the user.
*
@@ -28,6 +32,8 @@ abstract class Notice extends WireData {
/**
* Flag indicates the notice is a warning
*
* #pw-internal
*
* @deprecated use NoticeWarning instead.
*
*/
@@ -46,7 +52,7 @@ abstract class Notice extends WireData {
const logOnly = 16;
/**
* Flag indicates the notice is allowed to contain markup and won't be automatically entity encoded
* Flag indicates the notice is allowed to contain markup and wont be automatically entity encoded
*
* Note: entity encoding is done by the admin theme at output time, which should detect this flag.
*
@@ -56,8 +62,8 @@ abstract class Notice extends WireData {
/**
* Create the Notice
*
* @param string $text
* @param int $flags
* @param string $text Notification text
* @param int $flags Flags
*
*/
public function __construct($text, $flags = 0) {
@@ -69,6 +75,8 @@ abstract class Notice extends WireData {
}
/**
* Get the notice log
*
* @return string Name of log (basename)
*
*/
@@ -111,21 +119,88 @@ class NoticeWarning extends Notice {
/**
* A class to contain multiple Notice instances, whether messages or errors
* ProcessWire Notices
*
* #pw-summary A class to contain multiple Notice instances, whether messages, warnings or errors
* #pw-body =
* This class manages notices that have been sent by `Wire::message()`, `Wire::warning()` and `Wire::error()` calls.
* The message(), warning() and error() methods are available on every `Wire` derived object. This class is primarily
* for internal use in the admin. However, it may also be useful in some front-end contexts.
* ~~~~~
* // Adding a NoticeMessage using object syntax
* $notices->add(new NoticeMessage("Hello World"));
*
* // Adding a NoticeMessage using regular syntax
* $notices->message("Hello World");
*
* // Adding a NoticeWarning, and allow markup in it
* $notices->message("Hello <strong>World</strong>", Notice::allowMarkup);
*
* // Adding a NoticeError that only appears if debug mode is on
* $notices->error("Hello World", Notice::debug);
* ~~~~~
* Iterating and outputting Notices:
* ~~~~~
* foreach($notices as $notice) {
* // skip over debug notices, if debug mode isn't active
* if($notice->flags & Notice::debug && !$config->debug) continue;
* // entity encode notices unless the allowMarkup flag is set
* if($notice->flags & Notice::allowMarkup) {
* $text = $notice->text;
* } else {
* $text = $sanitizer->entities($notice->text);
* }
* // output either an error, warning or message notice
* if($notice instanceof NoticeError) {
* echo "<p class='error'>$text</p>";
* } else if($notice instanceof NoticeWarning) {
* echo "<p class='warning'>$text</p>";
* } else {
* echo "<p class='message'>$text</p>";
* }
* }
* ~~~~~
*
* #pw-body
*
*
*/
class Notices extends WireArray {
const logAllNotices = false; // for debugging/dev purposes
/**
* #pw-internal
*
* @param mixed $item
* @return bool
*
*/
public function isValidItem($item) {
return $item instanceof Notice;
}
}
/**
* #pw-internal
*
* @return Notice
*
*/
public function makeBlankItem() {
return $this->wire(new NoticeMessage(''));
}
/**
* Add a Notice object
*
* ~~~~
* $notices->add(new NoticeError("An error occurred!"));
* ~~~~
*
* @param Notice $item
* @return $this
*
*/
public function add($item) {
if($item->flags & Notice::debug) {
@@ -171,10 +246,19 @@ class Notices extends WireArray {
protected function addLog($item) {
/** @var Notice $item */
$text = $item->text;
if($item->flags & Notice::allowMarkup && strpos($text, '&') !== false) {
$text = $this->wire('sanitizer')->unentities($text);
}
if($this->wire('config')->debug && $item->class) $text .= " ($item->class)";
$this->wire('log')->save($item->getName(), $text);
}
/**
* Are there NoticeError items present?
*
* @return bool
*
*/
public function hasErrors() {
$numErrors = 0;
foreach($this as $notice) {
@@ -182,7 +266,13 @@ class Notices extends WireArray {
}
return $numErrors > 0;
}
/**
* Are there NoticeWarning items present?
*
* @return bool
*
*/
public function hasWarnings() {
$numWarnings = 0;
foreach($this as $notice) {
@@ -196,6 +286,8 @@ class Notices extends WireArray {
*
* This enables us to safely print_r the string for debugging purposes
*
* #pw-internal
*
* @param array $a
* @return array
*

View File

@@ -1001,6 +1001,12 @@ class Page extends WireData implements \Countable, WireMatchable {
// check if it's a field.subfield property
if(strpos($key, '.') && ($value = $this->getFieldSubfieldValue($key)) !== null) return $value;
if(strpos($key, '_OR_')) {
// convert '_OR_' to '|'
$value = $this->getFieldFirstValue(str_replace('_OR_', '|', $key));
if($value !== null) return $value;
}
// optionally let a hook look at it
if($this->wire('hooks')->isHooked('Page::getUnknown()')) $value = $this->getUnknown($key);
}
@@ -1648,8 +1654,10 @@ class Page extends WireData implements \Countable, WireMatchable {
protected function setTemplate($tpl) {
if(!is_object($tpl)) $tpl = $this->wire('templates')->get($tpl);
if(!$tpl instanceof Template) throw new WireException("Invalid value sent to Page::setTemplate");
if($this->template && $this->template->id != $tpl->id) {
if($this->settings['status'] & Page::statusSystem) throw new WireException("Template changes are disallowed on this page");
if($this->template && $this->template->id != $tpl->id && $this->isLoaded) {
if($this->settings['status'] & Page::statusSystem) {
throw new WireException("Template changes are disallowed on this page");
}
if(is_null($this->templatePrevious)) $this->templatePrevious = $this->template;
$this->trackChange('template', $this->template, $tpl);
}
@@ -1674,10 +1682,14 @@ class Page extends WireData implements \Countable, WireMatchable {
if($parent->id && $this->id == $parent->id || $parent->parents->has($this)) {
throw new WireException("Page cannot be its own parent");
}
$this->trackChange('parent', $this->parent, $parent);
if(($this->parent && $this->parent->id) && $this->parent->id != $parent->id) {
if($this->settings['status'] & Page::statusSystem) throw new WireException("Parent changes are disallowed on this page");
$this->parentPrevious = $this->parent;
if($this->isLoaded) {
$this->trackChange('parent', $this->parent, $parent);
if(($this->parent && $this->parent->id) && $this->parent->id != $parent->id) {
if($this->settings['status'] & Page::statusSystem) {
throw new WireException("Parent changes are disallowed on this page");
}
if(is_null($this->parentPrevious)) $this->parentPrevious = $this->parent;
}
}
$this->parent = $parent;
return $this;
@@ -2259,7 +2271,7 @@ class Page extends WireData implements \Countable, WireMatchable {
* ~~~~~
*
* #pw-group-manipulation
* #pw-links [Blog post about setAndSave](https://processwire.com/blog/posts/processwire-2.6.9-core-updates-and-new-procache-version/#new-page-gt-setandsave-method)
* #pw-links [Blog post about setAndSave](https://processwire.com/blog/posts/processwire-2.6.9-core-updates-and-new-procache-version/)
*
* @param array|string $key Field or property name to set, or array of one or more ['property' => $value].
* @param string|int|bool|object $value Value to set, or omit if you provided an array in first argument.

View File

@@ -14,6 +14,11 @@
*
* PageArray is returned by all API methods in ProcessWire that can return more than one page at once.
* `$pages->find()` and `$page->children()` are common examples.
*
* The recommended way to create a new PageArray is to use the `$pages->newPageArray()` method:
* ~~~~~
* $pageArray = $pages->newPageArray();
* ~~~~~
* #pw-body
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer

View File

@@ -92,6 +92,7 @@ class PageComparison {
$property = $selector->field;
$subproperty = '';
if(is_array($property)) $property = reset($property);
if(strpos($property, '.')) list($property, $subproperty) = explode('.', $property, 2);
if(in_array($property, $ignores)) continue;

View File

@@ -7,6 +7,7 @@
* #pw-summary-traversal For the most part youll want to traverse from the parent `Pagefiles` object than these methods.
* #pw-summary-manipulation Remember to follow up any manipulations with a `$pages->save()` call.
* #pw-summary-tags Be sure to see the `Pagefiles::getTag()` and `Pagesfiles::findTag()` methods, which enable you retrieve files by tag.
* #pw-use-constructor
* #pw-body =
* Pagefile objects are contained by a `Pagefiles` object.
* #pw-body
@@ -19,7 +20,8 @@
* @property-read string $filename full disk path to the file on the server.
* @property-read string $name Returns the filename without the path, same as the "basename" property.
* @property-read string $hash Get a unique hash (for the page) representing this Pagefile.
* @property-read string $tagsArray Get file tags as an array. #pw-group-tags @since 3.0.17
* @property-read string $tagsArray Get file tags as an array. #pw-group-tags @since 3.0.17
* @property-read int $sort Sort order in database. #pw-group-other
* @property string $basename Returns the filename without the path.
* @property string $description Value of the files description field (string), if enabled. Note you can also set this property directly.
* @property string $tags Value of the files tags field (string), if enabled. #pw-group-tags
@@ -48,6 +50,8 @@ class Pagefile extends WireData {
/**
* Reference to the owning collection of Pagefiles
*
* @var Pagefiles
*
*/
protected $pagefiles;

View File

@@ -6,6 +6,7 @@
* #pw-summary Represents an image item attached to a page, typically via an Image Fieldtype.
* #pw-summary-variations A variation refers to an image that is based upon another (like a resized or cropped version for example).
* #pw-order-groups common,resize-and-crop,variations,other
* #pw-use-constructor
* #pw-body =
* Pageimage objects are usually contained by a `Pageimages` object, which is a type of `Pagefiles` and `WireArray`.
* In addition to the methods and properties below, you'll also want to look at `Pagefile` which this class inherits
@@ -93,9 +94,14 @@ class Pageimage extends Pagefile {
protected $error = '';
/**
* Construct a new Pagefile
* Construct a new Pageimage
*
* ~~~~~
* // Construct a new Pageimage, assumes that $page->images is a FieldtypeImage Field
* $pageimage = new Pageimage($page->images, '/path/to/file.png');
* ~~~~~
*
* @param Pagefiles $pagefiles
* @param Pageimages|Pagefiles $pagefiles
* @param string $filename Full path and filename to this pagefile
* @throws WireException
*
@@ -667,22 +673,36 @@ class Pageimage extends Pagefile {
* @param int|float $width Specify int to return resized image for hidpi, or float (or omit) to return current width at hidpi.
* @param array $options Optional options for use when resizing, see size() method for details.
* Or you may specify an int as if you want to return a hidpi width and want to calculate with that width rather than current image width.
* @return int|Pageimage
* @return int|Pageimage|string
*
*/
public function hidpiWidth($width = 0, $options = array()) {
if(is_string($width)) {
if(ctype_digit("$width")) {
$width = (int) $width;
} else if($width === "100%") {
return $this;
} else if(ctype_digit(str_replace(".", "", $width))) {
$width = (float) $width;
}
}
if(is_float($width) || $width < 1) {
// return hidpi width intended: scale omitted or provided in $width argument
$scale = $width;
if(!$scale || $scale < 0 || $scale > 1) $scale = 0.5;
if(is_string($options) && $options === "100%") return $options;
$width = is_array($options) ? 0 : (int) $options;
if($width < 1) $width = $this->width();
if($width === "100%") return $width;
return ceil($width * $scale);
} else if($width) {
} else if($width && is_int($width) && $width > 0) {
// resize intended
if(!is_array($options)) $options = array();
return $this->hidpiSize((int) $width, 0, $options);
}
return 0; // not possible to reach, but pleases the inspection
}
@@ -734,7 +754,7 @@ class Pageimage extends Pagefile {
*/
public function maxWidth($n, array $options = array()) {
$options['upscaling'] = false;
if($this->width() > $n) return $this->width($n);
if($this->width() > $n) return $this->width($n, $options);
return $this;
}
@@ -756,7 +776,7 @@ class Pageimage extends Pagefile {
*/
public function maxHeight($n, array $options = array()) {
$options['upscaling'] = false;
if($this->height() > $n) return $this->height($n);
if($this->height() > $n) return $this->height($n, $options);
return $this;
}
@@ -775,17 +795,9 @@ class Pageimage extends Pagefile {
$w = $this->width();
$h = $this->height();
if($w >= $h) {
if($w > $width && $h > $height) {
return $this->size($width, $height, $options);
} else {
return $this->maxWidth($width, $options);
}
return $this->maxWidth($width, $options);
} else {
if($w > $width && $h > $height) {
return $this->size($width, $height, $options);
} else {
return $this->maxHeight($height, $options);
}
return $this->maxHeight($height, $options);
}
}
@@ -1106,8 +1118,8 @@ class Pageimage extends Pagefile {
return false;
}
$info['hidpiWidth'] = $this->hidpiWidth(null, $info['width']);
$info['hidpiHeight'] = $this->hidpiWidth(null, $info['height']);
$info['hidpiWidth'] = $this->hidpiWidth(0, $info['width']);
$info['hidpiHeight'] = $this->hidpiWidth(0, $info['height']);
if(empty($info['crop'])) {
// attempt to extract crop info from suffix

View File

@@ -61,6 +61,8 @@
* @method unpublished(Page $page) Hook called after a published page has just been unpublished.
* @method saveFieldReady(Page $page, Field $field) Hook called just before a saveField() method saves a page fied.
* @method savedField(Page $page, Field $field) Hook called after saveField() method successfully executes.
* @method savePageOrFieldReady(Page $page, $fieldName = '') Hook inclusive of both saveReady() and saveFieldReady().
* @method savedPageOrField(Page $page, array $changes) Hook inclusive of both saved() and savedField().
* @method found(PageArray $pages, array $details) Hook called at the end of a $pages->find().
*
* TO-DO
@@ -276,8 +278,8 @@ class Pages extends Wire {
*
* #pw-group-retrieval
*
* @param $selector
* @param array $options
* @param string|array|Selectors $selector Selector to find pages
* @param array $options Options to modify behavior. See `Pages::find()` $options argument for details.
* @return PageArray
* @since 3.0.19
* @see Pages::find(), Pages::findOne()
@@ -474,8 +476,11 @@ class Pages extends Wire {
* #pw-group-manipulation
*
* @param Page $page Page to delete
* @param bool $recursive If set to true, then this will attempt to delete all children too.
* @param array $options Optional settings to change behavior (for the future, none currently in use).
* @param bool|array $recursive If set to true, then this will attempt to delete all children too.
* If you don't need this argument, optionally provide $options array instead.
* @param array $options Optional settings to change behavior:
* - uncacheAll (bool): Whether to clear memory cache after delete (default=false)
* - recursive (bool): Same as $recursive argument, may be specified in $options array if preferred.
* @return bool|int Returns true (success), or integer of quantity deleted if recursive mode requested.
* @throws WireException on fatal error
* @see Pages::trash()
@@ -1585,6 +1590,28 @@ class Pages extends Wire {
$this->log("Saved page field '$field->name'", $page);
}
/**
* Hook called when either of Pages::save or Pages::saveField is ready to execute
*
* #pw-hooker
*
* @param Page $page
* @param string $fieldName Populated only if call originates from saveField
*
*/
public function ___savePageOrFieldReady(Page $page, $fieldName = '') { }
/**
* Hook called after either of Pages::save or Pages::saveField successfully executes
*
* #pw-hooker
*
* @param Page $page
* @param array $changes Names of fields
*
*/
public function ___savedPageOrField(Page $page, array $changes = array()) { }
}

View File

@@ -504,7 +504,12 @@ class PagesEditor extends Wire {
$systemVersion = $config->systemVersion;
if(!$page->created_users_id) $page->created_users_id = $userID;
if($page->isChanged('status') && empty($options['noHooks'])) $this->pages->statusChangeReady($page);
$extraData = empty($options['noHooks']) ? $this->pages->saveReady($page) : array();
if(empty($options['noHooks'])) {
$extraData = $this->pages->saveReady($page);
$this->pages->savePageOrFieldReady($page);
} else {
$extraData = array();
}
$sql = '';
if(strpos($page->name, $this->untitledPageName) === 0) $this->pages->setupPageName($page);
@@ -659,7 +664,10 @@ class PagesEditor extends Wire {
// if page hasn't changed, don't continue further
if(!$page->isChanged() && !$isNew) {
$this->pages->debugLog('save', '[not-changed]', true);
if(empty($options['noHooks'])) $this->pages->saved($page, array());
if(empty($options['noHooks'])) {
$this->pages->saved($page, array());
$this->pages->savedPageOrField($page, array());
}
return true;
}
@@ -744,6 +752,7 @@ class PagesEditor extends Wire {
// trigger hooks
if(empty($options['noHooks'])) {
$this->pages->saved($page, $changes, $changesValues);
$this->pages->savedPageOrField($page, $changes);
if($triggerAddedPage) $this->pages->added($triggerAddedPage);
if($page->namePrevious && $page->namePrevious != $page->name) $this->pages->renamed($page);
if($page->parentPrevious) $this->pages->moved($page);
@@ -799,7 +808,10 @@ class PagesEditor extends Wire {
if($value instanceof Pagefiles || $value instanceof Pagefile) $page->filesManager()->save();
$page->trackChange($field->name);
if(empty($options['noHooks'])) $this->pages->saveFieldReady($page, $field);
if(empty($options['noHooks'])) {
$this->pages->saveFieldReady($page, $field);
$this->pages->savePageOrFieldReady($page, $field->name);
}
if($field->type->savePageField($page, $field)) {
$page->untrackChange($field->name);
@@ -813,7 +825,10 @@ class PagesEditor extends Wire {
$database->execute($query);
}
$return = true;
if(empty($options['noHooks'])) $this->pages->savedField($page, $field);
if(empty($options['noHooks'])) {
$this->pages->savedField($page, $field);
$this->pages->savedPageOrField($page, array($field->name));
}
} else {
$return = false;
}
@@ -864,7 +879,10 @@ class PagesEditor extends Wire {
} while(1);
if($insertSql) {
$sql = "INSERT INTO pages_parents (pages_id, parents_id) VALUES" . rtrim($insertSql, ",");
$sql =
'INSERT INTO pages_parents (pages_id, parents_id) ' .
'VALUES' . rtrim($insertSql, ',') . ' ' .
'ON DUPLICATE KEY UPDATE parents_id=VALUES(parents_id)';
$database->exec($sql);
}
@@ -951,24 +969,34 @@ class PagesEditor extends Wire {
* this method will throw an exception. If a recursive delete fails for any reason, an exception will be thrown.
*
* @param Page $page
* @param bool $recursive If set to true, then this will attempt to delete all children too.
* @param array $options Optional settings to change behavior (for the future)
* @param bool|array $recursive If set to true, then this will attempt to delete all children too.
* If you don't need this argument, optionally provide $options array instead.
* @param array $options Optional settings to change behavior:
* - uncacheAll (bool): Whether to clear memory cache after delete (default=false)
* - recursive (bool): Same as $recursive argument, may be specified in $options array if preferred.
* @return bool|int Returns true (success), or integer of quantity deleted if recursive mode requested.
* @throws WireException on fatal error
*
*/
public function delete(Page $page, $recursive = false, array $options = array()) {
$defaults = array(
'uncacheAll' => false,
'recursive' => is_bool($recursive) ? $recursive : false,
);
if(is_array($recursive)) $options = $recursive;
$options = array_merge($defaults, $options);
if($options) {} // to ignore unused parameter inspection
if(!$this->isDeleteable($page)) throw new WireException("This page may not be deleted");
$numDeleted = 0;
if($page->numChildren) {
if(!$recursive) {
if(!$options['recursive']) {
throw new WireException("Can't delete Page $page because it has one or more children.");
} else foreach($page->children("include=all") as $child) {
/** @var Page $child */
if($this->pages->delete($child, true)) {
if($this->pages->delete($child, true, $options)) {
$numDeleted++;
} else {
throw new WireException("Error doing recursive page delete, stopped by page $child");
@@ -990,6 +1018,7 @@ class PagesEditor extends Wire {
} catch(\Exception $e) {
}
/** @var PagesAccess $access */
$access = $this->wire(new PagesAccess());
$access->deletePage($page);
@@ -1008,10 +1037,10 @@ class PagesEditor extends Wire {
$page->status = Page::statusDeleted; // no need for bitwise addition here, as this page is no longer relevant
$this->pages->deleted($page);
$numDeleted++;
$this->pages->uncacheAll($page);
if($options['uncacheAll']) $this->pages->uncacheAll($page);
$this->pages->debugLog('delete', $page, true);
return $recursive ? $numDeleted : true;
return $options['recursive'] ? $numDeleted : true;
}
/**

View File

@@ -204,8 +204,10 @@ class PagesLoader extends Wire {
$pageFinder = $this->pages->getPageFinder();
$pagesInfo = array();
$pagesIDs = array();
if($debug) Debug::timer("$caller($selectorString)", true);
$profiler = $this->wire('profiler');
$profilerEvent = $profiler ? $profiler->start("$caller($selectorString)", "Pages") : null;
if($lazy) {
if(strpos($selectorString, 'limit=') === false) $options['getTotal'] = false;
@@ -307,6 +309,8 @@ class PagesLoader extends Wire {
$item->setQuietly('_debug_loader', "$caller($selectorString)");
}
}
if($profilerEvent) $profiler->stop($profilerEvent);
if($this->pages->hasHook('found()')) $this->pages->found($pages, array(
'pageFinder' => $pageFinder,

View File

@@ -3,8 +3,15 @@
/**
* ProcessWire PagesType
*
* Provides an interface to the Pages class but specific to
* a given page class/type, with predefined parent and template.
* #pw-summary Provides an interface to the Pages class but specific to a given page class/type, with predefined parent and template.
* #pw-body =
* This class is primarily used by the core as an alternative to `$pages`, providing an API for other Page types like
* `User`, `Role`, `Permission`, and `Language`. The `$users`, `$roles`, `$permissions` and `$languages` API variables
* are all instances of `PagesType`. This class is typically not instantiated on its own and instead acts as a base class
* which is extended.
*
* #pw-body
* #pw-use-constructor
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* https://processwire.com
@@ -83,6 +90,8 @@ class PagesType extends Wire implements \IteratorAggregate, \Countable {
/**
* Add one or more templates that this PagesType represents
*
* #pw-group-family
*
* @param array|int|string $templates Single or array of Template objects, IDs, or names
*
*/
@@ -110,6 +119,8 @@ class PagesType extends Wire implements \IteratorAggregate, \Countable {
/**
* Add one or more of parents that this PagesType represents
*
* #pw-group-family
*
* @param array|int|string|Page $parents Single or array of Page objects, IDs, or paths
*
*/
@@ -170,6 +181,8 @@ class PagesType extends Wire implements \IteratorAggregate, \Countable {
/**
* Is the given page a valid type for this class?
*
* #pw-internal
*
* @param Page $page
* @return bool
@@ -233,9 +246,10 @@ class PagesType extends Wire implements \IteratorAggregate, \Countable {
* Given a Selector string, return the Page objects that match in a PageArray.
*
* @param string $selectorString
* @param array $options
- findOne: apply optimizations for finding a single page and include pages with 'hidden' status
* @param array $options Options to modify default behavior:
* - `findOne` (bool): apply optimizations for finding a single page and include pages with 'hidden' status
* @return PageArray
* @see Pages::find()
*
*/
public function find($selectorString, $options = array()) {
@@ -301,13 +315,11 @@ class PagesType extends Wire implements \IteratorAggregate, \Countable {
}
/**
* Save a page object and it's fields to database.
* Save a page object and its fields to database.
*
* If the page is new, it will be inserted. If existing, it will be updated.
*
* This is the same as calling $page->save()
*
* If you want to just save a particular field in a Page, use $page->save($fieldName) instead.
* - This is the same as calling $page->save()
* - If the page is new, it will be inserted. If existing, it will be updated.
* - If you want to just save a particular field in a Page, use `$page->save($fieldName)` instead.
*
* @param Page $page
* @return bool True on success
@@ -320,11 +332,11 @@ class PagesType extends Wire implements \IteratorAggregate, \Countable {
}
/**
* Permanently delete a page and it's fields.
* Permanently delete a page and its fields.
*
* Unlike trash(), pages deleted here are not restorable.
* Unlike `$pages->trash()`, pages deleted here are not restorable.
*
* If you attempt to delete a page with children, and don't specifically set the $recursive param to True, then
* If you attempt to delete a page with children, and dont specifically set the `$recursive` argument to `true`, then
* this method will throw an exception. If a recursive delete fails for any reason, an exception will be thrown.
*
* @param Page $page
@@ -339,12 +351,12 @@ class PagesType extends Wire implements \IteratorAggregate, \Countable {
}
/**
* Adds a new page with the given $name and returns the Page
* Adds a new page with the given $name and returns it
*
* If they page has any other fields, they will not be populated, only the name will.
* Returns a NullPage if error, such as a page of this type already existing with the same name.
* - If the page has any other fields, they will not be populated, only the name will.
* - Returns a `NullPage` on error, such as when a page of this type already exists with the same name/parent.
*
* @param string $name
* @param string $name Name to use for the new page
* @return Page|NullPage
*
*/
@@ -375,32 +387,82 @@ class PagesType extends Wire implements \IteratorAggregate, \Countable {
* Make it possible to iterate all pages of this type per the \IteratorAggregate interface.
*
* Only recommended for page types that don't contain a lot of pages.
*
* #pw-internal
*
*/
public function getIterator() {
return $this->find("id>0, sort=name");
}
}
/**
* Get the template used by this type (or first template if there are multiple)
*
* #pw-group-family
*
* @return Template
*
*/
public function getTemplate() {
return $this->template;
}
/**
* Get the templates (plural) used by this type
*
* #pw-group-family
*
* @return array|Template[] Array of Template objects indexed by template ID.
*
*/
public function getTemplates() {
return count($this->templates) ? $this->templates : array($this->template);
}
/**
* Get the parent page ID used by this type (or first parent ID if there are multiple)
*
* #pw-group-family
*
* @return int
*
*/
public function getParentID() {
return $this->parent_id;
}
/**
* Get the parent page IDs used by this type
*
* #pw-group-family
*
* @return array Array of parent page IDs (integers)
*
*/
public function getParentIDs() {
return count($this->parents) ? $this->parents : array($this->parent_id);
}
/**
* Get the parent Page object (or first parent Page object if there are multiple)
*
* #pw-group-family
*
* @return Page|NullPage
*
*/
public function getParent() {
return $this->wire('pages')->get($this->parent_id);
}
/**
* Get the parent Page objects in a PageArray
*
* #pw-group-family
*
* @return PageArray
*
*/
public function getParents() {
if(count($this->parents)) {
return $this->wire('pages')->getById($this->parents);
@@ -411,17 +473,42 @@ class PagesType extends Wire implements \IteratorAggregate, \Countable {
return $parents;
}
}
/**
* Set the PHP class name to use for Page objects of this type
*
* #pw-group-family
*
* @param string $class
*
*/
public function setPageClass($class) {
$this->pageClass = $class;
}
/**
* Get the PHP class name used by Page objects of this type
*
* #pw-group-family
*
* @return string
*
*/
public function getPageClass() {
if($this->pageClass) return $this->pageClass;
if($this->template && $this->template->pageClass) return $this->template->pageClass;
return 'Page';
}
/**
* Return the number of pages in this type matching the given selector string
*
* @param string $selectorString Optional, if omitted then returns count of all pages of this type
* @param array $options Options to modify default behavior (see $pages->count method for details)
* @return int
* @see Pages::count()
*
*/
public function count($selectorString = '', array $options = array()) {
if(empty($selectorString) && empty($options) && count($this->parents) == 1) {
return $this->getParent()->numChildren();
@@ -449,6 +536,8 @@ class PagesType extends Wire implements \IteratorAggregate, \Countable {
/**
* Hook called just before a page is saved
*
* #pw-hooker
*
* @param Page $page The page about to be saved
* @return array Optional extra data to add to pages save query.
@@ -464,6 +553,8 @@ class PagesType extends Wire implements \IteratorAggregate, \Countable {
*
* This is the same as Pages::save, except that it occurs before other save-related hooks (below),
* Whereas Pages::save occurs after. In most cases, the distinction does not matter.
*
* #pw-hooker
*
* @param Page $page The page that was saved
* @param array $changes Array of field names that changed
@@ -474,6 +565,8 @@ class PagesType extends Wire implements \IteratorAggregate, \Countable {
/**
* Hook called when a new page has been added
*
* #pw-hooker
*
* @param Page $page
*
@@ -482,6 +575,8 @@ class PagesType extends Wire implements \IteratorAggregate, \Countable {
/**
* Hook called when a page is about to be deleted, but before data has been touched
*
* #pw-hooker
*
* @param Page $page
*
@@ -490,6 +585,8 @@ class PagesType extends Wire implements \IteratorAggregate, \Countable {
/**
* Hook called when a page and it's data have been deleted
*
* #pw-hooker
*
* @param Page $page
*

View File

@@ -151,7 +151,9 @@ class ProcessController extends Wire {
if(!empty($info['permission'])) $permissionName = $info['permission'];
$this->hasPermission($permissionName, true); // throws exception if no permission
if(!$this->process) $this->process = $this->modules->get($processName);
if(!$this->process) {
$this->process = $this->modules->getModule($processName);
}
// set a proces fuel, primarily so that certain Processes can determine if they are the root Process
// example: PageList when in PageEdit

View File

@@ -1,45 +1,105 @@
<?php namespace ProcessWire;
/**
* ProcessWire API Bootstrap
*
* Initializes all the ProcessWire classes and prepares them for API use
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* https://processwire.com
*
* @todo: get language permissions to work with extra actions
*
*/
require_once(__DIR__ . '/boot.php');
/**
* ProcessWire API bootstrap class
* ProcessWire API Bootstrap
*
* Gets ProcessWire's API ready for use
* #pw-summary Represents an instance of ProcessWire connected with a set of API variables.
* #pw-summary-instances Methods for managing ProcessWire instances. Note that most of these methods are static.
* #pw-use-constants
* #pw-use-constructor
* #pw-body =
* This class boots a ProcessWire instance. The current ProcessWire instance is represented by the `$wire` API variable.
* ~~~~~
* // To create a new ProcessWire instance
* $wire = new ProcessWire('/server/path/', 'https://hostname/url/');
* ~~~~~
* #pw-body
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* https://processwire.com
*
* @method init()
* @method ready()
* @method finished()
*
*/
*
*
*/
class ProcessWire extends Wire {
const versionMajor = 3;
const versionMinor = 0;
const versionRevision = 36;
/**
* Major version number
*
*/
const versionMajor = 3;
/**
* Minor version number
*
*/
const versionMinor = 0;
/**
* Reversion revision number
*
*/
const versionRevision = 42;
/**
* Version suffix string (when applicable)
*
*/
const versionSuffix = '';
const indexVersion = 300; // required version for index.php file (represented by PROCESSWIRE define)
/**
* Minimum required index.php version, represented by the PROCESSWIRE define
*
*/
const indexVersion = 300;
/**
* Minimum required .htaccess file version
*
*/
const htaccessVersion = 300;
const statusBoot = 0; // system is booting
const statusInit = 2; // system and modules are initializing
const statusReady = 4; // system and $page are ready
const statusRender = 8; // $page's template is being rendered
const statusFinished = 16; // request has been delivered
const statusFailed = 1024; // request failed due to exception or 404
/**
* Status when system is booting
*
*/
const statusBoot = 0;
/**
* Status when system and modules are initializing
*
*/
const statusInit = 2;
/**
* Systus when system, $page and API variables are ready
*
*/
const statusReady = 4;
/**
* Status when the current $pages template file is being rendered
*
*/
const statusRender = 8;
/**
* Status when the request has been fully delivered
*
*/
const statusFinished = 16;
/**
* Status when the request failed due to an Exception or 404
*
*/
const statusFailed = 1024;
/**
* Whether debug mode is on or off
@@ -126,7 +186,7 @@ class ProcessWire extends Wire {
if(is_string($config)) $config = self::buildConfig($config, $rootURL);
if(!$config instanceof Config) throw new WireException("No configuration information available");
// this is reset in the $this->config() method based on current debug mode
// this is reset in the $this->setConfig() method based on current debug mode
ini_set('display_errors', true);
error_reporting(E_ALL | E_STRICT);
@@ -145,12 +205,23 @@ class ProcessWire extends Wire {
$this->wire('hooks', new WireHooks($this, $config), true);
$this->shutdown = $this->wire(new WireShutdown());
$this->config($config);
$this->setConfig($config);
$this->load($config);
if($this->getNumInstances() > 1) {
// this instance is not handling the request and needs a mock $page API var and pageview
/** @var ProcessPageView $view */
$view = $this->wire('modules')->get('ProcessPageView');
$view->execute(false);
}
}
public function __toString() {
return $this->className() . " " . self::versionMajor . "." . self::versionMinor . "." . self::versionRevision;
$str = $this->className() . " ";
$str .= self::versionMajor . "." . self::versionMinor . "." . self::versionRevision;
if(self::versionSuffix) $str .= " " . self::versionSuffix;
if($this->getNumInstances() > 1) $str .= " #$this->instanceID";
return $str;
}
/**
@@ -159,7 +230,7 @@ class ProcessWire extends Wire {
* @param Config $config
*
*/
protected function config(Config $config) {
protected function setConfig(Config $config) {
$this->wire('config', $config, true);
$this->wire($config->paths);
@@ -207,6 +278,10 @@ class ProcessWire extends Wire {
$process = $this->wire('process');
if($process == 'ProcessPageView') $process->finished();
});
if($config->useFunctionsAPI) {
include($config->paths->core . 'FunctionsAPI.php');
}
$this->setStatus(self::statusBoot);
}
@@ -262,7 +337,9 @@ class ProcessWire extends Wire {
}
/**
* Load's ProcessWire using the supplied Config and populates all API fuel
* Loads ProcessWire using the supplied Config and populates all API fuel
*
* #pw-internal
*
* @param Config $config
* @throws WireDatabaseException|WireException on fatal error
@@ -402,6 +479,8 @@ class ProcessWire extends Wire {
/**
* Hookable init for anyone that wants to hook immediately before any autoload modules initialized or after all modules initialized
*
* #pw-hooker
*
*/
protected function ___init() {
if($this->debug) Debug::timer('boot.modules.autoload.init');
@@ -411,6 +490,8 @@ class ProcessWire extends Wire {
/**
* Hookable ready for anyone that wants to hook immediately before any autoload modules ready or after all modules ready
*
* #pw-hooker
*
*/
protected function ___ready() {
@@ -423,6 +504,8 @@ class ProcessWire extends Wire {
/**
* Hookable ready for anyone that wants to hook when the request is finished
*
* #pw-hooker
*
*/
protected function ___finished() {
@@ -430,9 +513,11 @@ class ProcessWire extends Wire {
$config = $this->wire('config');
$session = $this->wire('session');
$cache = $this->wire('cache');
$profiler = $this->wire('profiler');
if($session) $session->maintenance();
if($cache) $cache->maintenance();
if($profiler) $profiler->maintenance();
if($config->templateCompile) {
$compiler = new FileCompiler($this->wire('config')->paths->templates);
@@ -443,6 +528,7 @@ class ProcessWire extends Wire {
$compiler = new FileCompiler($this->wire('config')->paths->siteModules);
$compiler->maintenance();
}
}
/**
@@ -495,7 +581,16 @@ class ProcessWire extends Wire {
if(is_object($value)) return call_user_func_array(array($value, '__invoke'), $arguments);
return parent::__call($method, $arguments);
}
/**
* Get an API variable
*
* #pw-internal
*
* @param string $name Optional API variable name
* @return mixed|null|Fuel
*
*/
public function fuel($name = '') {
if(empty($name)) return $this->fuel;
return $this->fuel->$name;
@@ -522,6 +617,8 @@ class ProcessWire extends Wire {
/**
* Instance ID of this ProcessWire instance
*
* #pw-group-instances
*
* @return int
*
*/
@@ -532,6 +629,8 @@ class ProcessWire extends Wire {
/**
* Add a ProcessWire instance and return the instance ID
*
* #pw-group-instances
*
* @param ProcessWire $wire
* @return int
*
@@ -546,6 +645,8 @@ class ProcessWire extends Wire {
/**
* Get all ProcessWire instances
*
* #pw-group-instances
*
* @return array
*
*/
@@ -553,9 +654,23 @@ class ProcessWire extends Wire {
return self::$instances;
}
/**
* Return number of instances
*
* #pw-group-instances
*
* @return int
*
*/
public static function getNumInstances() {
return count(self::$instances);
}
/**
* Get a ProcessWire instance by ID
*
* #pw-group-instances
*
* @param int|null $instanceID Omit this argument to return the current instance
* @return null|ProcessWire
*
@@ -568,6 +683,8 @@ class ProcessWire extends Wire {
/**
* Get the current ProcessWire instance
*
* #pw-group-instances
*
* @return ProcessWire|null
*
*/
@@ -582,6 +699,8 @@ class ProcessWire extends Wire {
/**
* Set the current ProcessWire instance
*
* #pw-group-instances
*
* @param ProcessWire $wire
*
*/
@@ -592,6 +711,8 @@ class ProcessWire extends Wire {
/**
* Remove a ProcessWire instance
*
* #pw-group-instances
*
* @param ProcessWire $wire
*
*/
@@ -606,7 +727,7 @@ class ProcessWire extends Wire {
}
/**
* Build a Config object for booting ProcessWire
* Static method to build a Config object for booting ProcessWire
*
* @param string $rootPath Path to root of installation where ProcessWire's index.php file is located.
* @param string $rootURL Should be specified only for secondary ProcessWire instances.

View File

@@ -914,15 +914,15 @@ class Sanitizer extends Wire {
* #pw-group-strings
*
* @param string $value String value to sanitize
* @param array $options Options to modify default behavior:
* - `maxLength` (int): maximum characters allowed, or 0=no max (default=255).
* - `maxBytes` (int): maximum bytes allowed (default=0, which implies maxLength*4).
* - `stripTags` (bool): strip markup tags? (default=true).
* - `allowableTags` (string): markup tags that are allowed, if stripTags is true (use same format as for PHP's `strip_tags()` function.
* - `multiLine` (bool): allow multiple lines? if false, then $newlineReplacement below is applicable (default=false).
* - `newlineReplacement` (string): character to replace newlines with, OR specify boolean TRUE to remove extra lines (default=" ").
* - `inCharset` (string): input character set (default="UTF-8").
* - `outCharset` (string): output character set (default="UTF-8").
* @param array $options Options to modify default behavior:
* - `maxLength` (int): maximum characters allowed, or 0=no max (default=255).
* - `maxBytes` (int): maximum bytes allowed (default=0, which implies maxLength*4).
* - `stripTags` (bool): strip markup tags? (default=true).
* - `allowableTags` (string): markup tags that are allowed, if stripTags is true (use same format as for PHP's `strip_tags()` function.
* - `multiLine` (bool): allow multiple lines? if false, then $newlineReplacement below is applicable (default=false).
* - `newlineReplacement` (string): character to replace newlines with, OR specify boolean TRUE to remove extra lines (default=" ").
* - `inCharset` (string): input character set (default="UTF-8").
* - `outCharset` (string): output character set (default="UTF-8").
* @return string
* @see Sanitizer::textarea()
*
@@ -1007,7 +1007,7 @@ class Sanitizer extends Wire {
*
* @param string $value String value to sanitize
* @param array $options Options to modify default behavior
* - `maxLength` (int): maximum characters allowed, or 0=no max (default=16384 or 16kb).
* - `maxLength` (int): maximum characters allowed, or 0=no max (default=16384 or 16kb).
* - `maxBytes` (int): maximum bytes allowed (default=0, which implies maxLength*3 or 48kb).
* - `stripTags` (bool): strip markup tags? (default=true).
* - `allowableTags` (string): markup tags that are allowed, if stripTags is true (use same format as for PHP's `strip_tags()` function.
@@ -1070,9 +1070,10 @@ class Sanitizer extends Wire {
if(strpos($value, '<') !== false) {
// tag replacements before strip_tags()
$regex =
'!(?:<' .
'/?(?:ul|ol|p|h\d|div)(?:>|\s[^><]*)' .
'|br[\s/]*' .
'!<(?:' .
'/?(?:ul|ol|p|h\d|div)(?:>|\s[^><]*)' .
'|' .
'(?:br[\s/]*)' .
')>!is';
$value = preg_replace($regex, $newline, $value);
if(stripos($value, '</li>')) {
@@ -1510,7 +1511,7 @@ class Sanitizer extends Wire {
// value needs more filtering, replace all non-alphanumeric, non-single-quote and space chars
// See: http://php.net/manual/en/regexp.reference.unicode.php
// See: http://www.regular-expressions.info/unicode.html
$value = preg_replace('/[^[:alnum:]\pL\pN\pP\pM\p{Sm}\p{Sc}\p{Sk} \'\/]/u', ' ', $value);
$value = preg_replace('/[^[:alnum:]\pL\pN\pP\pM\p{S} \'\/]/u', ' ', $value);
// replace multiple space characters in sequence with just 1
$value = preg_replace('/\s\s+/u', ' ', $value);
@@ -1588,14 +1589,13 @@ class Sanitizer extends Wire {
* for text coming from user input since it doesn't allow any other HTML. But if you just
* want full markdown, then specify TRUE for the `$options` argument.
*
* ~~~~~
* Basic allowed markdown currently includes:
* **strong**
* *emphasis*
* [anchor-text](url)
* ~~strikethrough~~
* `code`
* ~~~~~
* - `**strong**`
* - `*emphasis*`
* - `[anchor-text](url)`
* - `~~strikethrough~~`
* - code surrounded by backticks
*
* ~~~~~
* // basic markdown
* echo $sanitizer->entitiesMarkdown($str);
@@ -1823,9 +1823,11 @@ class Sanitizer extends Wire {
$value = "";
} else if(is_bool($value)) {
$value = $value ? "1" : "";
} else if(is_array($value)) {
$value = "array-" . count($value);
} else if(!is_string($value)) {
$value = (string) $value;
}
if(is_array($value)) $value = "array-" . count($value);
if(!is_string($value)) $value = (string) $value;
if(!is_null($sanitizer) && is_string($sanitizer) && (method_exists($this, $sanitizer) || method_exists($this, "___$sanitizer"))) {
$value = $this->$sanitizer($value);
if(!is_string($value)) $value = (string) $value;
@@ -2314,42 +2316,50 @@ class Sanitizer extends Wire {
*/
public function testAll($value) {
$sanitizers = array(
'alpha',
'alphanumeric',
'array',
'bool',
'date',
'digits',
'email',
'emailHeader',
'entities',
'entities1',
'entitiesMarkdown',
'fieldName',
'filename',
'float',
'int',
'intArray',
'intSigned',
'intUnsigned',
'markupToLine',
'markupToText',
'minArray',
'name',
'names',
'varName',
'fieldName',
'templateName',
'pageName',
'pageNameTranslate',
'pageNameUTF8',
'filename',
'path',
'pagePathName',
'email',
'emailHeader',
'text',
'textarea',
'url',
'pagePathNameUTF8',
'path',
'purify',
'removeNewlines',
'selectorField',
'selectorValue',
'entities',
'entities1',
'unentities',
'entitiesMarkdown',
'purify',
'string',
'date',
'int',
'intUnsigned',
'intSigned',
'float',
'array',
'intArray',
'bool',
'templateName',
'text',
'textarea',
'unentities',
'url',
'varName',
);
$results = array();
foreach($sanitizers as $method) {
$results[$method] = $this->$method($value);
$results[$method] = $this->$method($value);
}
return $results;
}

View File

@@ -11,33 +11,62 @@
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* https://processwire.com
*
*/
/**
* Selector maintains a single selector consisting of field name, operator, and value.
*
* Field and value may optionally be arrays, where are assumed to be OR values.
* #pw-summary Selector maintains a single selector consisting of field name, operator, and value.
*
* Serves as the base class for the different Selector types (seen below this class).
* #pw-body =
* - Serves as the base class for the different Selector types (`SelectorEqual`, `SelectorNotEqual`, `SelectorLessThan`, etc.)
* - The constructor requires `$field` and `$value` properties which may either be an array or string.
* An array indicates multiple items in an OR condition. Multiple items may also be specified by
* pipe “|” separated strings.
* - Operator is determined by the Selector class name, and thus may not be changed without replacing
* the entire Selector.
*
* @property string|array $field Field or fields present in the selector (can be string or array) [1]
* @property array $fields Fields that were present in selector (same as $field, but always array)
* @property string $operator Operator used by the selector [2]
* @property string|array $value Value or values present in the selector (can be string or array) [1]
* @property array $values Values that were present in selector (same as $value, but always array)
* @property bool $not Is this a NOT selector? (i.e. returns the opposite if what it would otherwise)
* @property string|null $group Group name for this selector (if field was prepended with a "group_name@")
* @property string $quote Type of quotes value was in, or blank if it was not quoted. One of: '"[{(
* @property string $str String value of selector
* @property null|bool $forceMatch When boolean, it forces match (true) or non-match (false).
* ~~~~~
* // very basic usage example
* // constructor takes ($field, $value) which can be strings or arrays
* $s = new SelectorEqual('title', 'About Us');
* // $page can be any kind of Wire-derived object
* if($s->matches($page)) {
* // $page has title "About Us"
* }
* ~~~~~
* ~~~~~
* // another usage example
* $s = new SelectorContains('title|body|summary', 'foo|bar');
* if($s->matches($page)) {
* // the title, body or summary properties of $page contain either the text "foo" or "bar"
* }
* ~~~~~
*
* [1] The $field and $value properties may either be an array or string. As a result, we recommend
* accessing the $fields or $values properties (instead of $field or $value), because they are always
* return an array.
* ### List of core selector-derived classes
*
* [2] Operator is determined by the Selector class name, and thus may not be changed without replacing
* the entire Selector.
* - `SelectorEqual`
* - `SelectorNotEqual`
* - `SelectorGreaterThan`
* - `SelectorLessThan`
* - `SelectorGreaterThanEqual`
* - `SelectorLessThanEqual`
* - `SelectorContains`
* - `SelectorContainsLike`
* - `SelectorContainsWords`
* - `SelectorStarts`
* - `SelectorStartsLike`
* - `SelectorEnds`
* - `SelectorEndsLike`
* - `SelectorBitwiseAnd`
*
* #pw-body
*
* @property array $fields Fields that were present in selector (same as $field, but always an array).
* @property string|array $field Field or fields present in the selector (string if single, or array of strings if multiple). Preferable to use $fields property instead.
* @property-read string $operator Operator used by the selector.
* @property array $values Values that were present in selector (same as $value, but always array).
* @property string|array $value Value or values present in the selector (string if single, or array of strings if multiple). Preferable to use $values property instead.
* @property bool $not Is this a NOT selector? Indicates the selector returns the opposite if what it would otherwise. #pw-group-properties
* @property string|null $group Group name for this selector (if field was prepended with a "group_name@"). #pw-group-properties
* @property string $quote Type of quotes value was in, or blank if it was not quoted. One of: '"[{( #pw-group-properties
* @property-read string $str String value of selector, i.e. “a=b”. #pw-group-properties
* @property null|bool $forceMatch When boolean, it forces match (true) or non-match (false). (default=null) #pw-group-properties
*
*/
abstract class Selector extends WireData {
@@ -71,27 +100,112 @@ abstract class Selector extends WireData {
$this->set('forceMatch', null); // boolean true to force match, false to force non-match
}
/**
* Return the operator used by this Selector
*
* @return string
* @since 3.0.42 Prior versions just supported the 'operator' property.
*
*/
public function operator() {
return self::getOperator();
}
/**
* Get the field(s) of this Selector
*
* Note that if calling this as a property (rather than a method) it can return either a string or an array.
*
* @param bool|int $forceString Specify one of the following:
* - `true` (bool): to only return a string, where multiple-fields will be split by pipe "|". (default)
* - `false` (bool): to return string if 1 field, or array of multiple fields (same behavior as field property).
* - `1` (int): to return only the first value (string).
* @return string|array|null
* @since 3.0.42 Prior versions only supported the 'field' property.
* @see Selector::fields()
*
*/
public function field($forceString = true) {
$field = parent::get('field');
if($forceString && is_array($field)) {
if($forceString === 1) {
$field = reset($field);
} else {
$field = implode('|', $field);
}
}
return $field;
}
/**
* Return array of field(s) for this Selector
*
* @return array
* @see Selector::field()
* @since 3.0.42 Prior versions just supported the 'fields' property.
*
*/
public function fields() {
$field = parent::get('field');
if(is_array($field)) return $field;
if(!strlen($field)) return array();
return array($field);
}
/**
* Get the value(s) of this Selector
*
* Note that if calling this as a property (rather than a method) it can return either a string or an array.
*
* @param bool|int $forceString Specify one of the following:
* - `true` (bool): to only return a string, where multiple-values will be split by pipe "|". (default)
* - `false` (bool): to return string if 1 value, or array of multiple values (same behavior as value property).
* - `1` (int): to return only the first value (string).
* @return string|array|null
* @since 3.0.42 Prior versions only supported the 'value' property.
* @see Selector::values()
*
*/
public function value($forceString = true) {
$value = parent::get('value');
if($forceString && is_array($value)) {
if($forceString === 1) {
$value = reset($value);
} else {
$value = implode('|', $value);
}
}
return $value;
}
/**
* Return array of value(s) for this Selector
*
* @return array
* @see Selector::value()
* @since 3.0.42 Prior versions just supported the 'values' property.
*
*/
public function values() {
$values = parent::get('value');
if(is_array($values)) return $values;
if(!is_object($values) && !strlen($values)) return array();
return array($values);
}
public function get($key) {
if($key == 'operator') return $this->getOperator();
if($key == 'str') return $this->__toString();
if($key == 'values') {
$value = $this->value;
if(is_array($value)) return $value;
if(!is_object($value) && !strlen($value)) return array();
return array($value);
}
if($key == 'fields') {
$field = $this->field;
if(is_array($field)) return $field;
if(!strlen($field)) return array();
return array($field);
}
if($key == 'values') return $this->values();
if($key == 'fields') return $this->fields();
return parent::get($key);
}
/**
* Returns the selector field(s), optionally forcing as string or array
*
* #pw-internal
*
* @param string $type Omit for automatic, or specify 'string' or 'array' to force return in that type
* @return string|array
* @throws WireException if given invalid type
@@ -115,6 +229,8 @@ abstract class Selector extends WireData {
* When the $type argument is not specified, this method may return a string, array or Selectors object.
* A Selectors object is only returned if the value happens to contain an embedded selector.
*
* #pw-internal
*
* @param string $type Omit for automatic, or specify 'string' or 'array' to force return in that type
* @return string|array|Selectors
* @throws WireException if given invalid type
@@ -160,6 +276,8 @@ abstract class Selector extends WireData {
* Return the operator used by this Selector
*
* Strict standards don't let us make static abstract methods, so this one throws an exception if it's not reimplemented.
*
* #pw-internal
*
* @return string
* @throws WireException
@@ -291,6 +409,8 @@ abstract class Selector extends WireData {
/**
* Add all individual selector types to the runtime Selectors
*
* #pw-internal
*
*/
static public function loadSelectorTypes() {

View File

@@ -1,17 +1,40 @@
<?php namespace ProcessWire;
require_once(PROCESSWIRE_CORE_PATH . "Selector.php");
/**
* ProcessWire Selectors
*
* Processes a Selector string and can then be iterated to retrieve each resulting Selector object.
* #pw-summary Processes a selector string into a WireArray of Selector objects.
* #pw-summary-static-helpers Static helper methods useful in analyzing selector strings outside of this class.
* #pw-body =
* This Selectors class is used internally by ProcessWire to provide selector string (and array) matching throughout the core.
*
* ~~~~~
* $selectors = new Selectors("sale_price|retail_price>100, currency=USD|EUR");
* if($selectors->matches($page)) {
* // selector string matches the given $page (which can be any Wire-derived item)
* }
* ~~~~~
* ~~~~~
* // iterate and display what's in this Selectors object
* foreach($selectors as $selector) {
* echo "<p>";
* echo "Field(s): " . implode('|', $selector->fields) . "<br>";
* echo "Operator: " . $selector->operator . "<br>";
* echo "Value(s): " . implode('|', $selector->values) . "<br>";
* echo "</p>";
* }
* ~~~~~
* #pw-body
*
* @link https://processwire.com/api/selectors/ Official Selectors Documentation
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* https://processwire.com
*
*/
require_once(PROCESSWIRE_CORE_PATH . "Selector.php");
class Selectors extends WireArray {
/**
@@ -104,6 +127,11 @@ class Selectors extends WireArray {
/**
* Set the selector string or array (if not set already from the constructor)
*
* ~~~~~
* $selectors = new Selectors();
* $selectors->init("sale_price|retail_price>100, currency=USD|EUR");
* ~~~~~
*
* @param string|array $selector
*
*/
@@ -120,6 +148,8 @@ class Selectors extends WireArray {
/**
* Set the selector string
*
* #pw-internal
*
* @param string $selectorStr
*
*/
@@ -131,6 +161,8 @@ class Selectors extends WireArray {
/**
* Import items into this WireArray.
*
* #pw-internal
*
* @throws WireException
* @param string|WireArray $items Items to import.
* @return WireArray This instance.
@@ -148,6 +180,8 @@ class Selectors extends WireArray {
/**
* Per WireArray interface, return true if the item is a Selector instance
*
* #pw-internal
*
* @param Selector $item
* @return bool
*
@@ -158,6 +192,8 @@ class Selectors extends WireArray {
/**
* Per WireArray interface, return a blank Selector
*
* #pw-internal
*
*/
public function makeBlankItem() {
@@ -169,6 +205,8 @@ class Selectors extends WireArray {
*
* Static since there may be multiple instances of this Selectors class at runtime.
* See Selector.php
*
* #pw-internal
*
* @param string $operator
* @param string $class
@@ -184,6 +222,10 @@ class Selectors extends WireArray {
/**
* Return array of all valid operator characters
*
* #pw-group-static-helpers
*
* @return array
*
*/
static public function getOperatorChars() {
@@ -192,6 +234,8 @@ class Selectors extends WireArray {
/**
* Does the given string have an operator in it?
*
* #pw-group-static-helpers
*
* @param string $str
* @return bool
@@ -250,6 +294,8 @@ class Selectors extends WireArray {
* Does the given string start with a selector?
*
* Meaning string starts with [field][operator] like "field="
*
* #pw-group-static-helpers
*
* @param string $str
* @return bool
@@ -283,14 +329,18 @@ class Selectors extends WireArray {
return $has;
}
/**
* Create a new Selector object from a field name, operator, and value
*
* This is mostly for internal use, as the Selectors object already does this when you pass it
* a selector string in the constructor or init() method.
*
* #pw-group-advanced
*
* @param string $field
* @param string $operator
* @param string $value
* @return Selector
* @param string $field Field name or names (separated by a pipe)
* @param string $operator Operator, i.e. "="
* @param string $value Value or values (separated by a pipe)
* @return Selector Returns the correct type of `Selector` object that corresponds to the given `$operator`.
* @throws WireException
*
*/
@@ -613,6 +663,8 @@ class Selectors extends WireArray {
/**
* Given a value string with an "api_var" or "api_var.property" return the string value of the property
*
* #pw-internal
*
* @param string $value var or var.property
* @return null|string Returns null if it doesn't resolve to anything or a string of the value it resolves to
@@ -634,6 +686,8 @@ class Selectors extends WireArray {
* Set whether or not vars should be parsed
*
* By default this is true, so only need to call this method to disable variable parsing.
*
* #pw-internal
*
* @param bool $parseVars
*
@@ -645,6 +699,8 @@ class Selectors extends WireArray {
/**
* Does the given Selector value contain a parseable value?
*
* #pw-internal
*
* @param Selector $selector
* @return bool
*
@@ -665,6 +721,8 @@ class Selectors extends WireArray {
* Does the given value contain an API var reference?
*
* It is assumed the value was quoted in "[value]", and the quotes are not there now.
*
* #pw-internal
*
* @param string $value The value to evaluate
* @return bool
@@ -757,6 +815,8 @@ class Selectors extends WireArray {
/**
* Create this Selectors object from an array
*
* #pw-internal
*
* @param array $a
* @throws WireException
@@ -883,6 +943,7 @@ class Selectors extends WireArray {
if(isset($data['value'])) throw new WireException("You may not specify both 'value' and 'find' at the same time");
// if(!is_array($data['find'])) throw new WireException("Selector 'find' property must be specified as array");
$find = $data['find'];
$data['value'] = array();
}
if(isset($data['whitelist']) && $data['whitelist'] !== null) {
@@ -967,6 +1028,14 @@ class Selectors extends WireArray {
$fields[] = $_name;
}
// convert WireArray types to an array of $_values
if(count($_values) === 1) {
$value = reset($_values);
if(is_object($value) && $value instanceof WireArray) {
$_values = explode('|', (string) $value);
}
}
// determine value(s)
foreach($_values as $value) {
$_sanitize = $sanitize;
@@ -1017,6 +1086,8 @@ class Selectors extends WireArray {
* - If you need a literal comma, use a double comma ",,".
* - If you need a literal equals, use a double equals "==".
*
* #pw-group-static-helpers
*
* @param string $s
* @return array
*
@@ -1052,6 +1123,8 @@ class Selectors extends WireArray {
/**
* Given an assoc array, convert to a key=value selector-style string
*
* #pw-group-static-helpers
*
* @param $a
* @return string
*

View File

@@ -28,7 +28,7 @@
* @method void loginFailure($name, $reason) #pw-hooker
* @method void logoutSuccess(User $user) #pw-hooker
*
* @property SessionCSRF $CSRF
* @property SessionCSRF $CSRF
*
* Expected $config variables include:
* ===================================
@@ -395,9 +395,7 @@ class Session extends Wire implements \IteratorAggregate {
*/
public function get($key, $_key = null) {
if($key == 'CSRF') {
if(!$this->sessionInit) $this->init(); // init required for CSRF
if(is_null($this->CSRF)) $this->CSRF = $this->wire(new SessionCSRF());
return $this->CSRF;
return $this->CSRF();
} else if(!is_null($_key)) {
// namespace
return $this->getFor($key, $_key);
@@ -738,7 +736,7 @@ class Session extends Wire implements \IteratorAggregate {
$this->set('_user', 'challenge', $challenge);
$secure = $this->config->sessionCookieSecure ? (bool) $this->config->https : false;
// set challenge cookie to last 30 days (should be longer than any session would feasibly last)
setcookie(session_name() . '_challenge', $challenge, time()+60*60*24*30, '/', null, $secure, true); // PR #1264
setcookie(session_name() . '_challenge', $challenge, time()+60*60*24*30, '/', $this->config->sessionCookieDomain, $secure, true); // PR #1264
}
if($this->config->sessionFingerprint) {
@@ -892,10 +890,10 @@ class Session extends Wire implements \IteratorAggregate {
$time = time() - 42000;
$secure = $this->config->sessionCookieSecure ? (bool) $this->config->https : false;
if(isset($_COOKIE[$sessionName])) {
setcookie($sessionName, '', $time, '/', null, $secure, true);
setcookie($sessionName, '', $time, '/', $this->config->sessionCookieDomain, $secure, true);
}
if(isset($_COOKIE[$sessionName . "_challenge"])) {
setcookie($sessionName . "_challenge", '', $time, '/', null, $secure, true);
setcookie($sessionName . "_challenge", '', $time, '/', $this->config->sessionCookieDomain, $secure, true);
}
}
@@ -954,6 +952,7 @@ class Session extends Wire implements \IteratorAggregate {
if(!strpos($url, 'modal=')) $url .= (strpos($url, '?') !== false ? '&' : '?') . 'modal=1';
}
}
$this->wire()->setStatus(ProcessWire::statusFinished);
if($http301) header("HTTP/1.1 301 Moved Permanently");
header("Location: $url");
exit(0);
@@ -1132,4 +1131,32 @@ class Session extends Wire implements \IteratorAggregate {
}
}
/**
* Return an instance of ProcessWires CSRF object, which provides an API for cross site request forgery protection.
*
* ~~~~
* // output somewhere in <form> markup when rendering a form
* echo $session->CSRF->renderInput();
* ~~~~
* ~~~~
* // when processing form (POST request), check to see if token is present
* if($session->CSRF->hasValidToken()) {
* // form submission is valid
* // okay to process
* } else {
* // form submission is NOT valid
* throw new WireException('CSRF check failed!');
* }
* ~~~~
*
* @return SessionCSRF
* @see SessionCSRF::renderInput(), SessionCSRF::validate(), SessionCSRF::hasValidToken()
*
*/
public function CSRF() {
if(!$this->sessionInit) $this->init(); // init required for CSRF
if(is_null($this->CSRF)) $this->CSRF = $this->wire(new SessionCSRF());
return $this->CSRF;
}
}

View File

@@ -2,26 +2,52 @@
/**
* ProcessWire CSRF Protection
*
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* https://processwire.com
*
*/
/**
* Triggered when CSRF detected
* Exception triggered by SessionCSRF::validate() when CSRF detected
*
*/
class WireCSRFException extends WireException {}
/**
* ProcessWire CSRF Protection class
* ProcessWire CSRF Protection
*
* #pw-summary Provides an API for cross site request forgery protection.
* #pw-body =
* ~~~~
* // output somewhere in form markup when rendering a form
* echo $session->CSRF->renderInput();
* ~~~~
* ~~~~
* // when processing form (POST request), check to see if token is present
* if($session->CSRF->hasValidToken()) {
* // form submission is valid
* // okay to process
* } else {
* // form submission is NOT valid
* throw new WireException('CSRF check failed!');
* }
* ~~~~
* ~~~~
* // this alternative to hasValidToken() throws WireCSRFException when invalid
* $session->CSRF->validate();
* ~~~~
*
* #pw-body
*
*
*/
class SessionCSRF extends Wire {
/**
* Get a CSRF Token name, or create one if it doesn't yet exist
*
* #pw-group-initiating
*
* @param int|string|null $id Optional unique ID for this token
* @return string
@@ -38,6 +64,8 @@ class SessionCSRF extends Wire {
/**
* Get a CSRF Token value as stored in the session, or create one if it doesn't yet exist
*
* #pw-group-initiating
*
* @param int|string|null $id Optional unique ID for this token
* @return string
@@ -57,6 +85,8 @@ class SessionCSRF extends Wire {
/**
* Get a CSRF Token timestamp
*
* #pw-group-initiating
*
* @param int|string|null $id Optional unique ID for this token
* @return string
@@ -70,6 +100,8 @@ class SessionCSRF extends Wire {
/**
* Get a CSRF Token name and value
*
* #pw-group-initiating
*
* @param int|string|null $id Optional unique ID for this token
* @return array ("name" => "token name", "value" => "token value", "time" => created timestamp)
@@ -87,7 +119,9 @@ class SessionCSRF extends Wire {
* Get a CSRF Token name and value that can only be used once
*
* Note that a single call to hasValidToken($id) or validate($id) will invalidate the single use token.
* So call them once and store your result if you need the result multiple times.
* So call them once and store your result if you need the result multiple times.
*
* #pw-group-initiating
*
* @param int|string $id Optional unique ID/name for this token (of omitted one is generated automatically)
* @return array ("id' => "token ID", "name" => "token name", "value" => "token value", "time" => created timestamp)
@@ -111,6 +145,8 @@ class SessionCSRF extends Wire {
/**
* Returns true if the current POST request contains a valid CSRF token, false if not
*
* #pw-group-validating
*
* @param int|string|null $id Optional unique ID for this token, but required if checking a single use token.
* @return bool
@@ -139,6 +175,8 @@ class SessionCSRF extends Wire {
/**
* Throws an exception if the token is invalid
*
* #pw-group-validating
*
* @param int|string|null $id Optional unique ID for this token
* @throws WireCSRFException if token not valid
@@ -154,6 +192,8 @@ class SessionCSRF extends Wire {
/**
* Clear out token value
*
* #pw-group-resetting
*
* @param int|string|null $id Optional unique ID for this token
*
@@ -166,6 +206,8 @@ class SessionCSRF extends Wire {
/**
* Clear out all saved token values
*
* #pw-group-resetting
*
*/
public function resetAll() {
@@ -174,6 +216,15 @@ class SessionCSRF extends Wire {
/**
* Render a form input[hidden] containing the token name and value, as looked for by hasValidToken()
*
* ~~~~~
* <form method='post'>
* <input type='submit'>
* <?php echo $session->CSRF->renderInput(); ?>
* </form>
* ~~~~~
*
* #pw-group-initiating
*
* @param int|string|null $id Optional unique ID for this token
* @return string

View File

@@ -20,6 +20,7 @@
* @property int $flags Flags (bitmask) assigned to this template. See the flag constants. #pw-group-identification
* @property string $ns Namespace found in the template file, or blank if not determined. #pw-group-identification
* @property string $pageClass Class for instantiated page objects. Page assumed if blank, or specify class name. #pw-group-identification
* @property int $modified Last modified time for template or template file
*
* Fieldgroup/Fields
*

View File

@@ -72,7 +72,21 @@ class TemplateFile extends WireData {
protected $halt = false;
/**
* Variables that will be applied globally to this and all other TemplateFile instances
* Last tracked profile event
*
* @var mixed
*
*/
protected $profilerEvent = null;
/**
* @var WireProfilerInterface|null
*
*/
protected $profiler = null;
/**
* DEPRECATED: Variables that will be applied globally to this and all other TemplateFile instances
*
*/
static protected $globals = array();
@@ -165,13 +179,16 @@ class TemplateFile extends WireData {
}
/**
* Sets a variable to be globally accessable to all other TemplateFile instances
* Sets a variable to be globally accessable to all other TemplateFile instances (deprecated)
*
* Note, to set a variable for just this instance, use the set() as inherted from WireData.
*
* #pw-internal
*
* @param string $name
* @param mixed $value
* @param bool $overwrite Should the value be overwritten if it already exists? (default true)
* @deprecated
*
*/
public function setGlobal($name, $value, $overwrite = true) {
@@ -180,6 +197,27 @@ class TemplateFile extends WireData {
self::$globals[$name] = $value;
}
/**
* Start profiling a render
*
* @param string $filename
*
*/
protected function start($filename) {
if($this->profiler) {
$f = str_replace($this->wire('config')->paths->root, '/', $filename);
$this->profilerEvent = $this->profiler->start($f, $this);
}
}
/**
* Stop profiling a render
*
*/
protected function stop() {
if($this->profilerEvent) $this->profiler->stop($this->profilerEvent);
}
/**
* Render the template -- execute it and return it's output
*
@@ -200,7 +238,8 @@ class TemplateFile extends WireData {
// ensure that wire() functions in template file map to correct ProcessWire instance
$this->savedInstance = ProcessWire::getCurrentInstance();
ProcessWire::setCurrentInstance($this->wire());
$this->profiler = $this->wire('profiler');
$this->savedDir = getcwd();
if($this->chdir) {
@@ -208,19 +247,36 @@ class TemplateFile extends WireData {
} else {
chdir(dirname($this->filename));
}
$fuel = array_merge($this->getArray(), self::$globals); // so that script can foreach all vars to see what's there
extract($fuel);
ob_start();
foreach($this->prependFilename as $_filename) {
if($this->halt) break;
if($this->profiler) $this->start($_filename);
/** @noinspection PhpIncludeInspection */
require($_filename);
if($this->profiler) $this->stop();
}
if(!$this->halt) $returnValue = require($this->filename);
if($this->profiler) $this->start($this->filename);
if($this->halt) {
$returnValue = 0;
} else {
/** @noinspection PhpIncludeInspection */
$returnValue = require($this->filename);
}
if($this->profiler) $this->stop();
foreach($this->appendFilename as $_filename) {
if($this->halt) break;
if($this->profiler) $this->start($_filename);
/** @noinspection PhpIncludeInspection */
require($_filename);
if($this->profiler) $this->stop();
}
$out = "\n" . ob_get_contents() . "\n";
ob_end_clean();
@@ -229,6 +285,7 @@ class TemplateFile extends WireData {
$out = trim($out);
if(!strlen($out) && !$this->halt && $returnValue && $returnValue !== 1) return $returnValue;
return $out;
}
@@ -297,6 +354,7 @@ class TemplateFile extends WireData {
* USAGE from template file is: return $this->halt();
*
* @param bool $halt
* @return $this
*
*/
protected function halt($halt = true) {

View File

@@ -13,6 +13,7 @@
* @method TemplatesArray find($selector) Return the templates matching the the given selector query. #pw-internal
* @method bool save(Template $template) Save the given Template.
* @method bool delete() delete(Template $template) Delete the given Template. Note that this will throw a fatal error if the template is in use by any pages.
* @method bool|Saveable|Template clone(Saveable $item, $name = '')
*
*/
class Templates extends WireSaveableItems {

View File

@@ -14,7 +14,7 @@
* modifying arguments or return values. Several other hook methods are also provided for Wire derived
* classes that are hooking into others.
* #pw-body
* #pw-order-groups common,identification,hooks,notices,changes,hooker
* #pw-order-groups common,identification,hooks,notices,changes,hooker,api-helpers
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* https://processwire.com
@@ -55,6 +55,32 @@
* @method callUnknown($method, $arguments) See Wire::___callUnknown()
* @method Wire trackException(\Exception $e, $severe = true, $text = null)
*
* The following map API variables to function names and apply only if another function in the class does not
* already have the same name, which would override. All defined API variables can be accessed as functions
* that return the API variable, whether documented below or not.
*
* @method Pages|PageArray|Page|NullPage pages($selector = '') Access the $pages API variable as a function. #pw-group-api-helpers
* @method Page|Mixed page($key = '', $value = null) Access the $page API variable as a function. #pw-group-api-helpers
* @method Config|mixed config($key = '', $value = null) Access the $config API variable as a function. #pw-group-api-helpers
* @method Modules|Module|ConfigurableModule|null modules($name = '') Access the $modules API variable as a function. #pw-group-api-helpers
* @method User|mixed user($key = '', $value = null) Access the $user API variable as a function. #pw-group-api-helpers
* @method Users|PageArray|User|mixed users($selector = '') Access the $users API variable as a function. #pw-group-api-helpers
* @method Session|mixed session($key = '', $value = null) Access the $session API variable as a function. #pw-group-api-helpers
* @method Field|Fields|null fields($name = '') Access the $fields API variable as a function. #pw-group-api-helpers
* @method Templates|Template|null templates($name = '') Access the $templates API variable as a function. #pw-group-api-helpers
* @method WireDatabasePDO database() Access the $database API variable as a function. #pw-group-api-helpers
* @method Permissions|Permission|PageArray|null|NullPage permissions($selector = '') Access the $permissions API variable as a function. #pw-group-api-helpers
* @method Roles|Role|PageArray|null|NullPage roles($selector = '') Access the $roles API variable as a function. #pw-group-api-helpers
* @method Sanitizer|string|int|array|null|mixed sanitizer($name = '', $value = '') Access the $sanitizer API variable as a function. #pw-group-api-helpers
* @method WireDateTime|string|int datetime($format = '', $value = '') Access the $datetime API variable as a function. #pw-group-api-helpers
* @method WireFileTools files() Access the $files API variable as a function. #pw-group-api-helpers
* @method WireCache|string|array|PageArray|null cache($name = '', $expire = null, $func = null) Access the $cache API variable as a function. #pw-group-api-helpers
* @method Languages|Language|NullPage|null languages($name = '') Access the $languages API variable as a function. #pw-group-api-helpers
* @method WireInput|WireInputData array|string|int|null input($type = '', $key = '', $sanitizer = '') Access the $input API variable as a function. #pw-group-api-helpers
* @method WireInputData|string|int|array|null inputGet($key = '', $sanitizer = '') Access the $input->get() API variable as a function. #pw-group-api-helpers
* @method WireInputData|string|int|array|null inputPost($key = '', $sanitizer = '') Access the $input->post() API variable as a function. #pw-group-api-helpers
* @method WireInputData|string|int|array|null inputCookie($key = '', $sanitizer = '') Access the $input->cookie() API variable as a function. #pw-group-api-helpers
*
*/
abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable {
@@ -371,13 +397,47 @@ abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable {
$hooks = $this->wire('hooks');
if($hooks) {
$result = $hooks->runHooks($this, $method, $arguments);
if(!$result['methodExists'] && !$result['numHooksRun']) return $this->callUnknown($method, $arguments);
if(!$result['methodExists'] && !$result['numHooksRun']) {
$result = $this->_callWireAPI($method, $arguments);
if(!$result) return $this->callUnknown($method, $arguments);
}
} else {
return $this->___callUnknown($method, $arguments);
$result = $this->_callWireAPI($method, $arguments);
if(!$result) return $this->___callUnknown($method, $arguments);
}
return $result['return'];
}
/**
* Helper to __call() method that maps a call to an API variable when appropriate
*
* @param string $method
* @param array $arguments
* @return array|bool
* @internal
*
*/
protected function _callWireAPI($method, $arguments) {
$var = $this->_wire ? $this->_wire->fuel()->$method : null;
if(!$var) return false;
// requested method maps to an API variable
$result = array('return' => null);
$funcName = 'wire' . ucfirst($method);
if(__NAMESPACE__) $funcName = __NAMESPACE__ . "\\$funcName";
if(count($arguments) && function_exists($funcName)) {
// a function exists with this API var name
$wire = ProcessWire::getCurrentInstance();
// ensure function call maps to this PW instance
if($wire !== $this->_wire) ProcessWire::setCurrentInstance($this->_wire);
$result['return'] = call_user_func_array($funcName, $arguments);
if($wire !== $this->_wire) ProcessWire::setCurrentInstance($wire);
} else {
// if no arguments provided, just return API var
$result['return'] = $var;
}
return $result;
}
/**
* If method call resulted in no handler, this hookable method is called.
*

View File

@@ -1900,6 +1900,7 @@ class WireArray extends Wire implements \IteratorAggregate, \ArrayAccess, \Count
* - `append` (string): String to append to result. Ignored if result is blank.
* - Please note that if delimiter is omitted, $options becomes the second argument.
* @return string
* @see WireArray::each(), WireArray::explode()
*
*/
public function implode($delimiter, $property = '', array $options = array()) {
@@ -1966,6 +1967,7 @@ class WireArray extends Wire implements \IteratorAggregate, \ArrayAccess, \Count
* - `getMethod` (string): Method to call on each item to retrieve $property (default = "get")
* - `key` (string|null): Property of Wire objects to use for key of array, or omit (null) for non-associative array (default).
* @return array
* @see WireArray::each(), WireArray::implode()
*
*/
public function explode($property, array $options = array()) {
@@ -2046,11 +2048,10 @@ class WireArray extends Wire implements \IteratorAggregate, \ArrayAccess, \Count
* @param string|null $key Name of data property you want to get or set. Omit to get all data properties.
* @param mixed|null $value Value of data property you want to set. Omit when getting properties.
* @return $this|mixed|null Returns one of the following, depending on specified arguments:
* - `mixed` when getting a single property: whatever you set is what you will get back.
* - `null` if the property you are trying to get does not exist in the data.
* - `$this` reference to this WireArray if you were setting a value.
* - `array` of all data if you specified no arguments.
*
* - `mixed` when getting a single property: whatever you set is what you will get back.
* - `null` if the property you are trying to get does not exist in the data.
* - `$this` reference to this WireArray if you were setting a value.
* - `array` of all data if you specified no arguments.
*
*/
public function data($key = null, $value = null) {
@@ -2202,6 +2203,7 @@ class WireArray extends Wire implements \IteratorAggregate, \ArrayAccess, \Count
* (if using option #3 in arguments).
* - `array` (4): Returns an array of associative arrays containing the property values for each item
* you requested (if using option #4 in arguments).
* @see WireArray::implode(), WireArray::explode()
*
*/
public function each($func = null) {

View File

@@ -337,13 +337,13 @@ class WireCache extends Wire {
*
* @param string $name Name of cache, can be any string up to 255 chars
* @param string|array|PageArray $data Data that you want to cache
* @param int|Page $expire Lifetime of this cache, in seconds
* ...or specify: WireCache::expireHourly, WireCache::expireDaily, WireCache::expireWeekly, WireCache::expireMonthly
* ...or specify the future date you want it to expire (as unix timestamp or any strtotime compatible date format)
* ...or provide a Page object to expire when any page using that template is saved.
* ...or specify: WireCache::expireNever to prevent expiration.
* ...or specify: WireCache::expireSave to expire when any page or template is saved.
* ...or specify: Selector string matching pages that, when saved, expire the cache.
* @param int|Page $expire Lifetime of this cache, in seconds, OR one of the following:
* - Specify one of the `WireCache::expire*` constants.
* - Specify the future date you want it to expire (as unix timestamp or any strtotime compatible date format)
* - Provide a Page object to expire when any page using that template is saved.
* - Specify `WireCache::expireNever` to prevent expiration.
* - Specify `WireCache::expireSave` to expire when any page or template is saved.
* - Specify selector string matching pages that, when saved, expire the cache.
* @return bool Returns true if cache was successful, false if not
* @throws WireException if given uncachable data
*
@@ -375,6 +375,8 @@ class WireCache extends Wire {
$data = json_encode($data);
if($data === false) throw new WireException("Unable to encode array data for cache: $name");
}
if(is_null($data)) $data = '';
$sql =
'INSERT INTO caches (`name`, `data`, `expires`) VALUES(:name, :data, :expires) ' .
@@ -401,11 +403,17 @@ class WireCache extends Wire {
/**
* Same as save() except with namespace
*
* @param string|object $ns Namespace
* @param $name
* @param $data
* @param int $expire
* @return bool
* @param string|object $ns Namespace for cache
* @param string $name Name of cache, can be any string up to 255 chars
* @param string|array|PageArray $data Data that you want to cache
* @param int|Page $expire Lifetime of this cache, in seconds, OR one of the following:
* - Specify one of the `WireCache::expire*` constants.
* - Specify the future date you want it to expire (as unix timestamp or any strtotime compatible date format)
* - Provide a Page object to expire when any page using that template is saved.
* - Specify `WireCache::expireNever` to prevent expiration.
* - Specify `WireCache::expireSave` to expire when any page or template is saved.
* - Specify selector string matching pages that, when saved, expire the cache.
* @return bool Returns true if cache was successful, false if not
*
*/
public function saveFor($ns, $name, $data, $expire = self::expireDaily) {
@@ -521,7 +529,7 @@ class WireCache extends Wire {
/**
* Delete the cache identified by $name within given namespace ($ns)
*
* @param string $ns
* @param string $ns Namespace of cache
* @param string $name If none specified, all for $ns are deleted
* @return bool True on success, false on failure
*

View File

@@ -23,6 +23,8 @@
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* https://processwire.com
*
* @method WireArray and($items = null)
*
*/
@@ -399,15 +401,16 @@ class WireData extends Wire implements \IteratorAggregate, \ArrayAccess {
*
* #pw-group-retrieval
*
* @param WireArray|WireData|string $items May be any of the following:
* @param WireArray|WireData|string|null $items May be any of the following:
* - `WireData` object (or derivative)
* - `WireArray` object (or derivative)
* - Name of any property from this object that returns one of the above.
* - Omit argument to simply return this object in a WireArray
* @return WireArray Returns a WireArray of this object *and* the one(s) given.
* @throws WireException If invalid argument supplied.
*
*/
public function ___and($items) {
public function ___and($items = null) {
if(is_string($items)) $items = $this->get($items);
@@ -415,13 +418,13 @@ class WireData extends Wire implements \IteratorAggregate, \ArrayAccess {
// great, that's what we want
$a = clone $items;
$a->prepend($this);
} else if($items instanceof WireData) {
} else if($items instanceof WireData || is_null($items)) {
// single item
$className = $this->className(true) . 'Array';
if(!class_exists($className)) $className = wireClassName('WireArray', true);
$a = $this->wire(new $className());
if(!class_exists($className)) $className = wireClassName('WireArray', true);
$a = $this->wire(new $className());
$a->add($this);
$a->add($items);
if($items) $a->add($items);
} else {
// unknown
throw new WireException('Invalid argument provided to WireData::and(...)');

View File

@@ -1,35 +1,57 @@
<?php namespace ProcessWire;
/**
* ProcessWire Database Backup and Restore
*
* #pw-summary ProcessWire Database Backup and Restore
* #pw-summary-initialization Its not typically necessary to call these initialization methods unless doing manual initialization.
* #pw-var $backup
* #pw-instantiate $backup = $database->backup();
* #pw-order-groups actions,reporting,initialization,advanced
* #pw-body =
* This class intentionally does not have any external dependencies (other than PDO)
* so that it can be included by outside tools for restoring/exporting, with the main
* example of that being the ProcessWire installer.
*
* The recommended way to access these backup methods is via the `$database` API variable
* method `$database->backups()`, which returns a `WireDatabaseBackup` instance, however
* you can also initialize the class manually if you prefer, like this:
* ~~~~~
* // determine where backups will go (should NOT be web accessible)
* $backupPath = $config->paths->assets . 'backups/';
*
* // create a new WireDatabaseBackup instance
* $backup = new WireDatabaseBackup($backupPath);
*
* // Option 1: set the already-connected DB connection
* $backup->setDatabase($this->database);
*
* // Option 2: OR provide a Config object that contains the DB connection info
* $backup->setDatabaseConfig($this->config);
*
* ~~~~~
* ### Backup the database
* ~~~~~
* $file = $backup->backup();
* if($file) {
* echo "Backed up to: $file";
* } else {
* echo "Backup failed: " . implode("<br>", $backup->errors());
* }
* ~~~~~
*
* ### Restore a database
* ~~~~~
* $success = $backup->restore($file);
* if($success) {
* echo "Restored database from file: $file";
* } else {
* echo "Restore failed: " . implode("<br>", $backup->errors());
* }
* ~~~~~
* #pw-body
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* https://processwire.com
*
* USAGE
*
* Initialization
* ==============
* $backup = new WireDatabaseBackup('/path/to/backups/');
* $backup->setDatabase($this->database); // optional, if omitted it will attempt it's own connection
* $backup->setDatabaseConfig($this->config); // optional, only if setDatabase() was called
*
* Backup
* ======
* $file = $backup->backup([$options]);
* if($file) print_r($backup->notes());
* else print_r($backup->errors());
*
*
* Restore
* =======
* $success = $backup->restore($file, [$options]);
* if($success) print_r($backup->notes());
* else print_r($backup->errors());
*
*/
@@ -219,13 +241,31 @@ class WireDatabaseBackup {
*/
protected $path = null;
/**
* Cache for getAllTables()
*
* @var array
*
*/
protected $tables = array();
/**
* Cache for getAllTables()
*
* @var array
*
*/
protected $counts = array();
/**
* Construct
*
* You should follow-up the construct call with one or both of the following:
*
* - $backups->setDatabase(PDO|WireDatabasePDO);
* - $backups->setDatabaseConfig(array|object);
* - $backups->setDatabaseConfig(array|object);
*
* #pw-group-initialization
*
* @param string $path Path where database files are stored
* @throws \Exception
@@ -238,6 +278,8 @@ class WireDatabaseBackup {
/**
* Set the current ProcessWire instance
*
* #pw-internal
*
* @param ProcessWire $wire
*
*/
@@ -248,8 +290,16 @@ class WireDatabaseBackup {
/**
* Set the database configuration information
*
* @param array|object $config Containing these properties: dbUser, dbHost, dbPort, dbName,
* and optionally: dbPass, dbPath, dbCharset
* #pw-group-initialization
*
* @param array|Config|object $config Containing these properties:
* - dbUser
* - dbHost
* - dbPort
* - dbName
* - dbPass
* - dbPath (optional)
* - dbCharset (optional)
* @return $this
* @throws \Exception if missing required config settings
*
@@ -285,7 +335,9 @@ class WireDatabaseBackup {
}
/**
* Set the database connection
* Set the PDO database connection
*
* #pw-group-initialization
*
* @param \PDO|WireDatabasePDO $database
* @throws \PDOException on invalid connection
@@ -302,6 +354,8 @@ class WireDatabaseBackup {
/**
* Get current database connection, initiating the connection if not yet active
*
* #pw-advanced
*
* @return null|\PDO|WireDatabasePDO
* @throws \Exception
*
@@ -333,6 +387,8 @@ class WireDatabaseBackup {
/**
* Add an error and return last error
*
* #pw-group-reporting
*
* @param string $str If omitted, no error is added
* @return string
*
@@ -345,6 +401,8 @@ class WireDatabaseBackup {
/**
* Return all error messages that occurred
*
* #pw-group-reporting
*
* @param bool $reset Specify true to clear out existing errors or omit just to return error messages
* @return array
*
@@ -357,6 +415,8 @@ class WireDatabaseBackup {
/**
* Record a note
*
* #pw-group-reporting
*
* @param $key
* @param $value
@@ -369,6 +429,8 @@ class WireDatabaseBackup {
/**
* Get all notes
*
* #pw-group-reporting
*
* @param bool $reset
* @return array
@@ -383,6 +445,8 @@ class WireDatabaseBackup {
/**
* Set path where database files are stored
*
* #pw-group-initialization
*
* @param string $path
* @return $this
* @throws \Exception if path has a problem
@@ -395,7 +459,15 @@ class WireDatabaseBackup {
$this->path = $path;
return $this;
}
/**
* Get path where database files are stored
*
* #pw-group-reporting
*
* @return string
*
*/
public function getPath() {
return $this->path;
}
@@ -404,6 +476,8 @@ class WireDatabaseBackup {
* Return array of all backup files
*
* To get additional info on any of them, call getFileInfo($basename) method
*
* #pw-group-reporting
*
* @return array of strings (basenames)
*
@@ -423,8 +497,10 @@ class WireDatabaseBackup {
/**
* Get information about a backup file
*
* #pw-group-reporting
*
* @param $filename
* @param string $filename
* @return array Returns associative array of information on success, empty array on failure
*
*/
@@ -490,6 +566,8 @@ class WireDatabaseBackup {
/**
* Get array of all table names
*
* #pw-group-reporting
*
* @param bool $count If true, returns array will be indexed by name and include count of records as value
* @param bool $cache Allow use of cache?
@@ -498,35 +576,32 @@ class WireDatabaseBackup {
*/
public function getAllTables($count = false, $cache = true) {
static $tables = array();
static $counts = array();
if($cache) {
if($count && count($counts)) return $counts;
if(count($tables)) return $tables;
if($count && count($this->counts)) return $this->counts;
if(count($this->tables)) return $this->tables;
} else {
$tables = array();
$counts = array();
$this->tables = array();
$this->counts = array();
}
$query = $this->database->prepare('SHOW TABLES');
$query->execute();
/** @noinspection PhpAssignmentInConditionInspection */
while($row = $query->fetch(\PDO::FETCH_NUM)) $tables[$row[0]] = $row[0];
while($row = $query->fetch(\PDO::FETCH_NUM)) $this->tables[$row[0]] = $row[0];
$query->closeCursor();
if($count) {
foreach($tables as $table) {
foreach($this->tables as $table) {
$query = $this->database->prepare("SELECT COUNT(*) FROM `$table`");
$query->execute();
$row = $query->fetch(\PDO::FETCH_NUM);
$counts[$table] = (int) $row[0];
$this->counts[$table] = (int) $row[0];
}
$query->closeCursor();
return $counts;
return $this->counts;
} else {
return $tables;
return $this->tables;
}
}
@@ -535,9 +610,28 @@ class WireDatabaseBackup {
/**
* Perform a database export/dump
*
* @param array $options See $backupOptions
* @return string Full path and filename of database export file, or false on failure.
* #pw-group-actions
*
* @param array $options Options to modify default behavior:
* - `filename` (string): filename for backup: default is to make a dated filename, but this can also be used (basename only, no path)
* - `description` (string): optional description of this backup
* - `tables` (array): if specified, export will only include these tables
* - `user` (string): username to associate with the backup file (string), optional
* - `excludeTables` (array): exclude creating or inserting into these tables
* - `excludeCreateTables` (array): exclude creating these tables, but still export data
* - `excludeExportTables` (array): exclude exporting data, but still create tables
* - `whereSQL` (array): SQL conditions for export of individual tables [table => [SQL conditions]]. The `table` portion (index) may also be a full PCRE regexp, must start with `/` to be recognized as regex.
* - `maxSeconds` (int): max number of seconds allowed for execution (default=1200)
* - `allowDrop` (bool): use DROP TABLES statements before CREATE TABLE statements? (default=true)
* - `allowUpdate` (bool): use UPDATE ON DUPLICATE KEY so that INSERT statements can UPDATE when rows already present (all tables). (default=false)
* - `allowUpdateTables` (array): table names that will use UPDATE ON DUPLICATE KEY (does NOT require allowUpdate=true)
* - `findReplace` (array): find and replace in row data during backup. Example: ['databass' => 'database']
* - `findReplaceCreateTable` (array): find and replace in create table statements
* Example: ['DEFAULT CHARSET=latin1;' => 'DEFAULT CHARSET=utf8;']
* - `extraSQL` (array): additional SQL queries to append at the bottom. Example: ['UPDATE pages SET created=NOW()']
* @return string Full path and filename of database export file, or false on failure.
* @throws \Exception on fatal error
* @see WireDatabaseBackup::restore()
*
*/
public function backup(array $options = array()) {
@@ -582,7 +676,16 @@ class WireDatabaseBackup {
return $success ? $file : false;
}
/**
* Set backup options
*
* #pw-internal
*
* @param array $options
* @return $this
*
*/
public function setBackupOptions(array $options) {
$this->backupOptions = array_merge($this->backupOptions, $options);
return $this;
@@ -811,12 +914,26 @@ class WireDatabaseBackup {
/**
* Import a database SQL file that was created by this class
* Restore/import a MySQL database dump file
*
* This method is designed to restore dump files created by the backup() method of this
* class, however it *may* also work with dump files created from other sources like
* mysqldump or PhpMyAdmin.
*
* #pw-group-actions
*
* @param string $filename Filename to restore, optionally including path (if no path, then path set to construct is assumed)
* @param array $options See WireDatabaseBackup::$restoreOptions
* @param array $options Options to modify default behavior:
* - `tables` (array): table names to restore (empty=all)
* - `allowDrop` (bool): allow DROP TABLE statements (default=true)
* - `haltOnError` (bool): halt execution when an error occurs? (default=false)
* - `maxSeconds` (int): max number of seconds allowed for execution (default=1200)
* - `findReplace` (array): find and replace in row data. Example: ['databass' => 'database']
* - `findReplaceCreateTable` (array): find and replace in create table statements.
* Example: ['DEFAULT CHARSET=utf8;' => 'DEFAULT CHARSET=utf8mb4;']
* @return true on success, false on failure. Call the errors() method to retrieve errors.
* @throws \Exception on fatal error
* @see WireDatabaseBackup::backup()
*
*/
public function restore($filename, array $options = array()) {
@@ -843,7 +960,16 @@ class WireDatabaseBackup {
return $success;
}
/**
* Set restore options
*
* #pw-internal
*
* @param array $options
* @return $this
*
*/
public function setRestoreOptions(array $options) {
$this->restoreOptions = array_merge($this->restoreOptions, $options);
return $this;
@@ -983,6 +1109,8 @@ class WireDatabaseBackup {
*
* This method assumes both files follow the SQL dump format created by this class.
*
* #pw-advanced
*
* @param string $filename1 Original filename
* @param string $filename2 Filename that may have statements that will update/override those in filename1
* @param array $options
@@ -1077,6 +1205,8 @@ class WireDatabaseBackup {
/**
* Returns array of all create table statements, indexed by table name
*
* #pw-internal
*
* @param string $filename to extract all CREATE TABLE statements from
* @param array $options
* @return bool|array of CREATE TABLE statements, associative: indexed by table name
@@ -1099,7 +1229,9 @@ class WireDatabaseBackup {
}
/**
* Returns array of all INSERT statements, indexed by table name
* Returns array of all INSERT statements in given filename, indexed by table name
*
* #pw-internal
*
* @param string $filename to extract all CREATE TABLE statements from
* @return array of arrays of INSERT statements. Base array is associative indexed by table name.

View File

@@ -55,6 +55,14 @@ class WireDatabasePDO extends Wire implements WireDatabase {
*/
protected $pdo = null;
/**
* Whether or not our _init() has been called for the current $pdo connection
*
* @var bool
*
*/
protected $init = false;
/**
* PDO connection settings
*
@@ -99,6 +107,8 @@ class WireDatabasePDO extends Wire implements WireDatabase {
$name = $config->dbName;
$socket = $config->dbSocket;
$charset = $config->dbCharset;
$initCommand = str_replace('{charset}', $charset, $config->dbInitCommand);
if($socket) {
// if socket is provided ignore $host and $port and use $socket instead:
$dsn = "mysql:unix_socket=$socket;dbname=$name;";
@@ -107,13 +117,18 @@ class WireDatabasePDO extends Wire implements WireDatabase {
$port = $config->dbPort;
if($port) $dsn .= ";port=$port";
}
$driver_options = array(
\PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES '$charset'",
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION
);
);
if($initCommand) $driver_options[\PDO::MYSQL_ATTR_INIT_COMMAND] = $initCommand;
$database = new WireDatabasePDO($dsn, $username, $password, $driver_options);
$database->setDebugMode($config->debug);
$config->wire($database);
$database->_init();
return $database;
}
@@ -134,7 +149,37 @@ class WireDatabasePDO extends Wire implements WireDatabase {
$this->pdoConfig['pass'] = $password;
$this->pdoConfig['options'] = $driver_options;
$this->pdo();
$this->queryLogMax = (int) $this->wire('config')->dbQueryLogMax;
}
/**
* Additional initialization after DB connection established and Wire instance populated
*
* #pw-internal
*
*/
public function _init() {
if($this->init || !$this->isWired()) return;
$this->init = true;
$config = $this->wire('config');
$this->queryLogMax = (int) $config->dbQueryLogMax;
$sqlModes = $config->dbSqlModes;
if(is_array($sqlModes)) {
// ["5.7.0" => "remove:mode1,mode2/add:mode3"]
foreach($sqlModes as $minVersion => $commands) {
if(strpos($commands, '/') !== false) {
$commands = explode('/', $commands);
} else {
$commands = array($commands);
}
foreach($commands as $modes) {
$modes = trim($modes);
if(empty($modes)) continue;
$action = 'set';
if(strpos($modes, ':')) list($action, $modes) = explode(':', $modes);
$this->sqlMode(trim($action), trim($modes), $minVersion);
}
}
}
}
/**
@@ -148,12 +193,16 @@ class WireDatabasePDO extends Wire implements WireDatabase {
*
*/
public function pdo() {
if(!$this->pdo) $this->pdo = new \PDO(
$this->pdoConfig['dsn'],
$this->pdoConfig['user'],
$this->pdoConfig['pass'],
$this->pdoConfig['options']
);
if(!$this->pdo) {
$this->init = false;
$this->pdo = new \PDO(
$this->pdoConfig['dsn'],
$this->pdoConfig['user'],
$this->pdoConfig['pass'],
$this->pdoConfig['options']
);
}
if(!$this->init) $this->_init();
return $this->pdo;
}
@@ -684,13 +733,13 @@ class WireDatabasePDO extends Wire implements WireDatabase {
/**
* Retrieve new instance of WireDatabaseBackups ready to use with this connection
*
* See WireDatabaseBackup class for usage.
* See `WireDatabaseBackup` class for usage.
*
* #pw-group-custom
*
* @return WireDatabaseBackup
* @throws WireException|\Exception on fatal error
* @see WireDatabaseBackup
* @see WireDatabaseBackup::backup(), WireDatabaseBackup::restore()
*
*/
public function backups() {
@@ -731,4 +780,73 @@ class WireDatabasePDO extends Wire implements WireDatabase {
return $max;
}
/**
* Get SQL mode, set SQL mode, add to existing SQL mode, or remove from existing SQL mode
*
* #pw-group-custom
*
* ~~~~~
* // Get SQL mode
* $mode = $database->sqlMode();
*
* // Add an SQL mode
* $database->sqlMode('add', 'STRICT_TRANS_TABLES');
*
* // Remove SQL mode if version at least 5.7.0
* $database->sqlMode('remove', 'ONLY_FULL_GROUP_BY', '5.7.0');
* ~~~~~
*
* @param string $action Specify "get", "set", "add" or "remove". (default="get")
* @param string $mode Mode string or CSV string with SQL mode(s), i.e. "STRICT_TRANS_TABLES,ONLY_FULL_GROUP_BY".
* This argument should be omitted when using the "get" action.
* @param string $minVersion Make the given action only apply if MySQL version is at least $minVersion, i.e. "5.7.0".
* @return string|bool Returns string in "get" action, boolean false if required version not present, or true otherwise.
* @throws WireException If given an invalid $action
*
*/
public function sqlMode($action = 'get', $mode = '', $minVersion = '') {
$result = true;
$modes = array();
if(empty($action)) $action = 'get';
if($action !== 'get' && $minVersion) {
$serverVersion = $this->getAttribute(\PDO::ATTR_SERVER_VERSION);
if(version_compare($serverVersion, $minVersion, '<')) return false;
}
if($mode) {
foreach(explode(',', $mode) as $m) {
$modes[] = $this->escapeStr(strtoupper($this->wire('sanitizer')->fieldName($m)));
}
}
switch($action) {
case 'get':
$query = $this->pdo()->query("SELECT @@sql_mode");
$result = $query->fetchColumn();
$query->closeCursor();
break;
case 'set':
$modes = implode(',', $modes);
$result = $modes;
$this->pdo()->exec("SET sql_mode='$modes'");
break;
case 'add':
foreach($modes as $m) {
$this->pdo()->exec("SET sql_mode=(SELECT CONCAT(@@sql_mode,',$m'))");
}
break;
case 'remove':
foreach($modes as $m) {
$this->pdo()->exec("SET sql_mode=(SELECT REPLACE(@@sql_mode,'$m',''))");
}
break;
default:
throw new WireException("Unknown action '$action'");
}
return $result;
}
}

View File

@@ -365,7 +365,7 @@ class WireDateTime extends Wire {
*
* ~~~~~~
* // Output the current date/time in relative format
* echo $date->formatDate('relative');
* echo $datetime->date('relative');
* ~~~~~~
*
* @param string|int $format Use one of the following:

View File

@@ -535,7 +535,10 @@ class WireFileTools extends Wire {
// filename is absolute, make sure it's in a location we consider safe
$allowed = false;
foreach($options['allowedPaths'] as $path) {
if(strpos($filename, $path) === 0) $allowed = true;
if(strpos($filename, $path) === 0) {
$allowed = true;
break;
}
}
if(!$allowed) {
$error = "Filename $filename is not in an allowed path." ;
@@ -765,8 +768,20 @@ class WireFileTools extends Wire {
*
*/
public function compile($file, array $options = array()) {
static $compiled = array();
if(strpos($file, '/modules/')) {
// for multi-instance support, use the same compiled version
// otherwise, require_once() statements in a file may not work as intended
// applied just to site/modules for the moment, but may need to do site/templates too
$f = str_replace($this->wire('config')->paths->root, '', $file);
if(isset($compiled[$f])) return $compiled[$f];
} else {
$f = '';
}
$compiler = new FileCompiler(dirname($file), $options);
return $compiler->compile(basename($file));
$compiledFile = $compiler->compile(basename($file));
if($f) $compiled[$f] = $compiledFile;
return $compiledFile;
}
/**

View File

@@ -540,6 +540,7 @@ class WireHooks {
$hooks = $this->getHooks($object, $method);
$cancelHooks = false;
$profiler = $this->wire->wire('profiler');
foreach(array('before', 'after') as $when) {
@@ -605,6 +606,15 @@ class WireHooks {
$toObject = $hook['toObject'];
$toMethod = $hook['toMethod'];
if($profiler) {
$profilerEvent = $profiler->start($hook['id'], $this, array(
'event' => $event,
'hook' => $hook,
));
} else {
$profilerEvent = false;
}
if(is_null($toObject)) {
if(!is_callable($toMethod) && strpos($toMethod, "\\") === false && __NAMESPACE__) {
@@ -623,6 +633,8 @@ class WireHooks {
}
// @todo allow for use of $returnValue as alternative to $event->return
}
if($profilerEvent) $profiler->stop($profilerEvent);
$result['numHooksRun']++;

View File

@@ -5,7 +5,20 @@
*
* Provides capability for sending POST/GET requests to URLs
*
* #pw-summary WireHttp enables you to send HTTP requests to URLs, download files, and more.
* #pw-summary WireHttp enables you to send HTTP requests to URLs, download files, and more.
* #pw-var $http
* #pw-instantiate $http = new WireHttp();
* #pw-body =
* ~~~~~
* // Get the contents of a URL
* $response = $http->get("http://domain.com/path/");
* if($response !== false) {
* echo "Successful response: " . $sanitizer->entities($response);
* } else {
* echo "HTTP request failed: " . $http->getError();
* }
* ~~~~~
* #pw-body
*
* Thanks to @horst for his assistance with several methods in this class.
*
@@ -219,7 +232,7 @@ class WireHttp extends Wire {
* $response = $http->post("http://domain.com/path/", [
* 'foo' => bar',
* ]);
* if($response) {
* if($response !== false) {
* echo "Successful response: " . $sanitizer->entities($response);
* } else {
* echo "HTTP request failed: " . $http->getError();
@@ -244,7 +257,7 @@ class WireHttp extends Wire {
* $response = $http->get("http://domain.com/path/", [
* 'foo' => 'bar',
* ]);
* if($response) {
* if($response !== false) {
* echo "Successful response: " . $sanitizer->entities($response);
* } else {
* echo "HTTP request failed: " . $http->getError();
@@ -888,7 +901,7 @@ class WireHttp extends Wire {
* - `content-type`: {content-type} (replaced with actual content type)
* - `content-transfer-encoding`: binary
* - `content-length`: {filesize} (replaced with actual filesize)
* - To remove a header completely, make its value NULL and it won't be sent.
* - To remove a header completely, make its value NULL and it won't be sent.
* @throws WireException
*
*/

View File

@@ -38,13 +38,59 @@
*/
class WireInput extends Wire {
/**
* @var WireInputVars|null
*
*/
protected $getVars = null;
protected $postVars = null;
protected $cookieVars = null;
protected $whitelist = null;
protected $urlSegments = array();
protected $pageNum = 1;
/**
* @var WireInputVars|null
*
*/
protected $postVars = null;
/**
* @var WireInputVars|null
*
*/
protected $cookieVars = null;
/**
* @var WireInputVars|null
*
*/
protected $whitelist = null;
/**
* @var array
*
*/
protected $urlSegments = array();
/**
* @var int
*
*/
protected $pageNum = 1;
/**
* @var array
*
*/
protected $requestMethods = array(
'GET' => 'GET',
'POST' => 'POST',
'HEAD' => 'HEAD',
'PUT' => 'PUT',
'DELETE' => 'DELETE',
'OPTIONS' => 'OPTIONS',
);
/**
* Construct
*
*/
public function __construct() {
$this->useFuel(false);
$this->unregisterGLOBALS();
@@ -512,15 +558,17 @@ class WireInput extends Wire {
* Return the unsanitized query string that was part of this request, or blank if none
*
* Note that the returned query string is not sanitized, so if you use it in any output
* be sure to run it through `$sanitizer->entities()` first.
* be sure to run it through `$sanitizer->entities()` first. An optional assoc array
* param can be used to add new GET params or override existing ones.
*
* #pw-group-URLs
*
* @param array $overrides Optional assoc array for overriding or adding GET params
* @return string Returns the unsanitized query string
*
*/
public function queryString() {
return $this->get()->queryString();
public function queryString($overrides = array()) {
return $this->get()->queryString($overrides);
}
/**
@@ -537,6 +585,32 @@ class WireInput extends Wire {
return $this->wire('config')->https ? 'https' : 'http';
}
/**
* Return the current request method (i.e. GET, POST, etc.) or blank if not known
*
* Possible return values are:
* - GET
* - POST
* - HEAD
* - PUT
* - DELETE
* - OPTIONS
* - or blank if not known
*
* @return string
* @since 3.0.39
*
*/
public function requestMethod() {
if(isset($_SERVER['REQUEST_METHOD'])) {
$m = strtoupper($_SERVER['REQUEST_METHOD']);
$requestMethod = isset($this->requestMethods[$m]) ? $this->requestMethods[$m] : '';
} else {
$requestMethod = '';
}
return $requestMethod;
}
/**
* Emulate register globals OFF
*

View File

@@ -181,9 +181,15 @@ class WireInputData extends Wire implements \ArrayAccess, \IteratorAggregate, \C
public function count() {
return count($this->data);
}
public function remove($key) {
unset($this->data[$key]);
return $this;
}
public function removeAll() {
$this->data = array();
return $this;
}
public function __isset($key) {
@@ -194,8 +200,8 @@ class WireInputData extends Wire implements \ArrayAccess, \IteratorAggregate, \C
$this->offsetUnset($key);
}
public function queryString() {
return http_build_query($this->getArray());
public function queryString($overrides = array()) {
return http_build_query(array_merge($this->getArray(), $overrides));
}
/**

View File

@@ -10,6 +10,8 @@
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* https://processwire.com
*
* @method bool save($name, $text, $options = array())
*
*/
@@ -299,20 +301,23 @@ class WireLog extends Wire {
public function lineToEntry($line) {
$parts = explode("\t", $line, 4);
if(count($parts) == 2) {
$entry = array(
'date' => $parts[0],
'date' => $parts[0],
'user' => '',
'url' => '',
'text' => $parts[1]
);
} else if(count($parts) == 3) {
$user = strpos($parts[1], '/') === false ? $parts[1] : '';
$url = strpos($parts[2], '://') ? $parts[2] : '';
$text = empty($url) ? $parts[2] : '';
$entry = array(
'date' => $parts[0],
'user' => strpos($parts[1], '/') === false ? $parts[1] : '',
'url' => strpos($parts[1], '/') !== false ? $parts[1] : '',
'text' => $parts[2]
'date' => $parts[0],
'user' => $user,
'url' => $url,
'text' => $text
);
} else {
$entry = array(

View File

@@ -1,39 +1,66 @@
<?php namespace ProcessWire;
/**
* ProcessWire WireMail
*
* ProcessWire WireMail
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* https://processwire.com
*
* USAGE:
*
* $mail = new WireMail();
*
* // chained method call usage
* $mail->to('user@domain.com')->from('you@company.com')->subject('Message Subject')->body('Message Body')->send();
* #pw-summary A module type that handles sending of email in ProcessWire
* #pw-var $m
* #pw-body =
*
* Below are 3 different ways you can get a new instance of WireMail.
* When possible we recommend using option A or B below.
* ~~~~~
* $m = $mail->new(); // option A
* $m = wireMail(); // option B
* $m = new WireMail(); // option C
* ~~~~~
* Once you have an instance of WireMail (`$m`), you can use it to send email like in these examples below.
* ~~~~~
* // chained (fluent) method call usage
* $m->to('user@domain.com')
* ->from('you@company.com')
* ->subject('Message Subject')
* ->body('Message Body')
* ->send();
*
* // separate method call usage
* $mail->to('user@domain.com'); // specify CSV string or array for multiple addresses
* $mail->from('you@company.com');
* $mail->subject('Message Subject');
* $mail->body('Message Body');
* $mail->send();
* $m->to('user@domain.com'); // specify CSV string or array for multiple addresses
* $m->from('you@company.com');
* $m->subject('Message Subject');
* $m->body('Message Body');
* $m->send();
*
* // you can also set values without function calls:
* $mail->to = 'user@domain.com';
* $mail->from = 'you@company.com';
* ...and so on.
* // optionally specify “from” or “to” names as 2nd argument
* $m->to('user@domain.com', 'John Smith');
* $m->from('you@company.com', 'Mary Jane');
*
* // other methods or properties you might set (or get)
* $mail->bodyHTML('<html><body><h1>Message Body</h1></body></html>');
* $mail->header('X-Mailer', 'ProcessWire');
* $mail->param('-f you@company.com'); // PHP mail() param (envelope from example)
* $m->bodyHTML('<html><body><h1>Message Body</h1></body></html>');
* $m->attachment('/path/to/file.ext');
* $m->fromName('Mary Jane');
* $m->toName('John Smith');
* $m->header('X-Mailer', 'ProcessWire');
* $m->param('-f you@company.com'); // PHP mail() param (envelope from example)
*
* // note that the send() function always returns the quantity of messages sent
* $numSent = $mail->send();
* $numSent = $m->send();
* ~~~~~
* #pw-body
*
* @method int send()
* @property array $to
* @property array $toName
* @property string $from
* @property string $fromName
* @property string $subject
* @property string $body
* @property string $bodyHTML
* @property array $header
* @property array $param
* @property array $attachments Array of file attachments (if populated) #pw-advanced
*
*/
@@ -60,7 +87,6 @@ class WireMail extends WireData implements WireMailInterface {
$this->mail['header']['X-Mailer'] = "ProcessWire/" . $this->className();
}
public function __get($key) {
if(array_key_exists($key, $this->mail)) return $this->mail[$key];
return parent::__get($key);
@@ -119,12 +145,14 @@ class WireMail extends WireData implements WireMailInterface {
$email = $this->sanitizeEmail($email);
if(!strlen($name)) return $email;
$name = $this->sanitizeHeader($name);
$delim = '';
if(strpos($name, ',') !== false) {
// name contains a comma, so quote the value
$name = str_replace('"', '', $name); // remove existing quotes
$name = '"' . $name . '"'; // surround w/quotes
$delim = '"'; // add quotes
}
return "$name <$email>";
// Encode the name part as quoted printable according to rfc2047
return $delim . $this->quotedPrintableString($name) . $delim . " <$email>";
}
/**
@@ -134,14 +162,14 @@ class WireMail extends WireData implements WireMailInterface {
* you specify NULL as the email address, in which case it clears them all.
*
* @param string|array|null $email Specify any ONE of the following:
* 1. Single email address or "User Name <user@example.com>" string.
* 2. CSV string of #1.
* 3. Non-associative array of #1.
* 4. Associative array of (email => name)
* 5. NULL (default value, to clear out any previously set values)
* - Single email address or "User Name <user@example.com>" string.
* - CSV string of #1.
* - Non-associative array of #1.
* - Associative array of (email => name)
* - NULL (default value, to clear out any previously set values)
* @param string $name Optionally provide a TO name, applicable
* only when specifying #1 (single email) for the first argument.
* @return this
* @return $this
* @throws WireException if any provided emails were invalid
*
*/
@@ -191,8 +219,8 @@ class WireMail extends WireData implements WireMailInterface {
*
* This sets the 'to name' for whatever the last added 'to' email address was.
*
* @param string
* @return this
* @param string The 'to' name
* @return $this
* @throws WireException if you attempt to set a toName before a to email.
*
*/
@@ -207,9 +235,9 @@ class WireMail extends WireData implements WireMailInterface {
/**
* Set the email from address
*
* @param string Must be a single email address or "User Name <user@example.com>" string.
* @param string $email Must be a single email address or "User Name <user@example.com>" string.
* @param string|null An optional FROM name (same as setting/calling fromName)
* @return this
* @return $this
* @throws WireException if provided email was invalid
*
*/
@@ -226,8 +254,8 @@ class WireMail extends WireData implements WireMailInterface {
* It is preferable to do this with the from() method, but this is provided to ensure that
* all properties can be set with direct access, i.e. $mailer->fromName = 'User Name';
*
* @param string
* @return this
* @param string The 'from' name
* @return $this
*
*/
public function fromName($name) {
@@ -238,8 +266,8 @@ class WireMail extends WireData implements WireMailInterface {
/**
* Set the email subject
*
* @param string $subject
* @return this
* @param string $subject Email subject text
* @return $this
*
*/
public function subject($subject) {
@@ -249,9 +277,14 @@ class WireMail extends WireData implements WireMailInterface {
/**
* Set the email message body (text only)
*
* ~~~~~
* $m = wireMail();
* $m->body('Hello world');
* ~~~~~
*
* @param string $body in text only
* @return this
* @param string $body Email body in text only
* @return $this
*
*/
public function body($body) {
@@ -261,9 +294,16 @@ class WireMail extends WireData implements WireMailInterface {
/**
* Set the email message body (HTML only)
*
* This should be the text from an entire HTML document, not just an element.
*
* ~~~~~
* $m = wireMail();
* $m->bodyHTML('<html><body><h1>Hello world</h1></body></html>');
* ~~~~~
*
* @param string $body in HTML
* @return this
* @param string $body Email body in HTML
* @return $this
*
*/
public function bodyHTML($body) {
@@ -274,12 +314,14 @@ class WireMail extends WireData implements WireMailInterface {
/**
* Set any email header
*
* Note: multiple calls will append existing headers.
* To remove an existing header, specify NULL as the value.
* - Multiple calls will append existing headers.
* - To remove an existing header, specify NULL as the value.
*
* #pw-advanced
*
* @param string $key
* @param string $value
* @return this
* @param string $key Header name
* @param string $value Header value
* @return $this
*
*/
public function header($key, $value) {
@@ -299,13 +341,18 @@ class WireMail extends WireData implements WireMailInterface {
/**
* Set any email param
*
* See $additional_parameters at: http://www.php.net/manual/en/function.mail.php
* Note: multiple calls will append existing params.
* To remove an existing param, specify NULL as the value.
* This function may only be applicable to PHP mail().
* See `$additional_parameters` at <http://www.php.net/manual/en/function.mail.php>
*
* - Multiple calls will append existing params.
* - To remove an existing param, specify NULL as the value.
*
* This function may only be applicable if you don't have other WireMail modules
* installed as email params are only used by PHP's `mail()` function.
*
* #pw-advanced
*
* @param string $value
* @return this
* @return $this
*
*/
public function param($value) {
@@ -319,13 +366,25 @@ class WireMail extends WireData implements WireMailInterface {
/**
* Add a file to be attached to the email
*
* ~~~~~~
* $m = wireMail();
* $m->to('user@domain.com')->from('hello@world.com');
* $m->subject('Test attachment');
* $m->body('This is just a test of a file attachment');
* $m->attachment('/path/to/file.jpg');
* $m->send();
* ~~~~~~
*
* #pw-advanced
*
* Note: multiple calls will append attachments.
* To remove the supplied attachments, specify NULL as the value.
* This function may only be applicable to PHP mail().
* - Multiple calls will append attachments.
* - To remove the supplied attachments, specify NULL as the value.
* - Attachments may or may not be supported by 3rd party WireMail modules.
*
* @param string $value
* @return this
* @param string $value Full path and filename of file attachment
* @param string $filename Optional different basename for file as it appears in the mail
* @return $this
*
*/
public function attachment($value, $filename = '') {
@@ -341,14 +400,15 @@ class WireMail extends WireData implements WireMailInterface {
/**
* Send the email
*
* This is the primary method that modules extending this class would want to replace.
* Call this method only after you have specified at least the `subject`, `to` and `body`.
*
* #pw-notes This is the primary method that modules extending this class would want to replace.
*
* @return int Returns a positive number (indicating number of addresses emailed) or 0 on failure.
*
*/
public function ___send() {
$header = '';
$from = $this->from;
if(!strlen($from)) $from = $this->wire('config')->adminEmail;
if(!strlen($from)) $from = 'processwire@' . $this->wire('config')->httpHost;
@@ -363,7 +423,6 @@ class WireMail extends WireData implements WireMailInterface {
$header = trim($header);
$param = trim($param);
$body = '';
$text = $this->body;
$html = $this->bodyHTML;
@@ -376,21 +435,38 @@ class WireMail extends WireData implements WireMailInterface {
// Plain Text
$body = "This is a multi-part message in MIME format.\r\n\r\n" .
"--$boundary\r\n" .
"Content-Type: text/plain; charset=\"utf-8\"\r\n" .
"Content-Transfer-Encoding: 7bit\r\n\r\n" .
"$text\r\n\r\n";
"--$boundary\r\n";
$textbody = "Content-Type: text/plain; charset=\"utf-8\"\r\n" .
"Content-Transfer-Encoding: quoted-printable\r\n\r\n" .
quoted_printable_encode($text) . "\r\n\r\n";
// HTML
if($this->bodyHTML){
$body .= "--$boundary\r\n" .
"Content-Type: text/html; charset=\"utf-8\"\r\n" .
"Content-Transfer-Encoding: 7bit\r\n\r\n" .
"$html\r\n\r\n";
$htmlbody = "Content-Type: text/html; charset=\"utf-8\"\r\n" .
"Content-Transfer-Encoding: quoted-printable\r\n\r\n" .
quoted_printable_encode($html) . "\r\n\r\n";
if(count($this->attachments)) {
$subboundary = "==Multipart_Boundary_alt_x" . md5(time()) . "x";
$body .= "Content-Type: multipart/alternative;\r\n boundary=\"$subboundary\"\r\n\r\n" .
"--$subboundary\r\n" .
$textbody .
"--$subboundary\r\n" .
$htmlbody .
"--$subboundary--\r\n\r\n";
} else {
$body .= $textbody .
"--$boundary\r\n" .
$htmlbody;
}
} else {
$body .= $textbody;
}
// Attachments
foreach ($this->attachments as $filename => $file) {
foreach($this->attachments as $filename => $file) {
$content = file_get_contents($file);
$content = chunk_split(base64_encode($content));
@@ -404,17 +480,106 @@ class WireMail extends WireData implements WireMailInterface {
$body .= "--$boundary--\r\n";
} else {
$header .= "\r\nContent-Type: text/plain; charset=\"utf-8\"";
$body = $text;
$header .= "\r\nContent-Type: text/plain; charset=UTF-8\r\n" .
"Content-Transfer-Encoding: quoted-printable";
$body = quoted_printable_encode($text);
}
$numSent = 0;
foreach($this->to as $to) {
$toName = $this->mail['toName'][$to];
if($toName) $to = $this->bundleEmailAndName($to, $toName); // bundle to "User Name <user@example.com"
if(@mail($to, $this->subject, $body, $header, $param)) $numSent++;
$subject = $this->encodeSubject($this->subject);
if($param) {
if(@mail($to, $subject, $body, $header, $param)) $numSent++;
} else {
if(@mail($to, $subject, $body, $header)) $numSent++;
}
}
return $numSent;
}
/**
* Encode a subject, use mbstring if available
*
* #pw-advanced
*
* @param string $subject
* @return string
*
*/
public function encodeSubject($subject) {
if(extension_loaded("mbstring")) {
// Need to pass in the header name and subtract it afterwards,
// otherwise the first line would grow too long
return substr(mb_encode_mimeheader("Subject: $subject", 'UTF-8', 'Q', "\r\n"), 9);
}
$out = array();
$isFirst = true;
$n = 0;
while(strlen($subject) > 0 && ++$n < 50) {
$part = $this->findBestEncodePart($subject, 63, $isFirst);
$out[] = $this->quotedPrintableString($part);
$subject = substr($subject, strlen($part));
$isFirst = false;
}
return implode("\r\n ", $out);
}
/**
* Tries to split the passed subject at a whitespace at or before $maxlen,
* falling back to a hard substr if none was found, and returns the
* left part.
*
* Makes sure that the quoted-printable encoded part is inside the 76 characters
* header limit (66 for first line that has the header name, minus a buffer
* of 2 characters for whitespace) given in rfc2047.
*
* @param string $input The subject to encode
* @param int $maxlen Maximum length of unencoded string, defaults to 63
* @param bool $isFirst Set to true for first line to account for the header name
* @return string
*
*/
protected function findBestEncodePart($input, $maxlen = 63, $isFirst = false) {
$maxEffLen = $maxlen - ($isFirst ? 10 : 0);
if(strlen($input) <= $maxEffLen) {
$part = $input;
} else if(strpos($input, " ") === FALSE || strrpos($input, " ") === FALSE || strpos($input, " ") > $maxEffLen) {
// Force cutting of subject since there is no whitespace to break on
$part = substr($input, $maxlen - $maxEffLen);
} else {
$searchstring = substr($input, 0, $maxEffLen);
$lastpos = strrpos($searchstring, " ");
$part = substr($input, 0, $lastpos);
}
if(strlen($this->quotedPrintableString($part)) > 74 - ($isFirst ? 10 : 0)) {
return $this->findBestEncodePart($input, $maxlen - 1, $isFirst);
}
return $part;
}
/**
* Return the text quoted-printable encoded
*
* Uses short notation for charset and encoding suitable for email headers
* as laid out in rfc2047.
*
* #pw-advanced
*
* @param string $text
* @return string
*
*/
public function quotedPrintableString($text) {
return '=?utf-8?Q?' . quoted_printable_encode($text) . '?=';
}
}

View File

@@ -7,6 +7,21 @@
* https://processwire.com
*
* #pw-summary Provides an API interface to email and WireMail.
* #pw-body =
* ~~~~~
* // Simple usage example
* $message = $mail->new();
* $message->subject('Hello world')
* ->to('user@domain.com')
* ->from('you@company.com');
* ->body('Hello there big world')
* ->bodyHTML('<h2>Hello there big world</h2>');
* $numSent = $message->send();
* ~~~~~
* #pw-body
*
* @method WireMail new() Create a new WireMail() instance
* @property WireMail new Get a new WireMail() instance (same as method version)
*
*
*/
@@ -17,10 +32,10 @@ class WireMailTools extends Wire {
* Get a new WireMail instance for sending email
*
* ~~~~~
* $mailer = $mail->new();
* $mailer->to('user@domain.com')->from('you@company.com');
* $mailer->subject('Mail Subject')->body('Mail Body Text')->bodyHTML('Body HTML');
* $numSent = $mailer->send();
* $message = $mail->new();
* $message->to('user@domain.com')->from('you@company.com');
* $message->subject('Mail Subject')->body('Mail Body Text')->bodyHTML('Body HTML');
* $numSent = $message->send();
* ~~~~~
*
* @return WireMail
@@ -41,6 +56,7 @@ class WireMailTools extends Wire {
}
// if no module found, default to WireMail
if(is_null($mail)) $mail = $this->wire(new WireMail());
/** @var WireMail $mail */
// reset just in case module was not singular
$mail->to();
@@ -138,4 +154,9 @@ class WireMailTools extends Wire {
return $numSent;
}
public function __get($key) {
if($key === 'new') return $this->new();
return parent::__get($key);
}
}

View File

@@ -9,16 +9,27 @@
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* https://processwire.com
*
* @method WireArray load(WireArray $items, $selectors = null);
* @method bool save(Saveable $item);
* @method bool delete(Saveable $item);
* @method WireArray load(WireArray $items, $selectors = null)
* @method bool save(Saveable $item)
* @method bool delete(Saveable $item)
* @method WireArray find($selectors)
* @method void saveReady(Saveable $item) #pw-hooker
* @method void deleteReady(Saveable $item) #pw-hooker
* @method void cloneReady(Saveable $item, Saveable $copy) #pw-hooker
* @method array saved(Saveable $item, array $changes = array()) #pw-hooker
* @method void added(Saveable $item) #pw-hooker
* @method void deleted(Saveable $item) #pw-hooker
* @method void cloned(Saveable $item, Saveable $copy) #pw-hooker
*
*
*/
abstract class WireSaveableItems extends Wire implements \IteratorAggregate {
/**
* Return the WireArray that this DAO stores it's items in
*
* @return WireArray
*
*/
abstract public function getAll();
@@ -31,6 +42,8 @@ abstract class WireSaveableItems extends Wire implements \IteratorAggregate {
/**
* Return the name of the table that this DAO stores item records in
*
* @return string
*
*/
abstract public function getTable();
@@ -40,6 +53,8 @@ abstract class WireSaveableItems extends Wire implements \IteratorAggregate {
* Return the default name of the field that load() should sort by (default is none)
*
* This is overridden by selectors if applied during the load method
*
* @return string
*
*/
public function getSort() { return ''; }

View File

@@ -29,6 +29,7 @@ $corePreloads = array(
'FilenameArray.php',
'Paths.php',
'Config.php',
'FunctionsWireAPI.php',
'Functions.php',
'LanguageFunctions.php',
'WireShutdown.php',

View File

@@ -3,7 +3,7 @@
/**
* Class AdminThemeDefault
*
* @property string $colors
* @property string $colors Color set being used: "classic", "warm", "modern" or "futura"
*
*/

View File

@@ -110,6 +110,7 @@ class AdminThemeDefaultHelpers extends WireData {
if($page->name != 'page' || $this->wire('input')->urlSegment1) return '';
$user = $this->wire('user');
if($this->wire('user')->isGuest() || !$user->hasPermission('page-edit')) return '';
/** @var ProcessPageAdd $module */
$module = $this->wire('modules')->getModule('ProcessPageAdd', array('noInit' => true));
$data = $module->executeNavJSON(array('getArray' => true));
$items = array();
@@ -124,8 +125,8 @@ class AdminThemeDefaultHelpers extends WireData {
$out =
"<div id='head_button'>" .
"<button class='ui-button dropdown-toggle'><i class='fa fa-angle-down'></i> $label</button>" .
"<ul class='dropdown-menu shortcuts' data-at='right bottom+1'>$out</ul>" .
"<button class='ui-button pw-dropdown-toggle'><i class='fa fa-angle-down'></i> $label</button>" .
"<ul class='pw-dropdown-menu pw-dropdown-menu-rounded' data-at='right bottom+1'>$out</ul>" .
"</div>";
return $out;
@@ -143,7 +144,7 @@ class AdminThemeDefaultHelpers extends WireData {
if($this->wire('user')->isLoggedin() && $this->wire('modules')->isInstalled('SystemNotifications')) {
$systemNotifications = $this->wire('modules')->get('SystemNotifications');
if(!$systemNotifications->placement) return;
if(!$systemNotifications->placement) return '';
}
$defaults = array(
@@ -232,7 +233,9 @@ class AdminThemeDefaultHelpers extends WireData {
$info = $this->wire('modules')->getModuleInfo($p->process);
if(!empty($info['icon'])) $icon = $info['icon'];
}
if($p->page_icon) $icon = $p->page_icon; // allow for option of an admin field overriding the module 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
@@ -286,10 +289,10 @@ class AdminThemeDefaultHelpers extends WireData {
if(!$level && count($children)) {
$out .= "<a href='$p->url' id='topnav-page-$p' data-from='topnav-page-{$p->parent}' class='page-$p- dropdown-toggle'>$title</a>";
$out .= "<a href='$p->url' id='topnav-page-$p' data-from='topnav-page-{$p->parent}' class='page-$p- pw-dropdown-toggle'>$title</a>";
$my = 'left-1 top';
if(in_array($p->name, array('access', 'page', 'module'))) $my = 'left top';
$out .= "<ul class='dropdown-menu topnav' data-my='$my' data-at='left bottom'>";
$out .= "<ul class='pw-dropdown-menu topnav' data-my='$my' data-at='left bottom'>";
if($children instanceof PageArray) foreach($children as $c) {
@@ -308,7 +311,7 @@ class AdminThemeDefaultHelpers extends WireData {
$title = $this->_($c->title);
if(!$title) $title = $c->name;
$out .=
"<li><a class='has-items' data-from='topnav-page-$p' href='$c->url'>$icon$title</a>" .
"<li><a class='pw-has-items' data-from='topnav-page-$p' href='$c->url'>$icon$title</a>" .
"<ul>" . $this->renderTopNavItemArray($c, $moduleInfo['nav']) . "</ul></li>";
} else if(!empty($moduleInfo['useNavJSON'])) {
@@ -317,7 +320,7 @@ class AdminThemeDefaultHelpers extends WireData {
if(!strlen($title)) continue;
$icon = $this->getPageIcon($c);
$out .=
"<li><a class='has-items has-ajax-items' data-from='topnav-page-$p' data-json='{$c->url}navJSON/' " .
"<li><a class='pw-has-items pw-has-ajax-items' data-from='topnav-page-$p' data-json='{$c->url}navJSON/' " .
"href='$c->url'>$icon$title</a><ul></ul></li>";
} else {
@@ -360,6 +363,7 @@ class AdminThemeDefaultHelpers extends WireData {
// 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));
@@ -396,7 +400,7 @@ class AdminThemeDefaultHelpers extends WireData {
$out .= "<li><a href='{$p->url}$item[url]'>$icon$label</a></li>";
} else {
$out .=
"<li><a class='has-items has-ajax-items' data-from='topnav-page-$p' data-json='{$p->url}$item[navJSON]' " .
"<li><a class='pw-has-items pw-has-ajax-items' data-from='topnav-page-$p' data-json='{$p->url}$item[navJSON]' " .
"href='{$p->url}$item[url]'>$icon$label</a><ul></ul></li>";
}
}
@@ -452,17 +456,17 @@ class AdminThemeDefaultHelpers extends WireData {
"<i class='fa fa-fw fa-power-off'></i> " . $this->_('Logout') . "</a></li>";
}
$outMobile = "<ul id='topnav-mobile' class='dropdown-menu topnav' data-my='left top' data-at='left bottom'>$outMobile$outTools</ul>";
$outMobile = "<ul id='topnav-mobile' class='pw-dropdown-menu topnav' data-my='left top' data-at='left bottom'>$outMobile$outTools</ul>";
$out .=
"<li>" .
"<a target='_blank' id='tools-toggle' class='dropdown-toggle' href='{$config->urls->root}'>" .
"<a target='_blank' id='tools-toggle' class='pw-dropdown-toggle' href='{$config->urls->root}'>" .
"<i class='fa fa-wrench'></i></a>" .
"<ul class='dropdown-menu topnav' data-my='left top' data-at='left bottom'>" . $outTools .
"<ul class='pw-dropdown-menu topnav' data-my='left top' data-at='left bottom'>" . $outTools .
"</ul></li>";
$out .=
"<li class='collapse-topnav-menu'><a href='$admin->url' class='dropdown-toggle'>" .
"<li class='collapse-topnav-menu'><a href='$admin->url' class='pw-dropdown-toggle'>" .
"<i class='fa fa-lg fa-bars'></i></a>$outMobile</li>";
$this->wire('session')->setFor('AdminThemeDefault', 'topnav', $out);
@@ -529,9 +533,11 @@ class AdminThemeDefaultHelpers extends WireData {
*
*/
public function renderJSConfig() {
/** @var Config $config */
$config = $this->wire('config');
/** @var array $jsConfig */
$jsConfig = $config->js();
$jsConfig['debug'] = $config->debug;

View File

@@ -10,13 +10,28 @@
*
*/
/** @var Config $config */
/** @var AdminThemeDefault $adminTheme */
/** @var User $user */
/** @var Modules $modules */
/** @var Notices $notices */
/** @var Page $page */
if(!defined("PROCESSWIRE")) die();
if(!isset($content)) $content = '';
$searchForm = $user->hasPermission('page-edit') ? $modules->get('ProcessPageSearch')->renderSearchForm() : '';
$version = $adminTheme->version . 'h';
if($user->hasPermission('page-edit')) {
/** @var ProcessPageSearch $searchForm */
$searchForm = $modules->get('ProcessPageSearch');
$searchForm = $searchForm->renderSearchForm();
} else {
$searchForm = '';
}
$version = $adminTheme->version . 'i';
$config->styles->prepend($config->urls->root . "wire/templates-admin/styles/AdminTheme.css?v=$version");
$config->styles->prepend($config->urls->adminTemplates . "styles/" . ($adminTheme->colors ? "main-$adminTheme->colors" : "main-classic") . ".css?v=$version");
$config->styles->append($config->urls->root . "wire/templates-admin/styles/font-awesome/css/font-awesome.min.css?v=$version");
@@ -26,11 +41,12 @@ $config->scripts->append($config->urls->root . "wire/templates-admin/scripts/mai
$config->scripts->append($config->urls->adminTemplates . "scripts/main.$ext?v=$version");
require_once(dirname(__FILE__) . "/AdminThemeDefaultHelpers.php");
/** @var AdminThemeDefaultHelpers $helpers */
$helpers = $this->wire(new AdminThemeDefaultHelpers());
$extras = $adminTheme->getExtraMarkup();
?><!DOCTYPE html>
<html lang="<?php echo $helpers->_('en');
<html class="pw" lang="<?php echo $helpers->_('en');
/* this intentionally on a separate line */ ?>">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
@@ -57,10 +73,12 @@ $extras = $adminTheme->getExtraMarkup();
echo $extras['notices'];
?>
<div id="masthead" class="masthead ui-helper-clearfix">
<div class="container">
<div id="masthead" class="pw-masthead ui-helper-clearfix">
<div class="pw-container container">
<a id='logo' href='<?php echo $config->urls->admin?>'><img width='130' src="<?php echo $config->urls->adminTemplates?>styles/images/logo.png" alt="ProcessWire" /></a>
<a id='logo' href='<?php echo $config->urls->admin?>'>
<img width='130' src="<?php echo $config->urls->adminTemplates?>styles/images/logo.png" alt="ProcessWire" />
</a>
<?php
if($user->isLoggedin()) {
@@ -74,7 +92,7 @@ $extras = $adminTheme->getExtraMarkup();
</div><!--/#masthead-->
<div id='breadcrumbs'>
<div class='container'>
<div class='pw-container container'>
<?php
if($page->process == 'ProcessPageList' || ($page->name == 'lister' && $page->parent->name == 'page')) {
@@ -87,11 +105,13 @@ $extras = $adminTheme->getExtraMarkup();
</div>
</div><!--/#breadcrumbs-->
<div id="content" class="content fouc_fix">
<div class="container">
<div id="content" class="pw-content content pw-fouc-fix">
<div class="pw-container container">
<?php
if($page->body) echo $page->body;
$body = $page->get('body');
if($body) echo $body;
unset($body);
echo $content;
echo $extras['content'];
?>
@@ -99,8 +119,8 @@ $extras = $adminTheme->getExtraMarkup();
</div>
</div><!--/#content-->
<div id="footer" class="footer">
<div class="container">
<div id="footer" class="pw-footer footer">
<div class="pw-container container">
<p>
<?php if($user->isLoggedin()): ?>
<span id='userinfo'>
@@ -117,7 +137,9 @@ $extras = $adminTheme->getExtraMarkup();
<?php
echo $extras['footer'];
if($config->debug && $user->isSuperuser()) include($config->paths->root . '/wire/templates-admin/debug.inc');
if($config->debug && $user->isSuperuser()) {
include($config->paths->root . '/wire/templates-admin/debug.inc');
}
?>
</div>
</div><!--/#footer-->

View File

@@ -7,12 +7,15 @@
*
*/
/** @var Config $config */
/** @var ProcessWire $wire */
$config->inputfieldColumnWidthSpacing = 0; // percent spacing between columns
$markup = InputfieldWrapper::getMarkup();
$markup['item_label'] = "\n\t\t<label class='InputfieldHeader' for='{for}'>{out}</label>";
$markup['item_label_hidden'] = "\n\t\t<label class='InputfieldHeader InputfieldHeaderHidden'><span>{out}</span></label>";
$markup['item_content'] = "\n\t\t<div class='InputfieldContent'>\n{out}\n\t\t</div>";
$markup['item_label'] = "<label class='InputfieldHeader' for='{for}'>{out}</label>";
$markup['item_label_hidden'] = "<label class='InputfieldHeader InputfieldHeaderHidden'><span>{out}</span></label>";
$markup['item_content'] = "<div class='InputfieldContent'>{out}</div>";
InputfieldWrapper::setMarkup($markup);
$classes = InputfieldWrapper::getClasses();
@@ -20,10 +23,12 @@ $classes['item'] = "Inputfield {class} Inputfield_{name}";
$classes['item_error'] = "InputfieldStateError";
InputfieldWrapper::setClasses($classes);
wire()->addHookBefore('MarkupPagerNav::render', null, 'hookMarkupPagerNavRender');
$wire->addHookBefore('MarkupPagerNav::render', null, 'hookMarkupPagerNavRender');
/**
* Change the default prev/next links for MarkupPagerNav
*
* @param HookEvent $event
*
*/
function hookMarkupPagerNavRender(HookEvent $event) {
@@ -31,7 +36,7 @@ function hookMarkupPagerNavRender(HookEvent $event) {
if(!isset($options['nextItemLabel'])) {
$options['nextItemLabel'] = "<i class='fa fa-angle-right'></i>";
$options['previousItemLabel'] = "<i class='fa fa-angle-left'></i>";
$options['separatorItemLabel'] = "<span class='detail'>&hellip;</span>";
$options['separatorItemLabel'] = "<span class='pw-detail'>&hellip;</span>";
$event->arguments(1, $options);
}
}

View File

@@ -4,8 +4,8 @@
</div>
</div><!--/#content-->
<div id="footer" class="footer">
<div class="container">
<div id="footer" class="pw-footer">
<div class="pw-container">
<p>ProcessWire <?php echo PROCESSWIRE_INSTALL; ?> &copy; 2016</p>
</div>
</div><!--/#footer-->

View File

@@ -3,7 +3,7 @@ if(!defined("PROCESSWIRE_INSTALL")) die();
if(!isset($title)) $title = 'Title';
if(!isset($formAction)) $formAction = './install.php';
?><!DOCTYPE html>
<html lang="en">
<html class="pw" lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta name="robots" content="noindex, nofollow" />
@@ -23,27 +23,23 @@ if(!isset($formAction)) $formAction = './install.php';
</head>
<body>
<div id="masthead" class="masthead ui-helper-clearfix">
<div class="container">
<div id="masthead" class="pw-masthead ui-helper-clearfix">
<div class="pw-container">
<a id='logo' target='_blank' href='http://processwire.com'><img width='130' src="wire/modules/AdminTheme/AdminThemeDefault/styles/images/logo.png" alt="ProcessWire" /></a>
</div>
</div><!--/#masthead-->
<div id='breadcrumbs'>
<div class='container'>
<div class='pw-container'>
<ul class='nav'>
<li class='title'><?php echo htmlentities($title, ENT_QUOTES, "UTF-8"); ?></li>
<li class='support'><a target='_blank' href='https://processwire.com/talk/'><i class='fa fa-comments'></i> Need help?</a></li>
</ul>
</div>
</div><!--/#breadcrumbs-->
<div id="content" class="content">
<div class="container">
<div id="content" class="pw-content">
<div class="pw-container">
<?php if($formAction): ?>
<form action='<?php echo htmlentities($formAction, ENT_QUOTES, "UTF-8"); ?>' method='post'>
<?php endif; ?>

View File

@@ -13,7 +13,7 @@ var ProcessWireAdminTheme = {
*/
init: function() {
// fix annoying fouc with this particular button
var $button = $("#head_button > button.dropdown-toggle").hide();
var $button = $("#head_button > button.pw-dropdown-toggle").hide();
this.setupCloneButton();
ProcessWireAdmin.init();
@@ -22,7 +22,7 @@ var ProcessWireAdminTheme = {
var $body = $("body");
if($body.hasClass('hasWireTabs') && $("ul.WireTabs").length == 0) $body.removeClass('hasWireTabs');
$('#content').removeClass('fouc_fix'); // FOUC fix, deprecated
$('#content').removeClass('pw-fouc-fix'); // FOUC fix, deprecated
$body.removeClass("pw-init").addClass("pw-ready");
if($button.length > 0) $button.show();
@@ -41,13 +41,13 @@ var ProcessWireAdminTheme = {
// or buttons in the format button.head_button_clone with an ID attribute.
// var $buttons = $("#content a[id=''] button[id=''], #content button.head_button_clone[id!='']");
// var $buttons = $("#content a:not([id]) button:not([id]), #content button.head_button_clone[id!=]");
var $buttons = $("button.head_button_clone, button.head-button");
var $buttons = $("button.pw-head-button, button.head_button_clone"); // head_button_clone is legacy
// don't continue if no buttons here or if we're in IE
if($buttons.length == 0) return; // || $.browser.msie) return;
var $head = $("#head_button");
if($head.length == 0) $head = $("<div id='head_button'></div>").prependTo("#breadcrumbs .container");
if($head.length == 0) $head = $("<div id='head_button'></div>").prependTo("#breadcrumbs .pw-container");
$buttons.each(function() {
var $t = $(this);
@@ -56,7 +56,7 @@ var ProcessWireAdminTheme = {
if($a.length > 0) {
$button = $t.parent('a').clone(true);
$head.prepend($button);
} else if($t.hasClass('head_button_clone') || $t.hasClass('head-button')) {
} else if($t.hasClass('pw-head-button') || $t.hasClass('head_button_clone')) {
$button = $t.clone(true);
$button.attr('data-from_id', $t.attr('id')).attr('id', $t.attr('id') + '_copy');
//$a = $("<a></a>").attr('href', '#');
@@ -67,7 +67,6 @@ var ProcessWireAdminTheme = {
//$head.prepend($a.append($button));
$head.prepend($button);
}
// if($button.hasClass('dropdown-toggle') && $button.attr('data-dropdown')) { }
});
$head.show();
},

View File

@@ -1 +1 @@
var ProcessWireAdminTheme={init:function(){var b=$("#head_button > button.dropdown-toggle").hide();this.setupCloneButton();ProcessWireAdmin.init();this.setupSearch();this.setupMobile();var a=$("body");if(a.hasClass("hasWireTabs")&&$("ul.WireTabs").length==0){a.removeClass("hasWireTabs")}$("#content").removeClass("fouc_fix");a.removeClass("pw-init").addClass("pw-ready");if(b.length>0){b.show()}},setupCloneButton:function(){if($("body").is(".modal")){return}var b=$("button.head_button_clone, button.head-button");if(b.length==0){return}var a=$("#head_button");if(a.length==0){a=$("<div id='head_button'></div>").prependTo("#breadcrumbs .container")}b.each(function(){var e=$(this);var d=e.parent("a");var c;if(d.length>0){c=e.parent("a").clone(true);a.prepend(c)}else{if(e.hasClass("head_button_clone")||e.hasClass("head-button")){c=e.clone(true);c.attr("data-from_id",e.attr("id")).attr("id",e.attr("id")+"_copy");c.click(function(){$("#"+$(this).attr("data-from_id")).click();return false});a.prepend(c)}}});a.show()},setupSearch:function(){$.widget("custom.adminsearchautocomplete",$.ui.autocomplete,{_renderMenu:function(e,c){var f=this;var d="";$.each(c,function(g,h){if(h.type!=d){e.append("<li class='ui-widget-header'><a>"+h.type+"</a></li>");d=h.type}e.attr("id","ProcessPageSearchAutocomplete");f._renderItemData(e,h)})},_renderItemData:function(c,d){if(d.label==d.template){d.template=""}c.append("<li><a href='"+d.edit_url+"'>"+d.label+" <small>"+d.template+"</small></a></li>")}});var b=$("#ProcessPageSearchQuery");var a=$("#ProcessPageSearchStatus");b.adminsearchautocomplete({minLength:2,position:{my:"right top",at:"right bottom"},search:function(c,d){a.html("<img src='"+ProcessWire.config.urls.modules+"Process/ProcessPageList/images/loading.gif'>")},open:function(c,d){$("#topnav").hide()},close:function(c,d){$("#topnav").show()},source:function(e,c){var d=b.parents("form").attr("data-action")+"for?get=template_label,title&include=all&admin_search="+e.term;$.getJSON(d,function(g){var f=g.matches.length;if(f<g.total){a.text(g.matches.length+"/"+g.total)}else{a.text(f)}c($.map(g.matches,function(h){return{label:h.title,value:h.title,page_id:h.id,template:h.template_label?h.template_label:"",edit_url:h.editUrl,type:h.type}}))})},select:function(c,d){}}).focus(function(){$(this).siblings("label").find("i").hide()}).blur(function(){a.text("");$(this).siblings("label").find("i").show()})},setupMobile:function(){var a=0;var c=0;var b=function(){var h=$("#topnav");var g=$("body");var e=h.height();if(e>50){if(!g.hasClass("collapse-topnav")){g.addClass("collapse-topnav");a=g.width()}}else{if(a>0){var f=g.width();if(g.hasClass("collapse-topnav")&&f>a){g.removeClass("collapse-topnav");a=0}}}h.children(".collapse-topnav-menu").children("a").click(function(){if($(this).is(".hover")){$(this).mouseleave()}else{$(this).mouseenter()}return false});var d=$(".WireTabs");if(d.length<1){return}d.each(function(){var j=$(this);var i=j.height();if(i>65){if(!g.hasClass("collapse-wiretabs")){g.addClass("collapse-wiretabs");c=g.width()}}else{if(c>0){var k=g.width();if(g.hasClass("collapse-wiretabs")&&k>c){g.removeClass("collapse-wiretabs");c=0}}}})};b();$(window).resize(b)}};$(document).ready(function(){ProcessWireAdminTheme.init();$("a.notice-remove","#notices").click(function(){$("#notices").slideUp("fast",function(){$(this).remove()});return false})});
var ProcessWireAdminTheme={init:function(){var b=$("#head_button > button.pw-dropdown-toggle").hide();this.setupCloneButton();ProcessWireAdmin.init();this.setupSearch();this.setupMobile();var a=$("body");if(a.hasClass("hasWireTabs")&&$("ul.WireTabs").length==0){a.removeClass("hasWireTabs")}$("#content").removeClass("pw-fouc-fix");a.removeClass("pw-init").addClass("pw-ready");if(b.length>0){b.show()}},setupCloneButton:function(){if($("body").is(".modal")){return}var b=$("button.pw-head-button, button.head_button_clone");if(b.length==0){return}var a=$("#head_button");if(a.length==0){a=$("<div id='head_button'></div>").prependTo("#breadcrumbs .pw-container")}b.each(function(){var e=$(this);var d=e.parent("a");var c;if(d.length>0){c=e.parent("a").clone(true);a.prepend(c)}else{if(e.hasClass("pw-head-button")||e.hasClass("head_button_clone")){c=e.clone(true);c.attr("data-from_id",e.attr("id")).attr("id",e.attr("id")+"_copy");c.click(function(){$("#"+$(this).attr("data-from_id")).click();return false});a.prepend(c)}}});a.show()},setupSearch:function(){$.widget("custom.adminsearchautocomplete",$.ui.autocomplete,{_renderMenu:function(e,c){var f=this;var d="";$.each(c,function(g,h){if(h.type!=d){e.append("<li class='ui-widget-header'><a>"+h.type+"</a></li>");d=h.type}e.attr("id","ProcessPageSearchAutocomplete");f._renderItemData(e,h)})},_renderItemData:function(c,d){if(d.label==d.template){d.template=""}c.append("<li><a href='"+d.edit_url+"'>"+d.label+" <small>"+d.template+"</small></a></li>")}});var b=$("#ProcessPageSearchQuery");var a=$("#ProcessPageSearchStatus");b.adminsearchautocomplete({minLength:2,position:{my:"right top",at:"right bottom"},search:function(c,d){a.html("<img src='"+ProcessWire.config.urls.modules+"Process/ProcessPageList/images/loading.gif'>")},open:function(c,d){$("#topnav").hide()},close:function(c,d){$("#topnav").show()},source:function(e,c){var d=b.parents("form").attr("data-action")+"for?get=template_label,title&include=all&admin_search="+e.term;$.getJSON(d,function(g){var f=g.matches.length;if(f<g.total){a.text(g.matches.length+"/"+g.total)}else{a.text(f)}c($.map(g.matches,function(h){return{label:h.title,value:h.title,page_id:h.id,template:h.template_label?h.template_label:"",edit_url:h.editUrl,type:h.type}}))})},select:function(c,d){}}).focus(function(){$(this).siblings("label").find("i").hide()}).blur(function(){a.text("");$(this).siblings("label").find("i").show()})},setupMobile:function(){var a=0;var c=0;var b=function(){var h=$("#topnav");var g=$("body");var e=h.height();if(e>50){if(!g.hasClass("collapse-topnav")){g.addClass("collapse-topnav");a=g.width()}}else{if(a>0){var f=g.width();if(g.hasClass("collapse-topnav")&&f>a){g.removeClass("collapse-topnav");a=0}}}h.children(".collapse-topnav-menu").children("a").click(function(){if($(this).is(".hover")){$(this).mouseleave()}else{$(this).mouseenter()}return false});var d=$(".WireTabs");if(d.length<1){return}d.each(function(){var j=$(this);var i=j.height();if(i>65){if(!g.hasClass("collapse-wiretabs")){g.addClass("collapse-wiretabs");c=g.width()}}else{if(c>0){var k=g.width();if(g.hasClass("collapse-wiretabs")&&k>c){g.removeClass("collapse-wiretabs");c=0}}}})};b();$(window).resize(b)}};$(document).ready(function(){ProcessWireAdminTheme.init();$("a.notice-remove","#notices").click(function(){$("#notices").slideUp("fast",function(){$(this).remove()});return false})});

View File

@@ -23,6 +23,7 @@
float: left;
}
#content label .pw-detail,
#content label .detail {
font-weight: normal;
}
@@ -47,7 +48,8 @@
margin-top: 1em;
}
.container {
.container,
.pw-container {
max-width: 1024px;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -504,6 +504,7 @@
$ui-tabs-container-bg: transparentize($subtle-bg, 0.3); /* note same as item nested item list header */
.Inputfields .langTabsContainer,
#content .Inputfields .langTabsContainer {
padding-top: 0;
padding-left: 0;
@@ -595,7 +596,8 @@ $ui-tabs-container-bg: transparentize($subtle-bg, 0.3); /* note same as item nes
margin-bottom: 0;
}
#content.fouc_fix .Inputfields .InputfieldHeader {
.pw-fouc-fix .Inputfields .InputfieldHeader,
#content.pw-fouc-fix .Inputfields .InputfieldHeader {
// don't show right-aligned header icons when page is rendering
.toggle-icon,
.langTabsToggle {

View File

@@ -87,7 +87,7 @@
}
ul.dropdown-menu {
ul.pw-dropdown-menu {
display: none;
}
@@ -223,16 +223,16 @@
display: none;
}
.has-items-icon {
.pw-has-items-icon {
float: right;
position: relative;
top: 4px;
}
.pw-button-dropdown,
.dropdown-menu-rounded,
.dropdown-menu-rounded ul,
.dropdown-menu.shortcuts {
.pw-dropdown-menu-rounded,
.pw-dropdown-menu-rounded ul,
.pw-dropdown-menu.pw-dropdown-shortcuts {
border-radius: 5px !important;
li:first-child > a {

View File

@@ -3,7 +3,7 @@
*
*/
.content {
.pw-content {
/**
* Actions: like the "|edit|view|new" in the PageList

View File

@@ -1,4 +1,4 @@
.content {
.pw-content {
/**
* Pagination

View File

@@ -1,4 +1,4 @@
.content {
.pw-content {
table th,
table th.header,
@@ -18,7 +18,7 @@
.AdminDataList {
margin-top: 0;
.container > & {
.pw-container > & {
margin-top: 1em;
}

View File

@@ -26,7 +26,8 @@
*
*/
.content {
.content,
.pw-content {
.ui-widget-content a {
color: $link-color;
}
@@ -67,8 +68,8 @@
color: $reverse-text-color;
}
.ui-accordion-content,
.content .InputfieldForm .ui-accordion-content,
.content .InputfieldForm .InputfieldMarkup .ui-accordion-content {
.pw-content .InputfieldForm .ui-accordion-content,
.pw-content .InputfieldForm .InputfieldMarkup .ui-accordion-content {
margin-top: 0;
padding-top: 0;
}
@@ -415,7 +416,7 @@ body.hidpi-device .ui-slider .ui-slider-handle {
}
}
.dropdown-menu {
.pw-dropdown-menu {
/* dropdown menu for admin theme */
display: none;
border-radius: 0 !important;

View File

@@ -4,6 +4,7 @@
*
*/
.WireTabs,
#content .WireTabs {
position: relative;
top: (-1 * $tabs-height);

View File

@@ -192,7 +192,7 @@ $no-ghost-error-icon-color: $no-ghost-error-color;
*
*/
.dropdown-menu a.on {
.pw-dropdown-menu a.on {
color: $text-color;
}

View File

@@ -48,12 +48,14 @@ body {
font-family: $regular-font;
}
.container,
.pw-container,
.container,
.ui-dialog {
line-height: $base-line-height;
font-size: $base-font-size;
}
.pw-container,
.container {
position: relative;
width: 95%;
@@ -94,51 +96,30 @@ code {
font-size: 14px;
}
.description {
.pw-description, .description {
color: $medium-text-color;
margin: 0.5em 0;
@include word-wrap();
}
.notes,
.detail {
.pw-notes, .notes,
.pw-detail, .detail {
color: $medium-text-color;
font-size: 0.875em;
line-height: 1.5em;
}
.notes {
.pw-notes, .notes {
background: $notes-bg;
@include word-wrap();
}
.description strong,
.notes strong {
.pw-description strong, .description strong,
.pw-notes strong .notes strong {
color: $medium-text-color;
font-weight: $bold-weight;
}
.align_left,
.align-left {
float: left;
margin: 0 1em 0.5em 0;
}
.align_right,
.align-right {
float: right;
margin: 0 0 0.5em 1em;
}
.align_center,
.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
.error {
.pw-error, .error {
background: $error-bg;
color: $error-color;
font-weight: $bold-weight;
@@ -153,15 +134,6 @@ code {
z-index: 9999;
}
.pw-no-select {
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-o-user-select: none;
user-select: none; /* prevent selection of this element */
}
/***********************************************************************************************
* CONTENT
*
@@ -175,7 +147,7 @@ code {
z-index: 0;
background: $text-bg;
.container {
.pw-container, .container {
min-height: 50px;
> form,
@@ -186,7 +158,7 @@ code {
}
}
.content {
.pw-content, .content {
/* any content area that you want to inherit these general use styles */
img {
@@ -254,7 +226,7 @@ code {
dl {
margin: 1em 0;
}
.container > dl:first-child {
.pw-container > dl:first-child {
margin-top: 0;
}
dt,
@@ -309,7 +281,7 @@ code {
#debug {
// margin-top: 2em;
.container {
.pw-container {
width: 100%;
}
@@ -359,7 +331,7 @@ code {
body.modal {
.container {
.pw-container, .container {
margin: 0;
width: 100%;
min-width: 300px;
@@ -377,7 +349,7 @@ body.modal {
}
}
.content h2 {
.pw-content h2 {
margin: 1em 0 0.5em 0;
font-size: 1.3em;
}
@@ -412,7 +384,7 @@ body.modal-inline {
#content {
padding-bottom: 0;
}
.container {
.pw-container, .container {
padding-left: 0;
padding-right: 0;
}
@@ -435,21 +407,21 @@ body.id-23 #content form {
margin-top: 1em;
}
#content .container > form,
#content .container > .ui-helper-clearfix > form {
#content .pw-container > form,
#content .pw-container > .ui-helper-clearfix > form {
/* for wiretabs and/or forms to line up with headline area */
top: $form-top-margin-adjustment;
}
body:not(.hasWireTabs) #headline .container {
body:not(.hasWireTabs) #headline .pw-container {
min-height: 40px;
}
.content > .container > #PageListContainer {
.pw-content > .pw-container > #PageListContainer {
margin-top: 0;
}
.hasWireTabs .content #fieldgroupContext {
.hasWireTabs .pw-content #fieldgroupContext {
/* context selection in field editor */
top: -2.25em;
right: 0;
@@ -558,7 +530,7 @@ body:not(.hasWireTabs) #headline .container {
display: block;
}
.container {
.pw-container, .container {
width: 100%;
}
#footer #userinfo {

View File

@@ -124,11 +124,11 @@ class AdminThemeRenoHelpers extends AdminThemeDefaultHelpers {
"class" => "superuser",
"label" => "<i class='fa fa-bolt'></i>",
"children" => array(
"<i class='fa fa-life-ring'></i> " . $this->_('Support Forums') => array('http://processwire.com/talk/', 'target="_blank"'),
"<i class='fa fa-life-ring'></i> " . $this->_('Support Forums') => array('https://processwire.com/talk/', 'target="_blank"'),
"<i class='fa fa-book'></i> " . $this->_('Documentation') => array('https://processwire.com/docs/', 'target="_blank"'),
"<i class='fa fa-github'></i> " . $this->_('Github Repo') => array('https://github.com/ryancramerdesign/ProcessWire/', 'target="_blank"'),
"<i class='fa fa-github'></i> " . $this->_('Github Repo') => array('https://github.com/processwire/processwire/', 'target="_blank"'),
"<i class='fa fa-code'></i> " . $this->_('Cheatsheet') => array('http://cheatsheet.processwire.com', 'target="_blank"'),
"<i class='fa fa-anchor'></i> " . $this->_('Captain Hook') => array('http://processwire.com/api/hooks/captain-hook/', 'target="_blank"'),
"<i class='fa fa-anchor'></i> " . $this->_('Hooks') => array('https://processwire.com/api/hooks/', 'target="_blank"'),
)
);
}
@@ -181,7 +181,7 @@ class AdminThemeRenoHelpers extends AdminThemeDefaultHelpers {
}
// Add dropdown class if there are children
if($children) $class .= " dropdown";
if($children) $class .= " pw-dropdown";
$out .= "<li class='$class'><a href='$link' $attrs>$label</a>";
if (is_array($children)){

View File

@@ -12,10 +12,15 @@
*
*/
/** @var Config $config */
/** @var AdminThemeReno $adminTheme */
/** @var User $user */
/** @var Modules $modules */
if(!defined("PROCESSWIRE")) die();
if(!isset($content)) $content = '';
$version = $adminTheme->version . 'j';
$version = $adminTheme->version . 'k';
$ext = $config->debug ? "js" : "min.js";
// Search form
@@ -29,6 +34,7 @@ $colorFile = file_exists($config->paths->adminTemplates . $defaultColors) ? $con
// Styles
$config->styles->prepend($colorFile . "?v=" . $version);
$config->styles->prepend($config->urls->root . "wire/templates-admin/styles/AdminTheme.css?v=$version");
$config->styles->append($config->urls->root . "wire/templates-admin/styles/font-awesome/css/font-awesome.min.css?v=$version");
// Scripts
@@ -42,7 +48,7 @@ $extras = $adminTheme->getExtraMarkup();
?>
<!DOCTYPE html>
<html class="<?php echo $helpers->renderBodyClass(); ?>" lang="<?php echo $helpers->_('en');
<html class="pw <?php echo $helpers->renderBodyClass(); ?>" lang="<?php echo $helpers->_('en');
/* this intentionally on a separate line */ ?>">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
@@ -72,7 +78,7 @@ $extras = $adminTheme->getExtraMarkup();
<a href="#" class='main-nav-toggle'><i class="fa fa-bars"></i></a>
<div id="masthead" class="masthead ui-helper-clearfix">
<div id="masthead" class="pw-masthead masthead ui-helper-clearfix">
<?php echo $extras['masthead']; ?>
@@ -107,7 +113,7 @@ $extras = $adminTheme->getExtraMarkup();
<h1 id="title"><?php echo $helpers->getHeadline(); ?></h1>
</div>
<div id="content" class="content fouc_fix">
<div id="content" class="pw-content content pw-fouc-fix">
<?php
if($page->body) echo $page->body;
@@ -117,7 +123,7 @@ $extras = $adminTheme->getExtraMarkup();
</div>
<div id="footer" class="footer">
<div id="footer" class="pw-footer footer">
<p>
<?php if(!$user->isGuest()): ?>
<span id="userinfo">

View File

@@ -24,7 +24,7 @@ var ProcessWireAdminTheme = {
var $body = $("body");
var $html = $("html");
if($body.hasClass('hasWireTabs') && $("ul.WireTabs").length == 0) $body.removeClass('hasWireTabs');
$('#content').removeClass('fouc_fix'); // FOUC fix, deprecated
$('#content').removeClass('pw-fouc-fix'); // FOUC fix, deprecated
$body.removeClass('pw-init').addClass('pw-ready');
$html.removeClass('pw-init').addClass('pw-ready');
// this.browserCheck();
@@ -258,7 +258,7 @@ var ProcessWireAdminTheme = {
// if there are buttons in the format "a button" without ID attributes, copy them into the masthead
// or buttons in the format button.head_button_clone with an ID attribute.
// var $buttons = $("#content a[id=''] button[id=''], #content button.head_button_clone[id!='']");
var $buttons = $("button.head_button_clone, button.head_button, button.head-button");
var $buttons = $("button.pw-head-button, button.head_button_clone");
// don't continue if no buttons here or if we're in IE
if($buttons.length == 0 || $.browser.msie) return;
@@ -273,7 +273,7 @@ var ProcessWireAdminTheme = {
//$head.prepend($button);
$head.append($button);
// } else if($t.is('.head_button_clone')) {
} else if($t.hasClass('head_button_clone') || $t.hasClass('head-button')) {
} else if($t.hasClass('head_button_clone') || $t.hasClass('pw-head-button')) {
$button = $t.clone();
$button.attr('data-from_id', $t.attr('id')).attr('id', $t.attr('id') + '_copy');
//$a = $("<a></a>").attr('href', '#');
@@ -425,83 +425,20 @@ var ProcessWireAdminTheme = {
setupDropdowns: function() {
$('#masthead li.dropdown > a').on('click', function(e){
$('#masthead li.pw-dropdown > a').on('click', function(e){
$(this).next("ul").toggleClass('open');
$(this).parent().siblings().find('ul.open').removeClass('open');
return false;
});
$('#masthead li.dropdown > ul li a').on('click', function(e){
$('#masthead li.pw-dropdown > ul li a').on('click', function(e){
e.stopPropagation();
});
$(document).on('click', function(){
$('#masthead li.dropdown ul').removeClass('open');
$('#masthead li.pw-dropdown ul').removeClass('open');
});
/*
$("ul.dropdown-menu").each(function() {
var $ul = $(this).hide();
var $a = $ul.siblings(".dropdown-toggle");
if($a.is("button")) {
$a.button();
} else {
$ul.css({ 'border-top-right-radius': 0 });
}
// hide nav when an item is selected to avoid the whole nav getting selected
$ul.find('a').click(function() {
$ul.hide();
return true;
});
$ul.find(".has-items").each(function() {
var $icon = $("<i class='has-items-icon fa fa-angle-right ui-priority-secondary'></i>");
$(this).prepend($icon);
});
var lastOffset = null;
$a.mouseenter(function() {
var offset = $a.offset();
if(lastOffset != null) {
if(offset.top != lastOffset.top || offset.left != lastOffset.left) {
// dropdown-toggle has moved, destroy and re-create
$ul.menu('destroy').removeClass('dropdown-ready');
}
}
if(!$ul.hasClass('dropdown-ready')) {
$ul.css('position', 'absolute');
$ul.prependTo($('body')).addClass('dropdown-ready').menu();
var position = { my: 'right top', at: 'right bottom', of: $a };
var my = $ul.attr('data-my');
var at = $ul.attr('data-at');
if(my) position.my = my;
if(at) position.at = at;
$ul.position(position).css('z-index', 200);
}
$a.addClass('hover');
$ul.show();
lastOffset = offset;
}).mouseleave(function() {
setTimeout(function() {
if($ul.is(":hover")) return;
$ul.find('ul').hide();
$ul.hide();
$a.removeClass('hover');
}, 50);
});
$ul.mouseleave(function() {
if($a.is(":hover")) return;
$ul.hide();
$a.removeClass('hover');
});
});
*/
},
setupSideBarToggle: function() {
@@ -569,7 +506,7 @@ var ProcessWireAdminTheme = {
*/
browserCheck: function() {
if($.browser.msie && $.browser.version < 8)
$("#content .container").html("<h2>ProcessWire does not support IE7 and below at this time. Please try again with a newer browser.</h2>").show();
$("#content .pw-container").html("<h2>ProcessWire does not support IE7 and below at this time. Please try again with a newer browser.</h2>").show();
}
};

File diff suppressed because one or more lines are too long

View File

@@ -97,7 +97,7 @@ body,
font-size: $base-font-size;
}
.container {
.pw-container {
position: relative;
width: 100%;
max-width: $container-max-width;
@@ -278,11 +278,11 @@ body.id-23 {
padding: 1.8em 1.5em 3.5em 1.5em;
width: 100%;
}
#content .container {
#content .pw-container {
min-height: 50px;
}
#content .container > form,
#content .container > .ui-helper-clearfix > form {
#content .pw-container > form,
#content .pw-container > .ui-helper-clearfix > form {
position: relative;
top: -1px;
}
@@ -343,7 +343,7 @@ ul.action-group{
}
}
.content {
.pw-content {
img {
display: block;
@@ -371,7 +371,7 @@ ul.action-group{
font-size: 0.8em;
}
.container > table.AdminDataList {
.pw-container > table.AdminDataList {
margin-top: 1em;
}
@@ -513,7 +513,7 @@ ul.action-group{
dl {
margin: 1em 0;
}
.container > dl:first-child {
.pw-container > dl:first-child {
margin-top: 0;
}
dt,
@@ -757,7 +757,7 @@ ul.action-group{
border-right-color: transparent;
border-bottom-color: transparent;
border-left-color: transparent;
border-width: 1px 1px 2px 1px;
border-width: 1px 1px 0 1px;
border-style: solid;
border-radius: 3px 3px 0 0;
color: $link;
@@ -951,7 +951,7 @@ body.modal-inline #ProcessListerResults {
#debug {
.container {
.pw-container {
width: 100%;
}
@@ -1000,7 +1000,7 @@ html.modal, body.modal {
body.modal {
.container {
.pw-container {
margin: 0;
width: 100%;
min-width: 300px;
@@ -1017,7 +1017,7 @@ body.modal {
padding: 0 20px 20px 20px;
}
.content h2 {
.pw-content h2 {
margin: 1em 0;
font-size: 1.2em;
color: $medium-text-color;
@@ -1042,7 +1042,7 @@ body.modal {
display: none;
}
& .content #fieldgroupContext {
& .pw-content #fieldgroupContext {
/* context selection in field editor */
top: 0 ;
right: 1.5em;
@@ -1055,7 +1055,7 @@ body.modal-inline {
#content {
padding: 0;
}
.container {
.pw-container {
padding-left: 0;
padding-right: 0;
}
@@ -1074,21 +1074,21 @@ body.id-8:not(.hasUrlSegments) #breadcrumbs ul li:not(:first-child):not(:last-ch
display: none;
}
#content .container > form,
#content .container > .ui-helper-clearfix > form {
#content .pw-container > form,
#content .pw-container > .ui-helper-clearfix > form {
/* for wiretabs and/or forms to line up with headline area */
top: $form-top-margin-adjustment;
}
body:not(.hasWireTabs) #headline .container {
body:not(.hasWireTabs) #headline .pw-container {
min-height: 40px;
}
.content > .container > #PageListContainer {
.pw-content > .pw-container > #PageListContainer {
margin-top: 0;
}
.content #fieldgroupContext {
.pw-content #fieldgroupContext {
/* context selection in field editor */
top: 1.5em;
right: 1.5em;
@@ -1195,7 +1195,7 @@ body.pw-init .toggle-icon {
display: inline-block;
}
#masthead #topnav li.dropdown:not(.superuser) > a:after{
#masthead #topnav li.pw-dropdown:not(.superuser) > a:after{
margin-left: 3px;
}
@@ -1220,7 +1220,7 @@ body.pw-init .toggle-icon {
margin-left: 0 !important;
}
.container {
.pw-container {
width: 100%;
}
#footer #userinfo {

View File

@@ -94,7 +94,7 @@
padding-left: 0;
}
.container {
.pw-container {
line-height: 1;
}
@@ -165,7 +165,7 @@
}
& li.dropdown{
& li.pw-dropdown{
position: relative;
&:not(.superuser) > a:after{
@@ -287,7 +287,7 @@
*
*/
.dropdown-menu {
.pw-dropdown-menu {
display: none;
position: relative;
z-index: 100;
@@ -303,7 +303,7 @@
display: none;
}
.has-items-icon {
.pw-has-items-icon {
float: right;
position: relative;
right: 0.5em;
@@ -311,8 +311,8 @@
}
// add-new dropdown
&.dropdown-menu-rounded,
&.shortcuts {
&.pw-dropdown-menu-rounded,
&.pw-dropdown-shortcuts {
min-width: 200px;
padding: 0.3em 0 0.3em 0;
background: $dropdown-bg;

View File

@@ -73,7 +73,7 @@
border: none;
border-bottom: 1px solid $no-border-color;
}
.container {
.pw-container {
margin-left: 0.5em;
margin-right: 1em;
max-width: 100%;

View File

@@ -1,6 +1,6 @@
// When PW 2.5 support no longer needed, please replace this file with _pagination-2.6.scss
body .content{
body .pw-content{
.MarkupPagerNav {
list-style: none;
margin: 0;

View File

@@ -43,7 +43,8 @@
*
*/
.content {
.content,
.pw-content {
.ui-widget-content a {
color: $link-color;
@@ -88,8 +89,10 @@
}
}
.ui-accordion-content,
.content .InputfieldForm .ui-accordion-content,
.content .InputfieldForm .InputfieldMarkup .ui-accordion-content {
.pw-content .InputfieldForm .ui-accordion-content,
.pw-content .InputfieldForm .InputfieldMarkup .ui-accordion-content,
.content .InputfieldForm .ui-accordion-content, // TBD
.content .InputfieldForm .InputfieldMarkup .ui-accordion-content { // TBD
margin-top: 0;
padding-top: 0;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -15,6 +15,12 @@
class FieldtypeCache extends Fieldtype {
/**
* Get module information
*
* @return array
*
*/
public static function getModuleInfo() {
return array(
'title' => 'Cache',
@@ -99,6 +105,13 @@ class FieldtypeCache extends Fieldtype {
return parent::___savePageField($page, $field);
}
/**
* Get number of pages in the cache
*
* @param Field $field FieldtypeCache field to check
* @return int Number of cached pages
*
*/
public function getNumPagesCached(Field $field) {
$database = $this->wire('database');
$table = $database->escapeTable($field->getTable());
@@ -113,6 +126,13 @@ class FieldtypeCache extends Fieldtype {
return $num;
}
/**
* Regenerate the cache for the given Field
*
* @param Field $field Field of type FieldtypeCache
* @return int Number of pages that were cached
*
*/
protected function regenerateCache(Field $field) {
$numPages = 0;

View File

@@ -108,7 +108,11 @@ class CommentForm extends Wire implements CommentFormInterface {
// the name of a field that must be set (and have any non-blank value), typically set in Javascript to keep out spammers
// to use it, YOU must set this with a <input hidden> field from your own javascript, somewhere in the form
'requireSecurityField' => '',
'requireSecurityField' => '',
// the name of a field that must NOT be set
// creates an input field that a (human) visitor should ignore, maybe hiding it with css is a good idea
'requireHoneypotField' => '',
// should a redirect be performed immediately after a comment is successfully posted?
'redirectAfterPost' => null, // null=unset (must be set to true to enable)
@@ -254,7 +258,10 @@ class CommentForm extends Wire implements CommentFormInterface {
$attrs = $options['attrs'];
$id = $attrs['id'];
$submitKey = $id . "_submit";
$honeypot = $options['requireHoneypotField'];
$inputValues = array('cite' => '', 'email' => '', 'website' => '', 'stars' => '', 'text' => '', 'notify' => '');
if($honeypot) $inputValues[$honeypot] = '';
$user = $this->wire('user');
if($user->isLoggedin()) {
@@ -366,6 +373,18 @@ class CommentForm extends Wire implements CommentFormInterface {
"\n\t</p>";
}
// do we need to show the honeypot field?
$honeypot = $this->options['requireHoneypotField'];
if($honeypot) {
$honeypotLabel = isset($labels[$honeypot]) ? $labels[$honeypot] : '';
$honeypotValue = isset($inputValues[$honeypot]) ? $inputValues[$honeypot] : '';
$form .=
"\n\t<p class='CommentFormHP {$id}_hp'>" .
"\n\t\t<label for='{$id}_$honeypot'>$honeypotLabel</label>" .
"\n\t\t<input type='text' id='{$id}_$honeypot' name='$honeypot' value='$honeypotValue' size='3' />" .
"\n\t</p>";
}
$form .=
"\n\t<p class='CommentFormText {$id}_text'>" .
"\n\t\t<label for='{$id}_text'>$labels[text]</label>" .
@@ -427,6 +446,19 @@ class CommentForm extends Wire implements CommentFormInterface {
"\n\t</p>";
}
// do we need to show the honeypot field?
$honeypot = $this->options['requireHoneypotField'];
if($honeypot) {
$honeypotLabel = isset($labels[$honeypot]) ? $labels[$honeypot] : '';
$honeypotValue = isset($inputValues[$honeypot]) ? $inputValues[$honeypot] : '';
$form .=
"\n\t<p class='CommentFormHP {$id}_hp'>" .
"\n\t\t<label><span>$honeypotLabel</span>" .
"\n\t\t<input type='text' name='$honeypot' value='$honeypotValue' size='3' />" .
"\n\t\t</label>" .
"\n\t</p>";
}
$form .=
"\n\t<p class='CommentFormText {$id}_text'>" .
"\n\t\t<label>" .
@@ -491,6 +523,10 @@ class CommentForm extends Wire implements CommentFormInterface {
if(empty($data[$key])) return false;
}
if($key = $this->options['requireHoneypotField']) {
if(!empty($data[$key])) return false;
}
$comment = $this->wire(new Comment());
$comment->user_agent = $_SERVER['HTTP_USER_AGENT'];
$comment->ip = $this->wire('session')->getIP();

View File

@@ -35,6 +35,10 @@
clear: both;
}
.CommentFormHP {
display: none;
}
@media only screen and (max-width: 767px) {
.CommentFormCite,
.CommentFormEmail,

View File

@@ -692,6 +692,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
public function ___exportValue(Page $page, Field $field, $value, array $options = array()) {
$a = array();
if(!WireArray::iterable($value)) return $a;
foreach($value as $k => $p) {
/** @var Page $p */
if($p->isUnpublished()) continue;
@@ -1056,6 +1057,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
}
if(in_array($operator, array('*=', '~=', '^=', '$=', '%='))) {
/** @var DatabaseQuerySelectFulltext $ft */
$ft = $this->wire(new DatabaseQuerySelectFulltext($query));
$ft->match($table, $subfield, $operator, $value);
@@ -1220,7 +1222,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
if($value->has($p)) continue;
// $this->message("Deleted Repeater", Notice::debug);
// delete the repeater page
$this->wire('pages')->delete($p);
$this->wire('pages')->delete($p, $saveOptions);
}
$result = parent::___savePageField($page, $field);

View File

@@ -498,6 +498,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
$forIDs = null;
if($loading == FieldtypeRepeater::loadingAll && $collapse != FieldtypeRepeater::collapseNone) $forIDs = array();
list($editorUrl, $queryString) = explode('?', $this->page->editUrl());
if($queryString) {}
$this->wire('config')->js('InputfieldRepeater', array(
'editorUrl' => $editorUrl,

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