From 5542b77440e83cad0293919d976ec8ddc445fed7 Mon Sep 17 00:00:00 2001 From: Ryan Cramer Date: Thu, 20 Oct 2016 07:02:24 -0400 Subject: [PATCH 1/2] Add profiler to TemplateFile and PageRender --- wire/core/TemplateFile.php | 53 ++++++++++++++++++++++++++++++++-- wire/modules/PageRender.module | 3 ++ 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/wire/core/TemplateFile.php b/wire/core/TemplateFile.php index 974bf046..3dce303b 100644 --- a/wire/core/TemplateFile.php +++ b/wire/core/TemplateFile.php @@ -71,6 +71,20 @@ class TemplateFile extends WireData { */ protected $halt = false; + /** + * Last tracked profile event + * + * @var mixed + * + */ + protected $profilerEvent = null; + + /** + * @var WireProfilerInterface|null + * + */ + protected $profiler = null; + /** * Variables that will be applied globally to this and all other TemplateFile instances * @@ -180,6 +194,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 +235,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 +244,29 @@ 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); 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 = require($this->filename); + if($this->profiler) $this->stop(); + foreach($this->appendFilename as $_filename) { if($this->halt) break; + if($this->profiler) $this->start($_filename); require($_filename); + if($this->profiler) $this->stop(); } + $out = "\n" . ob_get_contents() . "\n"; ob_end_clean(); @@ -229,6 +275,7 @@ class TemplateFile extends WireData { $out = trim($out); if(!strlen($out) && !$this->halt && $returnValue && $returnValue !== 1) return $returnValue; + return $out; } diff --git a/wire/modules/PageRender.module b/wire/modules/PageRender.module index 42f2882c..144282d7 100644 --- a/wire/modules/PageRender.module +++ b/wire/modules/PageRender.module @@ -500,7 +500,10 @@ class PageRender extends WireData implements Module, ConfigurableModule { // own additional variables in it if they want to $output->set('options', $options); + $profiler = $this->wire('profiler'); + $profilerEvent = $profiler ? $profiler->start($page->path, $this, array('page' => $page)) : null; $data = $output->render(); + if($profilerEvent) $profiler->stop($profilerEvent); } if($data && $cacheAllowed && $cacheFile) $cacheFile->save($data); From b4f2dda5fad707a1c9f0f521f56119b169500ec4 Mon Sep 17 00:00:00 2001 From: Ryan Cramer Date: Fri, 28 Oct 2016 05:41:45 -0400 Subject: [PATCH 2/2] Various updates primarily related to functions API and profiler support. --- wire/config.php | 13 + wire/core/.phpstorm.meta.php | 110 ++-- wire/core/Config.php | 1 + wire/core/Functions.php | 73 +++ wire/core/FunctionsAPI.php | 419 +++++++++++++++ wire/core/FunctionsWireAPI.php | 486 ++++++++++++++++++ wire/core/Modules.php | 73 ++- wire/core/Page.php | 6 + wire/core/PagesLoader.php | 6 +- wire/core/ProcessController.php | 4 +- wire/core/ProcessWire.php | 4 + wire/core/Wire.php | 66 ++- wire/core/WireHooks.php | 12 + wire/core/WireInputData.php | 6 + wire/core/WireLog.php | 2 + wire/core/boot.php | 1 + .../ProcessPageEditImageSelect.module | 19 +- 17 files changed, 1216 insertions(+), 85 deletions(-) create mode 100644 wire/core/FunctionsAPI.php create mode 100644 wire/core/FunctionsWireAPI.php diff --git a/wire/config.php b/wire/config.php index a574d6af..94f40273 100644 --- a/wire/config.php +++ b/wire/config.php @@ -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 *************************************************************************/ diff --git a/wire/core/.phpstorm.meta.php b/wire/core/.phpstorm.meta.php index dc9b92c0..95e8d0f9 100644 --- a/wire/core/.phpstorm.meta.php +++ b/wire/core/.phpstorm.meta.php @@ -1,4 +1,4 @@ - [ '' == '@', - '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 ] ]; } diff --git a/wire/core/Config.php b/wire/core/Config.php index b2394544..f1e4205f 100644 --- a/wire/core/Config.php +++ b/wire/core/Config.php @@ -116,6 +116,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 diff --git a/wire/core/Functions.php b/wire/core/Functions.php index f352bec8..330961ea 100644 --- a/wire/core/Functions.php +++ b/wire/core/Functions.php @@ -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', '

this is some content

'); + * + * // prepend some text to region + * region('+content', '

Good morning

'); + * + * // append some text to region + * region('content+', '

Good night

'); + * + * // 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; +} + diff --git a/wire/core/FunctionsAPI.php b/wire/core/FunctionsAPI.php new file mode 100644 index 00000000..4067b667 --- /dev/null +++ b/wire/core/FunctionsAPI.php @@ -0,0 +1,419 @@ +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); +} + +/** + * Get or set a region for front-end output + * + * ~~~~~ + * // define a region + * region('content', '

this is some content

'); + * + * // prepend some text to region + * region('+content', '

Good morning

'); + * + * // append some text to region + * region('content+', '

Good night

'); + * + * // 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); +} + diff --git a/wire/core/FunctionsWireAPI.php b/wire/core/FunctionsWireAPI.php new file mode 100644 index 00000000..7daa0bf0 --- /dev/null +++ b/wire/core/FunctionsWireAPI.php @@ -0,0 +1,486 @@ +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; +} + +/** + * 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); +} + diff --git a/wire/core/Modules.php b/wire/core/Modules.php index 6c7bc5b4..99ea3d7f 100644 --- a/wire/core/Modules.php +++ b/wire/core/Modules.php @@ -653,8 +653,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 { @@ -1211,7 +1214,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; @@ -2910,34 +2915,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; } @@ -3427,18 +3436,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); @@ -3451,6 +3477,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; } diff --git a/wire/core/Page.php b/wire/core/Page.php index 948eca80..ac35404f 100644 --- a/wire/core/Page.php +++ b/wire/core/Page.php @@ -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); } diff --git a/wire/core/PagesLoader.php b/wire/core/PagesLoader.php index 51c0e728..ca22b20e 100644 --- a/wire/core/PagesLoader.php +++ b/wire/core/PagesLoader.php @@ -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, diff --git a/wire/core/ProcessController.php b/wire/core/ProcessController.php index 5e5d6fde..6a81b2bb 100644 --- a/wire/core/ProcessController.php +++ b/wire/core/ProcessController.php @@ -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 diff --git a/wire/core/ProcessWire.php b/wire/core/ProcessWire.php index 6f023c67..7a792059 100644 --- a/wire/core/ProcessWire.php +++ b/wire/core/ProcessWire.php @@ -207,6 +207,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); } diff --git a/wire/core/Wire.php b/wire/core/Wire.php index 1b529656..629ce769 100644 --- a/wire/core/Wire.php +++ b/wire/core/Wire.php @@ -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. * diff --git a/wire/core/WireHooks.php b/wire/core/WireHooks.php index 0eaffc65..4db05665 100644 --- a/wire/core/WireHooks.php +++ b/wire/core/WireHooks.php @@ -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']++; diff --git a/wire/core/WireInputData.php b/wire/core/WireInputData.php index d7fcdbd4..1794ef18 100644 --- a/wire/core/WireInputData.php +++ b/wire/core/WireInputData.php @@ -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) { diff --git a/wire/core/WireLog.php b/wire/core/WireLog.php index 796d2561..53589925 100644 --- a/wire/core/WireLog.php +++ b/wire/core/WireLog.php @@ -10,6 +10,8 @@ * * ProcessWire 3.x, Copyright 2016 by Ryan Cramer * https://processwire.com + * + * @method bool save($name, $text, $options = array()) * */ diff --git a/wire/core/boot.php b/wire/core/boot.php index e5360292..e95f2be5 100644 --- a/wire/core/boot.php +++ b/wire/core/boot.php @@ -29,6 +29,7 @@ $corePreloads = array( 'FilenameArray.php', 'Paths.php', 'Config.php', + 'FunctionsWireAPI.php', 'Functions.php', 'LanguageFunctions.php', 'WireShutdown.php', diff --git a/wire/modules/Process/ProcessPageEditImageSelect/ProcessPageEditImageSelect.module b/wire/modules/Process/ProcessPageEditImageSelect/ProcessPageEditImageSelect.module index 7e5bf482..40307ec7 100644 --- a/wire/modules/Process/ProcessPageEditImageSelect/ProcessPageEditImageSelect.module +++ b/wire/modules/Process/ProcessPageEditImageSelect/ProcessPageEditImageSelect.module @@ -95,6 +95,7 @@ class ProcessPageEditImageSelect extends Process implements ConfigurableModule { 'flipHorizontal' => $this->_('Flip horizontal'), 'flipVertical' => $this->_('Flip vertical'), 'noAccess' => $this->_('You do not have access to edit images on this page.'), + 'demoMode' => "Image editing functions are disabled in demo mode", ); } @@ -106,7 +107,12 @@ class ProcessPageEditImageSelect extends Process implements ConfigurableModule { */ public function init() { - if($this->config->demo) throw new WireException("Sorry, image editing functions are disabled in demo mode"); + // throw new WireException($this->labels['demoMode']); + if($this->config->demo) { + if($this->wire('input')->urlSegmentStr != 'variations') { + throw new WireException($this->labels['demoMode']); + } + } $this->modules->get("ProcessPageList"); @@ -1322,7 +1328,16 @@ class ProcessPageEditImageSelect extends Process implements ConfigurableModule { $this->wire('modules')->get('JqueryMagnific'); - return "
" . $form->render(); + $out = $form->render(); + + if($this->wire('config')->demo) { + $out = "

Note: " . $this->labels['demoMode'] . "

" . $out; + } else { + $out = "
" . $out; + } + + return $out; + } /**