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

Add a new Templates::fileModified() hookable method that is called whenever a change is detected to a template file. Plus update the Template and Templates class to make it possible for runtime modification of the templates path.

This commit is contained in:
Ryan Cramer
2019-09-20 10:46:47 -04:00
parent 7a76f4b857
commit 710c222b5a
5 changed files with 95 additions and 37 deletions

View File

@@ -140,7 +140,7 @@ class Paths extends WireData {
*
*/
public function set($key, $value) {
$value = self::normalizeSeparators($value);
if(DIRECTORY_SEPARATOR != '/') $value = self::normalizeSeparators($value);
if($key == 'root') {
$this->_root = $value;
return $this;
@@ -159,7 +159,7 @@ class Paths extends WireData {
*/
public function get($key) {
static $_http = null;
if($key == 'root') return $this->_root;
if($key === 'root') return $this->_root;
$http = '';
if(is_object($key)) {
$key = "$key";
@@ -174,7 +174,7 @@ class Paths extends WireData {
$key = substr($key, 4);
$key[0] = strtolower($key[0]);
}
if($key == 'root') {
if($key === 'root') {
$value = $http . $this->_root;
} else {
$value = parent::get($key);

View File

@@ -424,7 +424,7 @@ class ProcessWire extends Wire {
$fieldtypes = $this->wire('fieldtypes', new Fieldtypes(), true);
$fields = $this->wire('fields', new Fields(), true);
$fieldgroups = $this->wire('fieldgroups', new Fieldgroups(), true);
$templates = $this->wire('templates', new Templates($fieldgroups, $config->paths->templates), true);
$templates = $this->wire('templates', new Templates($fieldgroups), true);
$pages = $this->wire('pages', new Pages($this), true);
$this->initVar('fieldtypes', $fieldtypes);

View File

@@ -792,9 +792,11 @@ class Template extends WireData implements Saveable, Exportable {
if(empty($value)) return;
if(strpos($value, '/') === false) {
// value is basename
$value = $this->config->paths->templates . $value;
} else if(strpos($value, $this->config->paths->root) !== 0) {
// value is path outside of our installation root, which we do not accept
$value = $this->config->paths->templates . basename($value);
}
@@ -878,35 +880,52 @@ class Template extends WireData implements Saveable, Exportable {
*/
public function filename() {
if($this->filename) return $this->filename;
/** @var Config $config */
$config = $this->wire('config');
$path = $config->paths->templates;
$ext = '.' . $config->templateExtension;
$altFilename = $this->altFilename;
if(!$this->settings['name']) throw new WireException("Template must be assigned a name before 'filename' can be accessed");
if(!$this->settings['name']) {
throw new WireException("Template must be assigned a name before 'filename' can be accessed");
}
if($this->altFilename) {
$altFilename = $this->wire('templates')->path . basename($this->altFilename, "." . $this->config->templateExtension) . "." . $this->config->templateExtension;
$this->filename = $altFilename;
if($altFilename) {
$filename = $path . basename($altFilename, $ext) . $ext;
} else {
$this->filename = $this->wire('templates')->path . $this->settings['name'] . '.' . $this->config->templateExtension;
$filename = $path . $this->settings['name'] . $ext;
}
if($filename !== $this->filename) {
// first set of filename, or filename/path has been changed
$this->filenameExists = null;
$this->filename = $filename;
}
$isModified = false;
$fileExists = $this->filenameExists();
if($fileExists) {
$modified = filemtime($this->filename);
if($modified > $this->modified) {
$isModified = true;
$this->modified = $modified;
}
if($isModified || !$this->ns) {
// determine namespace
$this->ns = $this->wire('files')->getNamespace($this->filename);
// tell it to save the template after the request is finished
$this->addHookAfter('ProcessWire::finished', $this, 'hookFinished');
if($this->filenameExists === null) {
$this->filenameExists = file_exists($filename);
if($this->filenameExists) {
// if filename exists, keep track of last modification time
$isModified = false;
$modified = filemtime($filename);
if($modified > $this->modified) {
$isModified = true;
$this->modified = $modified;
}
if($isModified || !$this->ns) {
// determine namespace
$files = $this->wire('files');
/** @var WireFileTools $files */
$templates = $this->wire('templates');
/** @var Templates $templates */
$this->ns = $files->getNamespace($filename);
$templates->fileModified($this);
}
}
}
return $this->filename;
return $filename;
}
/**
@@ -928,11 +947,11 @@ class Template extends WireData implements Saveable, Exportable {
*
* #pw-group-files
*
* @return string
* @return bool
*
*/
public function filenameExists() {
if(!is_null($this->filenameExists)) return $this->filenameExists;
if($this->filenameExists !== null) return $this->filenameExists;
$this->filenameExists = file_exists($this->filename());
return $this->filenameExists;
}

View File

@@ -5,7 +5,7 @@
*
* Manages and provides access to all the Template instances
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2019 by Ryan Cramer
* https://processwire.com
*
* #pw-summary Manages and provides access to all the Templates.
@@ -16,6 +16,7 @@
* @method bool|Saveable|Template clone(Saveable $item, $name = '') #pw-internal
* @method array getExportData(Template $template) Export Template data for external use. #pw-advanced
* @method array setImportData(Template $template, array $data) Given an array of Template export data, import it to the given Template. #pw-advanced
* @method void fileModified(Template $template) Hook called when a template detects that its file has been modified. #pw-hooker
*
*/
class Templates extends WireSaveableItems {
@@ -30,26 +31,26 @@ class Templates extends WireSaveableItems {
* WireArray of all Template instances
*
*/
protected $templatesArray;
protected $templatesArray;
/**
* Path where Template files are stored
* Templates that had changed files during this request
*
* @var array Array of Template objects indexed by id
*
*/
protected $path;
protected $fileModTemplates = array();
/**
* Construct the Templates
*
* @param Fieldgroups $fieldgroups Reference to the Fieldgroups
* @param string $path Path to where template files are stored
*
*/
public function __construct(Fieldgroups $fieldgroups, $path) {
public function __construct(Fieldgroups $fieldgroups) {
$fieldgroups->wire($this);
$this->fieldgroups = $fieldgroups;
$this->templatesArray = $this->wire(new TemplatesArray());
$this->path = $path;
}
/**
@@ -113,7 +114,7 @@ class Templates extends WireSaveableItems {
*
*/
public function get($key) {
if($key == 'path') return $this->path;
if($key == 'path') return $this->wire('config')->paths->templates;
$value = $this->templatesArray->get($key);
if(is_null($value)) $value = parent::get($key);
return $value;
@@ -652,6 +653,44 @@ class Templates extends WireSaveableItems {
return $updated;
}
/**
* Hook called when a Template detects that its file has changed
*
* Note that the hook is not called until something in the system (like a page render) asks for the templates filename.
* Thats because it would not be efficient for PW to check the file for every template in the system on every request.
*
* #pw-hooker
*
* @param Template $template
* @since 3.0.141
*
*/
public function ___fileModified(Template $template) {
if(empty($this->fileModTemplates)) {
// add hook on first call
$this->addHookAfter('ProcessWire::finished', $this, '_hookFinished');
}
$this->fileModTemplates[$template->id] = $template;
}
/**
* Saves templates that had modified files to update 'modified' and 'ns' properties after the request is complete
*
* #pw-internal
*
* @param HookEvent $e
* @since 3.0.141
*
*/
public function _hookFinished(HookEvent $e) {
if($e) {}
foreach($this->fileModTemplates as $id => $template) {
if($template->isChanged('modified') || $template->isChanged('ns')) {
$template->save();
}
}
$this->fileModTemplates = array();
}
/**
* FUTURE USE: Is the parent/child relationship allowed?

View File

@@ -1682,7 +1682,7 @@ abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable {
*/
public function wire($name = '', $value = null, $lock = false) {
if(is_null($this->_wire)) {
if($this->_wire === null) {
// 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();