1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-11 09:14:58 +02:00

Merge recent changes from devns branch

This commit is contained in:
Ryan Cramer
2016-09-22 12:46:59 -04:00
parent bac5b0de5d
commit ee89ddcea5
45 changed files with 694 additions and 182 deletions

View File

@@ -555,6 +555,35 @@ $config->adminThumbOptions = array(
'suffix' => '',
);
/**
* File compiler options (as used by FileCompiler class)
*
* Enables modification of file compiler behaviors. See also $config->moduleCompile
* and $config->templateCompile settings.
*
* #property bool siteOnly Specify true to prevent compiler from attempting compilation outside files in /site/ (default=false).
* #property bool showNotices Show notices in admin about compiled files to superuser when logged in (default=true).
* #property bool logNotices Log notices about compiled files and maintenance to file-compiler.txt log (default=true).
* #property string chmodFile Mode to use for created files, i.e. "0644" (uses $config->chmodFile setting by default).
* #property string chmodDir Mode to use for created dirs, i.e. "0755" (uses $config->chmodDir setting by default).
* #property array exclusions Exclude paths that exist within any of these paths (default includes $config->paths->wire).
* #property array extensions File extensions that we compile (default=php, module, inc).
* #property string cachePath Path where compiled files are stored (default is $config->paths->cache . 'FileCompiler/')
*
* @var array
*
*/
$config->fileCompilerOptions = array(
'siteOnly' => false, // only allow compilation of files in /site/ directory
'showNotices' => true, // show notices about compiled files to superuser when logged in
'logNotices' => true, // log notices about compiled files and maintenance to file-compiler.txt log.
'chmodFile' => $config->chmodFile, // mode to use for created files, i.e. "0644"
'chmodDir' => $config->chmodDir, // mode to use for created directories, i.e. "0755"
'exclusions' => array(), // exclude filenames or paths that start with any of these
'extensions' => array('php', 'module', 'inc'), // file extensions we compile
'cachePath' => $config->paths->cache . 'FileCompiler/', // path where compiled files are stored
);
/**
* Temporary directory for uploads
*
@@ -1023,9 +1052,13 @@ $config->preloadCacheNames = array(
$config->allowExceptions = false;
/**
* Use the X-Powered-By header?
* X-Powered-By header behavior
*
* - true: Sends the generic PW header, replacing any other powered-by headers (recommended).
* - false: Sends blank powered-by, replacing any other powered-by headers.
* - null: Sends no powered-by, existing server powered-by headers will pass through.
*
* @var bool
* @var bool|null
*
*/
$config->usePoweredBy = true;

View File

@@ -40,6 +40,7 @@
*
* @property array $contentTypes Array of extensions and the associated MIME type for each (for template file output). #pw-group-template-files
* @property array $fileContentTypes Array of extensions and the associated MIME type for each (for file output). See /wire/config.php for details and defaults. #pw-group-files
* @property array $fileCompilerOptions Array of options for FileCompiler class. See /wire/config.php for details and defaults. #pw-group-files
*
* @property string $chmodDir Octal string permissions assigned to directories created by ProcessWire #pw-group-files
* @property string $chmodFile Octal string permissions assigned to files created by ProcessWire #pw-group-files
@@ -301,5 +302,71 @@ class Config extends WireData {
$this->jsFields[] = $key;
return parent::set($key, $value);
}
/**
* Allow for getting/setting config properties via method call
*
* This is primarily useful for getting or setting config properties that consist of associative arrays.
*
* ~~~~~
*
* // Enable debug mode, same as $config->debug = true;
* $config->debug(true);
*
* // Set a specific property in options array
* $config->fileCompilerOptions('siteOnly', true);
*
* // Get a specific property in options array
* $value = $config->fileCompilerOptions('siteOnly');
*
* // Set multiple properties in options array (leaving other existing properties alone)
* $config->fileCompilerOptions([
* 'siteOnly' => true,
* 'cachePath' => $config->paths->root . '.my-cache/'
* ]);
* ~~~~~
*
* #pw-internal
*
* @param string $method Requested method name
* @param array $arguments Arguments provided
* @return null|mixed Return value of method (if applicable)
* @throws WireException
*
*/
protected function ___callUnknown($method, $arguments) {
//$this->message("callUnknown($method)");
$value = parent::get($method);
if($value === null) return parent::___callUnknown($method, $arguments);
$numArgs = count($arguments);
if($numArgs === 0) return $value; // no arguments? just return value
if(is_array($value)) {
// existing value is an array
$property = $arguments[0];
if($numArgs === 1) {
// just a property name (get) or array (set) provided
if(is_string($property)) {
// just return property value from array
return isset($value[$property]) ? $value[$property] : null;
} else if(is_array($property)) {
// set multiple named properties
$value = array_merge($value, $property);
parent::set($method, $value);
} else {
throw new WireException("Invalid argument, array or string expected");
}
} else {
// property and value provided
$value[$property] = $arguments[1];
parent::set($method, $value);
}
} else if($numArgs === 1) {
// just set a value
parent::set($method, $arguments[0]);
}
return $this;
}
}

View File

@@ -480,7 +480,7 @@ class Fields extends WireSaveableItems {
foreach($schema1 as $key => $value) {
if(!in_array($value, $schema2)) {
if($this->config->debug) $this->message("changeFieldType loses table field '$value'");
if($this->wire('config')->debug) $this->message("changeFieldType loses table field '$value'");
unset($schema1[$key]);
}
}

View File

@@ -14,7 +14,7 @@
class FileCompiler extends Wire {
/**
* Compilation options
* Compilation options for this FileCompiler instance
*
* @var array
*
@@ -25,6 +25,25 @@ class FileCompiler extends Wire {
'modules' => false, // compile using installed FileCompiler modules
'skipIfNamespace' => false, // skip compiled file if original declares a namespace? (note: file still compiled, but not used)
);
/**
* Options for ALL FileCompiler instances
*
* Values shown below are for reference only as the get overwritten by $config->fileCompilerOptions at runtime.
*
* @var array
*
*/
protected $globalOptions = array(
'siteOnly' => false, // only allow compilation of files in /site/ directory
'showNotices' => true, // show notices about compiled files to superuser
'logNotices' => true, // log notices about compiled files and maintenance to file-compiler.txt log.
'chmodFile' => '', // mode to use for created files, i.e. "0644"
'chmodDir' => '', // mode to use for created directories, i.e. "0755"
'exclusions' => array(), // exclude files or paths that start with any of these (gets moved to $this->exclusions array)
'extensions' => array('php', 'module', 'inc'), // file extensions we compile (gets moved to $this->extensions array)
'cachePath' => '', // path where compiled files are stored (default is /site/assets/cache/FileCompiler/, moved to $this->cachePath)
);
/**
* Path to source files directory
@@ -42,6 +61,16 @@ class FileCompiler extends Wire {
*/
protected $targetPath = null;
/**
* Path to root of compiled files directory (upon which targetPath is based)
*
* Set via the $config->fileCompilerOptions['cachePath'] setting.
*
* @var string
*
*/
protected $cachePath;
/**
* Files or directories that should be excluded from compilation
*
@@ -78,12 +107,30 @@ class FileCompiler extends Wire {
*
*/
public function __construct($sourcePath, array $options = array()) {
$this->options = array_merge($this->options, $options);
$globalOptions = $this->wire('config')->fileCompilerOptions;
if(is_array($globalOptions)) {
$this->globalOptions = array_merge($this->globalOptions, $globalOptions);
}
if(!empty($this->globalOptions['extensions'])) {
$this->extensions = $this->globalOptions['extensions'];
}
if(empty($this->globalOptions['cachePath'])) {
$this->cachePath = $this->wire('config')->paths->cache . $this->className() . '/';
} else {
$this->cachePath = rtrim($this->globalOptions['cachePath'], '/') . '/';
}
if(!strlen(__NAMESPACE__)) {
// when PW compiled without namespace support
$this->options['skipIfNamespace'] = false;
$this->options['namespace'] = true;
}
if(strpos($sourcePath, '..') !== false) $sourcePath = realpath($sourcePath);
if(DIRECTORY_SEPARATOR != '/') $sourcePath = str_replace(DIRECTORY_SEPARATOR, '/', $sourcePath);
$this->sourcePath = rtrim($sourcePath, '/') . '/';
@@ -98,29 +145,59 @@ class FileCompiler extends Wire {
protected function init() {
static $preloaded = false;
$config = $this->wire('config');
if(!$preloaded) {
$this->wire('cache')->preloadFor($this);
$preloaded = true;
}
$targetPath = $this->wire('config')->paths->cache . $this->className() . '/';
if(!empty($this->globalOptions['exclusions'])) {
$this->exclusions = $this->globalOptions['exclusions'];
}
$this->addExclusion($config->paths->wire);
$rootPath = $config->paths->root;
$targetPath = $this->cachePath;
if(strpos($this->sourcePath, $targetPath) === 0) {
// sourcePath is inside the targetPath, correct this
$this->sourcePath = str_replace($targetPath, '', $this->sourcePath);
$this->sourcePath = $this->wire('config')->paths->root . $this->sourcePath;
$this->sourcePath = $rootPath . $this->sourcePath;
}
$t = str_replace($this->wire('config')->paths->root, '', $this->sourcePath);
$t = str_replace($rootPath, '', $this->sourcePath);
if(DIRECTORY_SEPARATOR != '/' && strpos($t, ':')) $t = str_replace(':', '', $t);
$this->targetPath = $targetPath . trim($t, '/') . '/';
// @todo move this somewhere outside of this class
$this->addExclusion($this->wire('config')->paths->wire);
// $this->addExclusion($this->wire('config')->paths->templates . 'admin.php');
$this->ns = '';
}
/**
* Make a directory with proper permissions
*
* @param string $path Path of directory to create
* @param bool $recursive Default is true
* @return bool
*
*/
protected function mkdir($path, $recursive = true) {
$chmod = $this->globalOptions['chmodDir'];
if(empty($chmod) || !is_string($chmod) || strlen($chmod) < 2) $chmod = null;
return $this->wire('files')->mkdir($path, $recursive, $chmod);
}
/**
* Change file to correct mode for FileCompiler
*
* @param string $filename
* @return bool
*
*/
protected function chmod($filename) {
$chmod = $this->globalOptions['chmodFile'];
if(empty($chmod) || !is_string($chmod) || strlen($chmod) < 2) $chmod = null;
return $this->wire('files')->chmod($filename, false, $chmod);
}
/**
@@ -131,7 +208,7 @@ class FileCompiler extends Wire {
*/
protected function initTargetPath() {
if(!is_dir($this->targetPath)) {
if(!$this->wire('files')->mkdir($this->targetPath, true)) {
if(!$this->mkdir($this->targetPath)) {
throw new WireException("Unable to create directory $this->targetPath");
}
}
@@ -140,13 +217,21 @@ class FileCompiler extends Wire {
/**
* Allow the given filename to be compiled?
*
* @param string $filename This property can be modified by the function
* @param string $basename This property can be modified by the function
* @param string $filename Full path and filename to compile (this property can be modified by the function).
* @param string $basename Just the basename (this property can be modified by the function).
* @return bool
*
*/
protected function allowCompile(&$filename, &$basename) {
if($this->globalOptions['siteOnly']) {
// only files in /site/ are allowed for compilation
if(strpos($filename, $this->wire('config')->paths->site) !== 0) {
// sourcePath is somewhere outside of the PW /site/, and not allowed
return false;
}
}
$ext = pathinfo($filename, PATHINFO_EXTENSION);
if(!in_array(strtolower($ext), $this->extensions)) {
if(!strlen($ext) && !is_file($filename)) {
@@ -237,10 +322,10 @@ class FileCompiler extends Wire {
set_time_limit(120);
$this->copyAllNewerFiles($sourcePath, $targetPath);
$targetDirname = dirname($targetPathname) . '/';
if(!is_dir($targetDirname)) $this->wire('files')->mkdir($targetDirname, true);
if(!is_dir($targetDirname)) $this->mkdir($targetDirname);
$targetData = $this->compileData($targetData, $sourcePathname);
if(false !== file_put_contents($targetPathname, $targetData, LOCK_EX)) {
$this->wire('files')->chmod($targetPathname);
$this->chmod($targetPathname);
touch($targetPathname, filemtime($sourcePathname));
$targetHash = md5_file($targetPathname);
$cacheData = array(
@@ -260,14 +345,24 @@ class FileCompiler extends Wire {
);
$this->wire('cache')->saveFor($this, $cacheName, $cacheData, WireCache::expireNever);
}
$u = $this->wire('user');
if($this->wire('config')->debug || ($u && $u->isSuperuser())) {
$this->message($this->_('Compiled file:') . ' ' . str_replace($this->wire('config')->paths->root, '/', $sourcePathname));
}
}
// if source and target are identical, use the source file
if($targetHash && $sourceHash === $targetHash) return $sourcePathname;
if($targetHash && $sourceHash === $targetHash) {
return $sourcePathname;
}
// show notices about compiled files, when applicable
if($compileNow) {
$message = $this->_('Compiled file:') . ' ' . str_replace($this->wire('config')->paths->root, '/', $sourcePathname);
if($this->globalOptions['showNotices']) {
$u = $this->wire('user');
if($u && $u->isSuperuser()) $this->message($message);
}
if($this->globalOptions['logNotices']) {
$this->log($message);
}
}
// if source file declares a namespace and skipIfNamespace option in use, use source file
if($this->options['skipIfNamespace'] && $this->ns && $this->ns != "\\") return $sourcePathname;
@@ -747,7 +842,7 @@ class FileCompiler extends Wire {
}
copy($sourceFile, $targetFile);
$this->wire('files')->chmod($targetFile);
$this->chmod($targetFile);
touch($targetFile, filemtime($sourceFile));
$numCopied++;
}
@@ -772,7 +867,7 @@ class FileCompiler extends Wire {
if(!is_null($targetPath)) {
// use it
} else if($all) {
$targetPath = $this->wire('config')->paths->cache . $this->className() . '/';
$targetPath = $this->cachePath;
} else {
$this->init();
$targetPath = $this->targetPath;
@@ -803,7 +898,7 @@ class FileCompiler extends Wire {
*/
public function clearCache($all = false) {
if($all) {
$targetPath = $this->wire('config')->paths->cache . $this->className() . '/';
$targetPath = $this->cachePath;
$this->wire('cache')->deleteFor($this);
} else {
$this->init();
@@ -832,7 +927,7 @@ class FileCompiler extends Wire {
return false;
}
touch($lastRunFile);
$this->wire('files')->chmod($lastRunFile);
$this->chmod($lastRunFile);
clearstatcache();
return $this->_maintenance($this->sourcePath, $this->targetPath);
@@ -854,6 +949,7 @@ class FileCompiler extends Wire {
$targetPath = rtrim($targetPath, '/') . '/';
$sourceURL = str_replace($this->wire('config')->paths->root, '/', $sourcePath);
$targetURL = str_replace($this->wire('config')->paths->root, '/', $targetPath);
$useLog = $this->globalOptions['logNotices'];
//$this->log("Running maintenance for $targetURL (source: $sourceURL)");
@@ -871,7 +967,7 @@ class FileCompiler extends Wire {
if($file->isDir()) {
if(!is_dir($sourceFile)) {
$this->wire('files')->rmdir($targetFile, true);
$this->log("Remove directory: $targetURL$basename");
if($useLog) $this->log("Maintenance/Remove directory: $targetURL$basename");
} else {
$this->_maintenance($sourceFile, $targetFile);
}
@@ -881,14 +977,14 @@ class FileCompiler extends Wire {
if(!file_exists($sourceFile)) {
// source file has been deleted
unlink($targetFile);
$this->log("Remove target file: $targetURL$basename");
if($useLog) $this->log("Maintenance/Remove target file: $targetURL$basename");
} else if(filemtime($sourceFile) != filemtime($targetFile)) {
// source file has changed
copy($sourceFile, $targetFile);
$this->wire('files')->chmod($targetFile);
$this->chmod($targetFile);
touch($targetFile, filemtime($sourceFile));
$this->log("Copy new version of source file to target file: $sourceURL$basename => $targetURL$basename");
if($useLog) $this->log("Maintenance/Copy new version of source file to target file: $sourceURL$basename => $targetURL$basename");
}
}

View File

@@ -1330,9 +1330,12 @@ class Modules extends WireArray {
// attempt to retrieve module
$module = parent::get($moduleName);
if($module && !$module instanceof Module) return false;
if($module) {
// module found, check to make sure it actually points to a module
if(!$module instanceof Module) $module = false;
if(!$module && $moduleName) {
} else if($moduleName) {
// unable to retrieve module, may be an uninstalled module
if(!$file) {
$file = $this->getModuleFile($moduleName, array('fast' => true));
@@ -1340,9 +1343,16 @@ class Modules extends WireArray {
}
if($file) {
$this->includeModuleFile($file, $moduleName);
if(class_exists($className, false)) {
// successful include module
$module = true;
// now check to see if included file resulted in presence of module class
if(class_exists($className)) {
$module = true;
} else {
if(!$namespace) $namespace = $this->getModuleNamespace($moduleName, array('file' => $file));
$nsClassName = trim($namespace, "\\") . "\\$moduleName";
if(class_exists($nsClassName, false)) {
// successful include module
$module = true;
}
}
}
}
@@ -1396,8 +1406,10 @@ class Modules extends WireArray {
// get compiled version (if it needs compilation)
$file = $this->compile($moduleName, $file);
/** @noinspection PhpIncludeInspection */
include_once($file);
if($file) {
/** @noinspection PhpIncludeInspection */
include_once($file);
}
// set instance back, if multi-instance
if($wire1 !== $wire2) ProcessWire::setCurrentInstance($wire1);
@@ -3185,8 +3197,10 @@ class Modules extends WireArray {
} else {
$ns = $this->getFileNamespace($file);
$file = $this->compile($className, $file, $ns);
/** @noinspection PhpIncludeInspection */
include($file);
if($file) {
/** @noinspection PhpIncludeInspection */
include($file);
}
}
}
if(!is_null($config)) {
@@ -3324,8 +3338,10 @@ class Modules extends WireArray {
if(!class_exists($className, false)) {
$configFile = $this->compile($className, $configurable);
// $configFile = $compile ? $this->wire('files')->compile($configurable) : $configurable;
/** @noinspection PhpIncludeInspection */
include_once($configFile);
if($configFile) {
/** @noinspection PhpIncludeInspection */
include_once($configFile);
}
}
if(wireClassExists($className)) {
@@ -3345,8 +3361,10 @@ class Modules extends WireArray {
$configFile = $this->compile($className, $configurable);
// $configFile = $compile ? $this->wire('files')->compile($configurable) : $configurable;
}
/** @noinspection PhpIncludeInspection */
include($configFile);
if($configFile) {
/** @noinspection PhpIncludeInspection */
include($configFile);
}
}
if(is_array($config)) {
// alternatively, file may just specify a $config array
@@ -3510,8 +3528,10 @@ class Modules extends WireArray {
$configClass = $ns . $moduleName . "Config";
if(!class_exists($configClass)) {
$configFile = $this->compile($moduleName, $file, $ns);
/** @noinspection PhpIncludeInspection */
include_once($configFile);
if($configFile) {
/** @noinspection PhpIncludeInspection */
include_once($configFile);
}
}
$configModule = null;
@@ -3523,8 +3543,10 @@ class Modules extends WireArray {
if(is_null($config)) {
$configFile = $this->compile($moduleName, $file, $ns);
// if(!$configFile) $configFile = $compile ? $this->wire('files')->compile($file) : $file;
/** @noinspection PhpIncludeInspection */
include($configFile); // in case of previous include_once
if($configFile) {
/** @noinspection PhpIncludeInspection */
include($configFile); // in case of previous include_once
}
}
if(is_array($config)) {
// file contains a $config array
@@ -4591,16 +4613,16 @@ class Modules extends WireArray {
* @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
* @return bool
* @return string|bool
*
*/
public function compile($moduleName, $file = '', $namespace = null) {
// don't compile when module compilation is disabled
if(!$this->wire('config')->moduleCompile) return false;
// if not given a file, track it down
if(empty($file)) $file = $this->getModuleFile($moduleName);
// don't compile when module compilation is disabled
if(!$this->wire('config')->moduleCompile) return $file;
// don't compile core modules
if(strpos($file, $this->coreModulesDir) !== false) return $file;

View File

@@ -182,7 +182,8 @@ class Password extends Wire {
$rawLength = (int) ($requiredLength * 3 / 4 + 1);
if(function_exists('mcrypt_create_iv')) {
$buffer = mcrypt_create_iv($rawLength, MCRYPT_DEV_URANDOM);
// @operator added for PHP 7.1 which throws deprecated notice on this function call
$buffer = @mcrypt_create_iv($rawLength, MCRYPT_DEV_URANDOM);
if($buffer) $valid = true;
}

View File

@@ -2321,6 +2321,7 @@ class Sanitizer extends Wire {
'templateName',
'pageName',
'pageNameTranslate',
'pageNameUTF8',
'filename',
'path',
'pagePathName',

View File

@@ -388,7 +388,7 @@ class Template extends WireData implements Saveable, Exportable {
$has = in_array($rolePage->id, $this->editRoles);
} else if($type === 'create') {
$has = in_array($rolePage->id, $this->createRoles);
} else if($tye == 'add') {
} else if($type == 'add') {
$has = in_array($rolePage->id, $this->addRoles);
}
return $has;

View File

@@ -368,8 +368,13 @@ abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable {
*
*/
public function __call($method, $arguments) {
$result = $this->wire('hooks')->runHooks($this, $method, $arguments);
if(!$result['methodExists'] && !$result['numHooksRun']) return $this->callUnknown($method, $arguments);
$hooks = $this->wire('hooks');
if($hooks) {
$result = $hooks->runHooks($this, $method, $arguments);
if(!$result['methodExists'] && !$result['numHooksRun']) return $this->callUnknown($method, $arguments);
} else {
return $this->___callUnknown($method, $arguments);
}
return $result['return'];
}
@@ -406,7 +411,8 @@ abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable {
*/
protected function ___callUnknown($method, $arguments) {
if($arguments) {} // intentional to avoid unused argument notice
if($this->wire('config')->disableUnknownMethodException) return null;
$config = $this->wire('config');
if($config && $config->disableUnknownMethodException) return null;
throw new WireException("Method " . $this->className() . "::$method does not exist or is not callable in this context");
}
@@ -1540,6 +1546,7 @@ abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable {
// this object has not yet been wired! use last known current instance as fallback
// note this condition is unsafe in multi-instance mode
$wire = ProcessWire::getCurrentInstance();
if(!$wire) return null;
// For live hunting objects that are using the fallback, uncomment the following:
// echo "<hr /><p>Non-wired object: '$name' in " . get_class($this) . ($value ? " (value=$value)" : "") . "</p>";

View File

@@ -710,4 +710,25 @@ class WireDatabasePDO extends Wire implements WireDatabase {
return $backups;
}
/**
* Get max length allowed for a fully indexed varchar column in ProcessWire
*
* #pw-internal
*
* @return int
*
*/
public function getMaxIndexLength() {
$config = $this->wire('config');
$engine = strtolower($config->dbEngine);
$charset = strtolower($config->dbCharset);
$max = 250;
if($charset == 'utf8mb4') {
if($engine == 'innodb') {
$max = 191;
}
}
return $max;
}
}

View File

@@ -21,6 +21,8 @@ class WireFileTools extends Wire {
* setting `$config->chmodDir`, and it can create directories recursively. Meaning, if you want to create directory /a/b/c/
* and directory /a/ doesn't yet exist, this method will take care of creating /a/, /a/b/, and /a/b/c/.
*
* The `$recursive` and `$chmod` arguments may optionally be swapped (since 3.0.34).
*
* ~~~~~
* // Create a new directory in ProcessWire's cache dir
* if($files->mkdir($config->paths->cache . 'foo-bar/')) {
@@ -29,14 +31,22 @@ class WireFileTools extends Wire {
* ~~~~~
*
* @param string $path Directory you want to create
* @param bool $recursive If set to true, all directories will be created as needed to reach the end.
* @param string $chmod Optional mode to set directory to (default: $config->chmodDir), format must be a string i.e. "0755"
* @param bool|string $recursive If set to true, all directories will be created as needed to reach the end.
* @param string|null|bool $chmod Optional mode to set directory to (default: $config->chmodDir), format must be a string i.e. "0755"
* If omitted, then ProcessWire's `$config->chmodDir` setting is used instead.
* @return bool True on success, false on failure
*
*/
public function mkdir($path, $recursive = false, $chmod = null) {
if(!strlen($path)) return false;
if(is_string($recursive) && strlen($recursive) > 2) {
// chmod argument specified as $recursive argument or arguments swapped
$_chmod = $recursive;
$recursive = is_bool($chmod) ? $chmod : false;
$chmod = $_chmod;
}
if(!is_dir($path)) {
if($recursive) {
$parentPath = substr($path, 0, strrpos(rtrim($path, '/'), '/'));
@@ -93,20 +103,29 @@ class WireFileTools extends Wire {
* This method also provides the option of going recursive, adjusting the read/write mode for an entire
* file/directory tree at once.
*
* The `$recursive` or `$chmod` arguments may be optionally swapped in order (since 3.0.34).
*
* ~~~~~
* // Update the mode of /site/assets/cache/foo-bar/ recursively
* $files->chmod($config->paths->cache . 'foo-bar/', true);
* ~~~~~
*
* @param string $path Path or file that you want to adjust mode for (may be a path/directory or a filename).
* @param bool $recursive If set to true, all files and directories in $path will be recursively set as well (default=false).
* @param string If you want to set the mode to something other than ProcessWire's chmodFile/chmodDir settings,
* @param bool|string $recursive If set to true, all files and directories in $path will be recursively set as well (default=false).
* @param string|null|bool $chmod If you want to set the mode to something other than ProcessWire's chmodFile/chmodDir settings,
* 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
*
*/
public function chmod($path, $recursive = false, $chmod = null) {
if(is_string($recursive) && strlen($recursive) > 2) {
// chmod argument specified as $recursive argument or arguments swapped
$_chmod = $recursive;
$recursive = is_bool($chmod) ? $chmod : false;
$chmod = $_chmod;
}
if(is_null($chmod)) {
// default: pull values from PW config

View File

@@ -1,7 +1,7 @@
DROP TABLE IF EXISTS `field_email`;
CREATE TABLE `field_email` (
`pages_id` int(10) unsigned NOT NULL,
`data` varchar(255) NOT NULL default '',
`data` varchar(250) NOT NULL default '',
PRIMARY KEY (`pages_id`),
KEY `data_exact` (`data`),
FULLTEXT KEY `data` (`data`)
@@ -122,7 +122,7 @@ INSERT INTO `field_title` (`pages_id`, `data`) VALUES (54,'Lock or unlock a page
DROP TABLE IF EXISTS `templates`;
CREATE TABLE `templates` (
`id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(255) character set ascii NOT NULL,
`name` varchar(250) character set ascii NOT NULL,
`fieldgroups_id` int(10) unsigned NOT NULL default '0',
`flags` int(11) NOT NULL default '0',
`cache_time` mediumint(9) NOT NULL default '0',
@@ -141,7 +141,7 @@ DROP TABLE IF EXISTS `fieldgroups`;
CREATE TABLE `fieldgroups` (
`id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(255) character set ascii NOT NULL,
`name` varchar(250) character set ascii NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=MyISAM AUTO_INCREMENT=97 DEFAULT CHARSET=utf8;
@@ -173,9 +173,9 @@ DROP TABLE IF EXISTS `fields`;
CREATE TABLE `fields` (
`id` int(10) unsigned NOT NULL auto_increment,
`type` varchar(128) character set ascii NOT NULL,
`name` varchar(255) character set ascii NOT NULL,
`name` varchar(250) character set ascii NOT NULL,
`flags` int(11) NOT NULL default '0',
`label` varchar(255) NOT NULL default '',
`label` varchar(250) NOT NULL default '',
`data` text NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`),

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

@@ -305,7 +305,7 @@
.InputfieldItemList {
/* nested item list */
.InputfieldItemHeader:not(:hover) {
.InputfieldItemHeader:not(:hover):not(.ui-state-error) {
/* lighten up nested item list headers use a different color */
background: $ui-state-default-bg;
color: $ui-state-default-color;

View File

@@ -113,6 +113,7 @@ var ProcessWireAdminTheme = {
// double click occurred
clearTimeout(clickTimer);
numClicks = 0;
window.location.href = $a.attr('href');
return true;
}
return false;

File diff suppressed because one or more lines are too long

View File

@@ -402,7 +402,7 @@ table {
//border-color: darken($item-border-color, 4%);
background: $item-bg;
border: none;
margin-bottom: 1em;
//margin-bottom: 1em;
}
.InputfieldItemList .InputfieldItemHeader {
@@ -440,7 +440,8 @@ table {
color: $ui-state-default-color;
}
.InputfieldItemList .InputfieldItemHeader.ui-state-error {
.InputfieldItemList .InputfieldItemHeader.ui-state-error,
.InputfieldItemList .InputfieldItemList .InputfieldItemHeader.ui-state-error {
/* deleted item state */
text-decoration: line-through;
background: $error-bg;

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

@@ -38,8 +38,9 @@ class FieldtypeEmail extends FieldtypeText {
}
public function getDatabaseSchema(Field $field) {
$len = $this->wire('database')->getMaxIndexLength();
$schema = parent::getDatabaseSchema($field);
$schema['data'] = "varchar(250) NOT NULL default ''";
$schema['data'] = "varchar($len) NOT NULL default ''";
return $schema;
}

View File

@@ -357,8 +357,9 @@ class FieldtypeFile extends FieldtypeMulti {
$database = $this->wire('database');
$schema = parent::getDatabaseSchema($field);
$table = $database->escapeTable($field->table);
$maxLen = $database->getMaxIndexLength();
$schema['data'] = 'varchar(250) NOT NULL';
$schema['data'] = "varchar($maxLen) NOT NULL";
$schema['description'] = "text NOT NULL";
$schema['modified'] = "datetime";
$schema['created'] = "datetime";

View File

@@ -702,6 +702,7 @@ class SelectableOptionManager extends Wire {
$database = $this->wire('database');
$table = self::optionsTable;
$languages = $this->wire('languages');
$maxLen = $database->getMaxIndexLength();
// check for added languages
foreach($languages as $language) {
@@ -715,13 +716,13 @@ class SelectableOptionManager extends Wire {
try {
$database->exec("ALTER TABLE $table ADD $titleCol TEXT");
$database->exec("ALTER TABLE $table ADD UNIQUE $titleCol ($titleCol(250), fields_id)");
$database->exec("ALTER TABLE $table ADD UNIQUE $titleCol ($titleCol($maxLen), fields_id)");
} catch(\Exception $e) {
$this->error($e->getMessage());
}
try {
$database->exec("ALTER TABLE $table ADD $valueCol VARCHAR(250)");
$database->exec("ALTER TABLE $table ADD INDEX $valueCol ($valueCol(250), fields_id)");
$database->exec("ALTER TABLE $table ADD $valueCol VARCHAR($maxLen)");
$database->exec("ALTER TABLE $table ADD INDEX $valueCol ($valueCol($maxLen), fields_id)");
$database->exec("ALTER TABLE $table ADD FULLTEXT {$titleCol}_$valueCol ($titleCol, $valueCol)");
} catch(\Exception $e) {
$this->error($e->getMessage());
@@ -755,6 +756,7 @@ class SelectableOptionManager extends Wire {
public function install() {
$database = $this->wire('database');
$maxLen = $database->getMaxIndexLength();
$query = $database->prepare("SHOW TABLES LIKE '" . self::optionsTable . "'");
$query->execute();
@@ -766,11 +768,11 @@ class SelectableOptionManager extends Wire {
"fields_id INT UNSIGNED NOT NULL, " .
"option_id INT UNSIGNED NOT NULL, " .
"`title` TEXT, " .
"`value` VARCHAR(250), " .
"`value` VARCHAR($maxLen), " .
"sort INT UNSIGNED NOT NULL, " .
"PRIMARY KEY (fields_id, option_id), " .
"UNIQUE title (title(250), fields_id), " .
"INDEX `value` (`value`(250), fields_id), " .
"UNIQUE title (title($maxLen), fields_id), " .
"INDEX `value` (`value`($maxLen), fields_id), " .
"INDEX sort (sort, fields_id), " .
"FULLTEXT title_value (`title`, `value`)" .
") ENGINE=$engine DEFAULT CHARSET=$charset";

View File

@@ -196,6 +196,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
*/
protected function preloadInputfieldAssets($fieldIDs = array()) {
if(empty($fieldIDs)) $fieldIDs = $this->field->get('repeaterFields');
if(!is_array($fieldIDs)) return;
foreach($fieldIDs as $fieldID) {
$field = $this->wire('fields')->get($fieldID);
try {

View File

@@ -120,6 +120,8 @@ class InputfieldFile extends Inputfield implements InputfieldItemList {
// get the max filesize
$filesize = trim(ini_get('post_max_size'));
$last = strtolower(substr($filesize, -1));
if(ctype_alpha($last)) $filesize = rtrim($filesize, $last);
$filesize = (int) $filesize;
if($last == 'g') $this->maxFilesize = (($filesize*1024)*1024)*1024;
else if($last == 'm') $this->maxFilesize = ($filesize*1024)*1024;
else if($last == 'k') $this->maxFilesize = $filesize*1024;

View File

@@ -1,6 +1,8 @@
jQuery(document).ready(function($) {
$("input.InputfieldPasswordComplexify").each(function() {
var $inputs = $("input.InputfieldPasswordComplexify");
$inputs.each(function() {
var $input = $(this);
var $inputfield = $input.closest('.Inputfield');
@@ -146,4 +148,16 @@ jQuery(document).ready(function($) {
if($on) $on.addClass('on').siblings('.on').removeClass('on');
});
});
// accommodate issue where Firefox auto-populates remembered password when it shouldn't
var $ffinputs = $inputs.filter("[autocomplete='off']");
if($ffinputs.length) {
setTimeout(function() {
$ffinputs.each(function() {
if($(this).val().length < 1) return;
$(this).val('').trigger('keyup').change()
.closest('.Inputfield').removeClass('InputfieldStateChanged');
});
}, 1000);
}
});

View File

@@ -1 +1 @@
jQuery(document).ready(function(a){a("input.InputfieldPasswordComplexify").each(function(){var h=a(this);var b=h.closest(".Inputfield");var g=b.find(".InputfieldPasswordConfirm");var f=g.next(".pass-confirm");var i=h.siblings(".pass-scores");var c=h.siblings(".pass-percent");var j=i.children();var e=i.attr("data-requirements").split(" ");var d=parseInt(h.attr("minlength"));var k={banMode:h.attr("data-banMode"),strengthScaleFactor:parseFloat(h.attr("data-factor"))};h.complexify(k,function(l,m){var s=null;var q=h.val();var t=q.length;var v=0;if(t>0){for(var p=0;p<e.length;p++){var r=false;var x=e[p];var u=b.find(".pass-require-"+x);if(x=="letter"){var w=XRegExp("\\p{L}");if(!w.test(q)){r=true}}else{if(x=="upper"){var w=XRegExp("\\p{Lu}");if(!w.test(q)){r=true}}else{if(x=="lower"){var w=XRegExp("\\p{Ll}");if(!w.test(q)){r=true}}else{if(x=="digit"){var w=XRegExp("\\p{N}");if(!w.test(q)){r=true}}else{if(x=="other"){var w=XRegExp("\\p{P}");var o=XRegExp("\\p{S}");if(!w.test(q)&&!o.test(q)){r=true}}else{if(x=="space"){var w=XRegExp("\\p{Z}");if(!w.test(q)){r=true}}else{if(x=="minlength"){if(t<d){r=true}}}}}}}}if(r){u.removeClass("pass-require-good ui-priority-secondary")}else{u.addClass("pass-require-good ui-priority-secondary");v++}}}else{b.find(".pass-require-good").removeClass("pass-require-good ui-priority-secondary")}if(t==0){j.removeClass("on");return}else{if(v<e.length){s=j.filter(".pass-fail")}else{if(t<d){s=j.filter(".pass-short")}else{if(!l){s=j.filter(".pass-common")}else{if(m==0){s=j.filter(".pass-invalid")}else{if(m<50){s=j.filter(".pass-weak")}else{if(m<70){s=j.filter(".pass-medium")}else{if(m<100){s=j.filter(".pass-good")}else{if(m==100){s=j.filter(".pass-excellent")}}}}}}}}}if(s&&!s.hasClass("on")){s.siblings(".on").removeClass("on");s.addClass("on")}if(s.hasClass("pass-fail")||s.hasClass("pass-short")||s.hasClass("pass-common")||s.hasClass("pass-invalid")){g.attr("disabled","disabled").val("").change()}else{g.removeAttr("disabled");s.find("small").remove();s.append("<small style='margin-left:0.5em'>("+Math.floor(m)+"%)</small>")}if(g.val().length){g.change()}});h.on("change",function(){var l=a(this).val();if(l.length>0){h.attr("required","required");g.attr("required","required")}else{if(!a(this).closest(".InputfieldStateRequired").length){h.removeAttr("required");g.removeAttr("required")}}});g.on("keyup change",function(){var o=h.val();var n=a(this).val();var l=null;var m=h.closest("p").removeClass("pass-matches");if(n.length==0){l=f.children(".confirm-pending")}else{if(o==n){l=f.children(".confirm-yes");m.addClass("pass-matches")}else{if(o.indexOf(n)===0){l=f.children(".confirm-qty");l.children("span").html(n.length+"/"+o.length)}else{l=f.children(".confirm-no")}}}if(l){l.addClass("on").siblings(".on").removeClass("on")}})})});
jQuery(document).ready(function(c){var a=c("input.InputfieldPasswordComplexify");a.each(function(){var j=c(this);var d=j.closest(".Inputfield");var i=d.find(".InputfieldPasswordConfirm");var h=i.next(".pass-confirm");var k=j.siblings(".pass-scores");var e=j.siblings(".pass-percent");var l=k.children();var g=k.attr("data-requirements").split(" ");var f=parseInt(j.attr("minlength"));var m={banMode:j.attr("data-banMode"),strengthScaleFactor:parseFloat(j.attr("data-factor"))};j.complexify(m,function(o,p){var u=null;var s=j.val();var v=s.length;var x=0;if(v>0){for(var r=0;r<g.length;r++){var t=false;var z=g[r];var w=d.find(".pass-require-"+z);if(z=="letter"){var y=XRegExp("\\p{L}");if(!y.test(s)){t=true}}else{if(z=="upper"){var y=XRegExp("\\p{Lu}");if(!y.test(s)){t=true}}else{if(z=="lower"){var y=XRegExp("\\p{Ll}");if(!y.test(s)){t=true}}else{if(z=="digit"){var y=XRegExp("\\p{N}");if(!y.test(s)){t=true}}else{if(z=="other"){var y=XRegExp("\\p{P}");var q=XRegExp("\\p{S}");if(!y.test(s)&&!q.test(s)){t=true}}else{if(z=="space"){var y=XRegExp("\\p{Z}");if(!y.test(s)){t=true}}else{if(z=="minlength"){if(v<f){t=true}}}}}}}}if(t){w.removeClass("pass-require-good ui-priority-secondary")}else{w.addClass("pass-require-good ui-priority-secondary");x++}}}else{d.find(".pass-require-good").removeClass("pass-require-good ui-priority-secondary")}if(v==0){l.removeClass("on");return}else{if(x<g.length){u=l.filter(".pass-fail")}else{if(v<f){u=l.filter(".pass-short")}else{if(!o){u=l.filter(".pass-common")}else{if(p==0){u=l.filter(".pass-invalid")}else{if(p<50){u=l.filter(".pass-weak")}else{if(p<70){u=l.filter(".pass-medium")}else{if(p<100){u=l.filter(".pass-good")}else{if(p==100){u=l.filter(".pass-excellent")}}}}}}}}}if(u&&!u.hasClass("on")){u.siblings(".on").removeClass("on");u.addClass("on")}if(u.hasClass("pass-fail")||u.hasClass("pass-short")||u.hasClass("pass-common")||u.hasClass("pass-invalid")){i.attr("disabled","disabled").val("").change()}else{i.removeAttr("disabled");u.find("small").remove();u.append("<small style='margin-left:0.5em'>("+Math.floor(p)+"%)</small>")}if(i.val().length){i.change()}});j.on("change",function(){var n=c(this).val();if(n.length>0){j.attr("required","required");i.attr("required","required")}else{if(!c(this).closest(".InputfieldStateRequired").length){j.removeAttr("required");i.removeAttr("required")}}});i.on("keyup change",function(){var q=j.val();var p=c(this).val();var n=null;var o=j.closest("p").removeClass("pass-matches");if(p.length==0){n=h.children(".confirm-pending")}else{if(q==p){n=h.children(".confirm-yes");o.addClass("pass-matches")}else{if(q.indexOf(p)===0){n=h.children(".confirm-qty");n.children("span").html(p.length+"/"+q.length)}else{n=h.children(".confirm-no")}}}if(n){n.addClass("on").siblings(".on").removeClass("on")}})});var b=a.filter("[autocomplete='off']");if(b.length){setTimeout(function(){b.each(function(){if(c(this).val().length<1){return}c(this).val("").trigger("keyup").change().closest(".Inputfield").removeClass("InputfieldStateChanged")})},1000)}});

View File

@@ -2,6 +2,16 @@
/**
* An Inputfield for handling a password
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* https://processwire.com
*
* @property array $requirements Array of requirements (See require* constants)
* @property array $requirementsLabels Text labels used for requirements
* @property float $complexifyFactor Complexify factor
* @property string $complexifyBanMode Complexify ban mode (default='loose')
* @property bool $showPass Allow password to be rendered in renderValue and/or re-populated in form?
* @property string $defaultLabel Default label for field (default='Set Password'). Used when no 'label' has been set.
*
*/
class InputfieldPassword extends InputfieldText {
@@ -14,15 +24,49 @@ class InputfieldPassword extends InputfieldText {
'permanent' => true,
);
}
/**
* Requirements: letter required
*
*/
const requireLetter = 'letter';
const requireLetter = 'letter';
/**
* Requirements: lowercase letter required
*
*/
const requireLowerLetter = 'lower';
const requireUpperLetter = 'upper';
/**
* Requirements: uppercase letter required
*
*/
const requireUpperLetter = 'upper';
/**
* Requirements: digit required
*
*/
const requireDigit = 'digit';
/**
* Requirements: other character (symbol) required
*
*/
const requireOther = 'other';
/**
* Page being edited, when applicable
*
* @var User|Page|null
*
*/
protected $_page = null;
/**
* Construct and establish default settings
*
*/
public function __construct() {
parent::__construct();
$this->attr('type', 'password');
@@ -41,8 +85,11 @@ class InputfieldPassword extends InputfieldText {
));
$this->set('showPass', false); // allow password to be rendered in renderValue and/or re-populated in form?
}
/**
* Init Inputfield, establishing the label if none has been set
*
*/
public function init() {
parent::init();
$this->set('defaultLabel', $this->_('Set Password'));
@@ -60,6 +107,15 @@ class InputfieldPassword extends InputfieldText {
if($page->hasStatus(Page::statusUnpublished)) $this->required = true;
}
/**
* Called before render
*
* @param Inputfield $parent
* @param bool $renderValueMode
* @return bool
* @throws WireException
*
*/
public function renderReady(Inputfield $parent = null, $renderValueMode = false) {
if($this->label == 'Set Password') $this->label = $this->defaultLabel;
$config = $this->wire('config');
@@ -67,9 +123,17 @@ class InputfieldPassword extends InputfieldText {
$config->scripts->add($url . 'jquery.complexify.min.js');
$config->scripts->add($url . 'jquery.complexify.banlist.js');
$this->wire('modules')->get('JqueryCore')->use('xregexp');
$page = $this->wire('page');
if($page && $page->template == 'admin') $this->attr('autocomplete', 'off'); // ProcessProfile and ProcessUser
return parent::renderReady($parent, $renderValueMode);
}
/**
* Render Password input(s)
*
* @return string
*
*/
public function ___render() {
$description = $this->getSetting('description');
@@ -109,8 +173,7 @@ class InputfieldPassword extends InputfieldText {
$this->attr('data-banMode', $this->complexifyBanMode ? $this->complexifyBanMode : 'loose');
$this->attr('data-factor', $this->complexifyFactor ? $this->complexifyFactor : '0.7');
$this->addClass('InputfieldPasswordComplexify');
$matchYes = $this->_('Matches');
$matchNo = $this->_('Does not match');
$failIcon = "<i class='fa fa-fw fa-frown-o'></i>";
$okIcon = "<i class='fa fa-fw fa-meh-o'></i>";
$goodIcon = "<i class='fa fa-fw fa-smile-o'></i>";
@@ -141,9 +204,18 @@ class InputfieldPassword extends InputfieldText {
$this->attr('value', $value);
if($trackChanges) $this->setTrackChanges(true);
return $out;
}
/**
* Set Inputfield setting
*
* @param string $key
* @param mixed $value
* @return $this
*
*/
public function set($key, $value) {
if($key == 'collapsed' && $this->_page && $this->_page->hasStatus(Page::statusUnpublished)) {
// prevent collapse of field when pass is for unpublished user
@@ -152,6 +224,12 @@ class InputfieldPassword extends InputfieldText {
return parent::set($key, $value);
}
/**
* Render non-editable Inputfield
*
* @return string
*
*/
public function ___renderValue() {
if(!$this->getSetting('showPass')) {
$value = strlen($this->attr('value')) ? '******' : '';
@@ -162,12 +240,20 @@ class InputfieldPassword extends InputfieldText {
return $value;
}
/**
* Process input
*
* @param WireInputData $input
* @return $this
*
*/
public function ___processInput(WireInputData $input) {
parent::___processInput($input);
$key = $this->attr('name');
$value = $this->attr('value');
if($value) {}
if(isset($input->$key)) {
// form was submitted
@@ -262,6 +348,8 @@ class InputfieldPassword extends InputfieldText {
/**
* Return the fields required to configure an instance of InputfieldPassword
*
* @return InputfieldWrapper
*
*/
public function ___getConfigInputfields() {

View File

@@ -10,6 +10,8 @@
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* https://processwire.com
*
* @method upgrade($fromVersion, $toVersion)
*
*
*/
@@ -172,6 +174,7 @@ class PagePathHistory extends WireData implements Module {
try {
$query->execute();
/** @noinspection PhpAssignmentInConditionInspection */
while($row = $query->fetch(\PDO::FETCH_NUM)) {
$paths[] = $this->wire('sanitizer')->pagePathName($row[0], Sanitizer::toUTF8);
}
@@ -184,10 +187,13 @@ class PagePathHistory extends WireData implements Module {
/**
* Hook called when a page is moved or renamed
*
* @param HookEvent $event
*
*/
public function hookPageMoved(HookEvent $event) {
/** @var Page $page */
$page = $event->arguments[0];
if($page->template == 'admin') return;
$age = time() - $page->created;
@@ -200,12 +206,13 @@ class PagePathHistory extends WireData implements Module {
$parentPrevious = $page->parentPrevious;
if($parentPrevious && $parentPrevious->id == $page->parent()->id) $parentPrevious = null;
foreach($languages as $language) {
/** @var Language $language */
if($language->isDefault()) continue;
$namePrevious = $page->get("-name$language");
if(!$namePrevious && !$parentPrevious) continue;
if(!$namePrevious) $namePrevious = $page->name;
$languages->setLanguage($language);
$pathPrevious = $parentPrevious ? $parentPrevious->path() : $page->parent()->path;
$pathPrevious = $parentPrevious ? $parentPrevious->path() : $page->parent()->path();
$pathPrevious = rtrim($pathPrevious, '/') . "/$namePrevious";
$this->setPathHistory($page, $pathPrevious, $language->id);
$languages->unsetLanguage();
@@ -243,6 +250,8 @@ class PagePathHistory extends WireData implements Module {
/**
* Hook called upon 404 from ProcessPageView::pageNotFound
*
* @param HookEvent $event
*
*/
public function hookPageNotFound(HookEvent $event) {
@@ -370,6 +379,8 @@ class PagePathHistory extends WireData implements Module {
/**
* When a page is deleted, remove it from our redirects list as well
*
* @param HookEvent $event
*
*/
public function hookPageDeleted(HookEvent $event) {
@@ -380,10 +391,16 @@ class PagePathHistory extends WireData implements Module {
$query->execute();
}
/**
* Install
*
*/
public function ___install() {
$len = $this->wire('database')->getMaxIndexLength();
$sql = "CREATE TABLE " . self::dbTableName . " (" .
"path VARCHAR(250) NOT NULL, " .
"path VARCHAR($len) NOT NULL, " .
"pages_id INT UNSIGNED NOT NULL, " .
"language_id INT UNSIGNED DEFAULT 0, " . // v2
"created TIMESTAMP NOT NULL, " .
@@ -396,6 +413,10 @@ class PagePathHistory extends WireData implements Module {
}
/**
* Uninstall
*
*/
public function ___uninstall() {
$this->wire('database')->query("DROP TABLE " . self::dbTableName);
}

View File

@@ -1040,7 +1040,7 @@ class ProcessField extends Process implements ConfigurableModule {
//$form->head = $this->_('Basic field settings');
$field = $this->modules->get('InputfieldName');
$field->attr('value', $this->field->name);
$field->description = $this->_("Any combination of ASCII letters [a-z], numbers [0-9], or underscores (no dashes or spaces).");
$field->description = $this->_("Use only ASCII letters (a-z A-Z), numbers (0-9) or underscores.");
$form->add($field);
$field = $this->modules->get('InputfieldSelect');

View File

@@ -1399,7 +1399,9 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod
}
$submitAction = $this->wire('input')->post('_after_submit_action');
if($submitAction == 'exit') {
if($this->redirectUrl) {
// non-default redirectUrl overrides after_submit_action
} else if($submitAction == 'exit') {
$this->redirectUrl = '../';
} else if($submitAction == 'view') {
$this->redirectUrl = $this->page->httpUrl();
@@ -1791,9 +1793,10 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod
*
*/
public function ___executeTemplate() {
if(!$this->useSettings || !$this->user->hasPermission('page-template', $this->page))
throw new WireException("You don't have permission to change the template on this page.");
if(!$this->useSettings || !$this->user->hasPermission('page-template', $this->page)) {
throw new WireException("You don't have permission to change the template on this page.");
}
if(!isset($_GET['template'])) throw new WireException("This method requires a 'template' get var");
$template = $this->templates->get((int) $_GET['template']);
@@ -1802,25 +1805,32 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod
if(!$this->isAllowedTemplate($template->id)) {
throw new WireException("That template is not allowed");
}
$labelConfirm = $this->_('Confirm template change'); // Change template confirmation subhead
$labelAction = sprintf($this->_('Change template from "%1$s" to "%2$s"'), $this->page->template, $template); // Change template A to B headline
$this->headline($labelConfirm);
/** @var InputfieldForm $form */
$form = $this->modules->get("InputfieldForm");
$form->attr('action', 'saveTemplate');
$form->attr('method', 'post');
$form->description = sprintf($this->_('Change template from "%1$s" to "%2$s"'), $this->page->template, $template); // Change template A to B headline
$form->description = $labelAction;
/** @var InputfieldMarkup $f */
$f = $this->modules->get("InputfieldMarkup");
$f->icon = 'cubes';
$f->label = $this->_('Confirm template change'); // Change template confirmation subhead
$list = '';
$f->label = $labelConfirm;
$list = array();
foreach($this->page->template->fieldgroup as $field) {
if(!$template->fieldgroup->has($field))
$list .= "<li class='ui-state-error-text'> <i class='icon-times-circle'></i> $field</li>";
if(!$template->fieldgroup->has($field)) {
$list[] = $this->wire('sanitizer')->entities($field->getLabel()) . " ($field->name)";
}
}
if(!$list) $this->executeSaveTemplate($template);
$f->description = $this->_('Warning, changing the template will delete the following fields:'); // Headline that precedes list of fields that will be deleted as a result of template change
$f->attr('value', "<ul>$list</ul>");
$icon = "<i class='fa fa-times-circle'></i> ";
$f->attr('value', "<p class='ui-state-error-text'>$icon" . implode("<br />$icon", $list) . '</p>');
$form->append($f);
/** @var InputfieldCheckbox $f */
@@ -1828,6 +1838,8 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod
$f->attr('name', 'template');
$f->attr('value', $template->id);
$f->label = $this->_('Are you sure?'); // Checkbox label to confirm they want to change template
$f->label2 = $labelAction;
$f->icon = 'warning';
$f->description = $this->_('Please confirm that you understand the above by clicking the checkbox below.'); // Checkbox description to confirm they want to change template
$form->append($f);

View File

@@ -32,6 +32,7 @@ class ProcessPageEditImageSelect extends Process implements ConfigurableModule {
protected $maxImageWidth = 835;
protected $page = null;
protected $masterPage = null;
protected $editorPage = null;
protected $defaultClass = 'Pageimage';
protected $file = '';
@@ -167,7 +168,18 @@ class ProcessPageEditImageSelect extends Process implements ConfigurableModule {
if(!$this->page) $this->page = $this->pages->get($id);
if(!$this->page) throw new WireException("No page specified");
if(!$this->page->id) throw new WireException("Unknown page");
if(!$this->editorPage) $this->editorPage = $this->page;
if(!$this->editorPage) $this->editorPage = $this->page;
// if $this->page is a repeater item (for example), $this->masterPage is page it lives on
$p = null;
if(strpos($this->page->className(), 'Repeater') !== false) {
/** @var RepeaterPage $p */
$p = $this->page;
while(strpos($p->className(), 'Repeater') !== false) {
$p = $p->getForPage();
}
}
$this->masterPage = $p && $p->id ? $p : $this->page;
// note we use hasPermission('page-view') rather than viewable() here because
// we want to allow pages without template files
@@ -177,14 +189,7 @@ class ProcessPageEditImageSelect extends Process implements ConfigurableModule {
&& $this->wire('modules')->get('PagePermissions')->userFieldEditable($fieldName)) {
// user editing allowed images field in their profile
} else if(strpos($this->page->className(), 'Repeater') !== false) {
/** @var RepeaterPage $p */
$p = $this->page;
while(strpos($p->className(), 'Repeater') !== false) {
$p = $p->getForPage();
}
if($p && $p->id && $p->editable()) {
// okay
} else {
if(!$this->masterPage->editable()) {
throw new WireException($this->labels['noAccess']);
}
} else {
@@ -554,7 +559,7 @@ class ProcessPageEditImageSelect extends Process implements ConfigurableModule {
*
*/
public function checkImageEditPermission($throw = true) {
if(!$this->rte && !$this->wire('user')->hasPermission('page-edit-images', $this->page)) {
if(!$this->rte && !$this->wire('user')->hasPermission('page-edit-images', $this->masterPage)) {
if($throw) {
throw new WirePermissionException($this->labels['noAccess']);
} else {

View File

@@ -17,7 +17,7 @@ class ProcessProfile extends Process implements ConfigurableModule, WirePageEdit
return array(
'title' => __('User Profile', __FILE__), // getModuleInfo title
'summary' => __('Enables user to change their password, email address and other settings that you define.', __FILE__), // getModuleInfo summary
'version' => 102,
'version' => 103,
'permanent' => true,
'permission' => 'profile-edit',
);
@@ -27,16 +27,23 @@ class ProcessProfile extends Process implements ConfigurableModule, WirePageEdit
* @var User
*
*/
protected $user;
protected $user;
/**
* Construct/establish initial module configuration
*
*/
public function __construct() {
$this->set('profileFields', array());
}
public function init() {
parent::init();
}
/**
* Execute/render profile edit form
*
* @return string
* @throws WireException
*
*/
public function ___execute() {
$fieldName = '';
@@ -123,8 +130,11 @@ class ProcessProfile extends Process implements ConfigurableModule, WirePageEdit
*
*/
protected function processInput(Inputfield $form, $fieldName = '') {
/** @var InputfieldForm $form */
$user = $this->user;
$languages = $this->wire('languages');
$form->processInput($this->input->post);
if(count($form->getErrors())) {
@@ -141,7 +151,7 @@ class ProcessProfile extends Process implements ConfigurableModule, WirePageEdit
if($fieldName && $field->name !== $fieldName) continue;
$field = $this->user->fields->getFieldContext($field);
$inputfield = $form->get($field->name);
$inputfield = $form->getChildByName($field->name);
$value = $inputfield->attr('value');
if($field->name == 'email' && strlen($value)) {
@@ -152,9 +162,20 @@ class ProcessProfile extends Process implements ConfigurableModule, WirePageEdit
}
if($field->name == 'pass' && empty($value)) continue;
if($user->get($field->name) !== $value) {
$user->set($field->name, $value);
if($inputfield->isChanged()) {
$v = $user->get($field->name);
if($languages && $inputfield->getSetting('useLanguages')) {
if(is_object($v)) {
$v->setFromInputfield($inputfield);
$user->set($field->name, $v);
$user->trackChange($field->name);
} else {
$user->set($field->name, $value);
}
} else {
$user->set($field->name, $value);
}
}
}
@@ -171,6 +192,14 @@ class ProcessProfile extends Process implements ConfigurableModule, WirePageEdit
}
/**
* Module configuration
*
* @param array $data
* @return InputfieldWrapper
* @throws WireException
*
*/
public function getModuleConfigInputfields(array $data) {
$profileFields = isset($data['profileFields']) ? $data['profileFields'] : array();

View File

@@ -33,17 +33,4 @@ $(document).ready(function() {
});
}
});
// from @horst-n #1236:
// prevent browser supported autocomplete for password fields (e.g. on Profilepage)
// to force this, attribute autocomplete='off' needs to be set for the password field
// this fix is only needed in Mozilla Firefox apparently
if($(".FieldtypePassword[autocomplete='off']").length) {
// simply set the value empty on document.ready doesn't work in FireFox,
// but one second later, it works :)
setTimeout(function() {
$(".FieldtypePassword[autocomplete='off']").attr('value', '')
.closest('.Inputfield').removeClass('InputfieldStateChanged'); // @GerardLuskin
}, 1000);
}
});

View File

@@ -1 +1 @@
$(document).ready(function(){if($("#Inputfield_id").val()==40){$("#wrap_Inputfield_pass").hide();$("#wrap_Inputfield_email").hide();$("#wrap_Inputfield_roles input").attr("disabled","disabled")}var a=$("#Inputfield_roles_37");if(a.size()>0&&!a.is(":checked")){a.attr("checked","checked")}$("#wrap_Inputfield_roles").find("input[type=checkbox]").each(function(){if($.inArray(parseInt($(this).val()),ProcessWire.config.ProcessUser.editableRoles)==-1){$(this).closest("label").addClass("ui-priority-secondary").click(function(){var b=$(this).find(".ui-state-error-text");if(b.length==0){b=$("<span class='ui-state-error-text'>&nbsp;("+ProcessWire.config.ProcessUser.notEditableAlert+")</span>");$(this).append(b);setTimeout(function(){b.fadeOut("normal",function(){b.remove()})},2000)}else{b.remove()}return false})}});if($(".FieldtypePassword[autocomplete='off']").length){setTimeout(function(){$(".FieldtypePassword[autocomplete='off']").attr("value","").closest(".Inputfield").removeClass("InputfieldStateChanged")},1000)}});
$(document).ready(function(){if($("#Inputfield_id").val()==40){$("#wrap_Inputfield_pass").hide();$("#wrap_Inputfield_email").hide();$("#wrap_Inputfield_roles input").attr("disabled","disabled")}var a=$("#Inputfield_roles_37");if(a.size()>0&&!a.is(":checked")){a.attr("checked","checked")}$("#wrap_Inputfield_roles").find("input[type=checkbox]").each(function(){if($.inArray(parseInt($(this).val()),ProcessWire.config.ProcessUser.editableRoles)==-1){$(this).closest("label").addClass("ui-priority-secondary").click(function(){var b=$(this).find(".ui-state-error-text");if(b.length==0){b=$("<span class='ui-state-error-text'>&nbsp;("+ProcessWire.config.ProcessUser.notEditableAlert+")</span>");$(this).append(b);setTimeout(function(){b.fadeOut("normal",function(){b.remove()})},2000)}else{b.remove()}return false})}})});