mirror of
https://github.com/processwire/processwire.git
synced 2025-08-15 11:14:12 +02:00
Add PagesVersions module to core, which provides an API for managing page versions
This commit is contained in:
186
wire/modules/Pages/PagesVersions/PageVersionInfo.php
Normal file
186
wire/modules/Pages/PagesVersions/PageVersionInfo.php
Normal file
@@ -0,0 +1,186 @@
|
||||
<?php namespace ProcessWire;
|
||||
|
||||
/**
|
||||
* Page Version Info
|
||||
*
|
||||
* For pages that are a version, this class represents
|
||||
* the `_version` property of the page.
|
||||
*
|
||||
* ProcessWire 3.x, Copyright 2023 by Ryan Cramer
|
||||
* https://processwire.com
|
||||
*
|
||||
* @property int $version
|
||||
* @property string $description
|
||||
* @property int $created
|
||||
* @property int $modified
|
||||
* @property int $pages_id
|
||||
* @property Page $page
|
||||
* @property int $created_users_id
|
||||
* @property int $modified_users_id
|
||||
* @property-read User|NullPage $createdUser
|
||||
* @property-read User|NullPage $modifiedUser
|
||||
* @property-read string $createdStr
|
||||
* @property-read string $modifiedStr
|
||||
* @property string $action
|
||||
*
|
||||
*/
|
||||
class PageVersionInfo extends WireData {
|
||||
|
||||
/**
|
||||
* Value for $action property when restoring a page
|
||||
*
|
||||
*/
|
||||
const actionRestore = 'restore';
|
||||
|
||||
/**
|
||||
* @var Page
|
||||
*
|
||||
*/
|
||||
protected $page;
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
*/
|
||||
public function __construct(array $data = []) {
|
||||
parent::__construct();
|
||||
$defaults = [
|
||||
'version' => 0,
|
||||
'description' => '',
|
||||
'created' => 0,
|
||||
'modified' => 0,
|
||||
'created_users_id' => 0,
|
||||
'modified_users_id' => 0,
|
||||
'pages_id' => 0,
|
||||
'action' => '',
|
||||
];
|
||||
parent::setArray(array_merge($defaults, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set property
|
||||
*
|
||||
* @param string $key
|
||||
* @param string|int|Page $value
|
||||
* @return self
|
||||
*
|
||||
*/
|
||||
public function set($key, $value) {
|
||||
if($key === 'version' || $key === 'pages_id') {
|
||||
$value = (int) $value;
|
||||
} else if($key === 'created' || $key === 'modified') {
|
||||
if($value) {
|
||||
$value = ctype_digit("$value") ? (int) $value : strtotime($value);
|
||||
} else {
|
||||
$value = 0;
|
||||
}
|
||||
} else if($key === 'created_users_id' || $key === 'modified_users_id') {
|
||||
$value = (int) $value;
|
||||
} else if($key === 'page') {
|
||||
$this->setPage($value);
|
||||
} else if($key === 'description') {
|
||||
$value = (string) $value;
|
||||
}
|
||||
return parent::set($key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get property
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed|NullPage|Page|User|null
|
||||
*
|
||||
*/
|
||||
public function get($key) {
|
||||
switch($key) {
|
||||
case 'page': return $this->getPage();
|
||||
case 'createdUser': return $this->getCreatedUser();
|
||||
case 'modifiedUser': return $this->getModifiedUser();
|
||||
case 'createdStr': return $this->created > 0 ? date('Y-m-d H:i:s', $this->created) : '';
|
||||
case 'modifiedStr': return $this->modified > 0 ? date('Y-m-d H:i:s', $this->modified) : '';
|
||||
}
|
||||
return parent::get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get page that this version is for
|
||||
*
|
||||
* @return NullPage|Page
|
||||
*
|
||||
*/
|
||||
public function getPage() {
|
||||
if($this->page) return $this->page;
|
||||
if($this->pages_id) {
|
||||
$this->page = $this->wire()->pages->get($this->pages_id);
|
||||
return $this->page;
|
||||
}
|
||||
return new NullPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set page that this version is for
|
||||
*
|
||||
* @param Page $page
|
||||
*
|
||||
*/
|
||||
public function setPage(Page $page) {
|
||||
$this->page = $page;
|
||||
if($page->id) parent::set('pages_id', $page->id);
|
||||
$page->wire($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user that created this version
|
||||
*
|
||||
* @return NullPage|User
|
||||
*
|
||||
*/
|
||||
public function getCreatedUser() {
|
||||
return $this->wire()->users->get($this->created_users_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user that last modified this version
|
||||
*
|
||||
* @return NullPage|User
|
||||
*
|
||||
*/
|
||||
public function getModifiedUser() {
|
||||
$id = $this->modified_users_id;
|
||||
return $id ? $this->wire()->users->get($id) : $this->getCreatedUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set action for PagesVersions
|
||||
*
|
||||
* #pw-internal
|
||||
*
|
||||
* @param string $action
|
||||
*
|
||||
*/
|
||||
public function setAction($action) {
|
||||
parent::set('action', $action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get action for PagesVersions
|
||||
*
|
||||
* #pw-internal
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
public function getAction() {
|
||||
return parent::get('action');
|
||||
}
|
||||
|
||||
/**
|
||||
* String value is version number as a string
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
public function __toString() {
|
||||
return (string) $this->version;
|
||||
}
|
||||
}
|
1427
wire/modules/Pages/PagesVersions/PagesVersions.module.php
Normal file
1427
wire/modules/Pages/PagesVersions/PagesVersions.module.php
Normal file
File diff suppressed because it is too large
Load Diff
227
wire/modules/Pages/PagesVersions/PagesVersionsFiles.php
Normal file
227
wire/modules/Pages/PagesVersions/PagesVersionsFiles.php
Normal file
@@ -0,0 +1,227 @@
|
||||
<?php namespace ProcessWire;
|
||||
|
||||
/**
|
||||
* File management for PagesVersions
|
||||
*
|
||||
* ProcessWire 3.x, Copyright 2023 by Ryan Cramer
|
||||
* https://processwire.com
|
||||
*
|
||||
*/
|
||||
class PagesVersionsFiles extends Wire {
|
||||
|
||||
const dirPrefix = 'v'; // directory prefix for version files
|
||||
|
||||
/**
|
||||
* @var PagesVersions
|
||||
*
|
||||
*/
|
||||
protected $pagesVersions;
|
||||
|
||||
/**
|
||||
* Are file hooks disabled?
|
||||
*
|
||||
* @var bool
|
||||
*
|
||||
*/
|
||||
protected $disableFileHooks = false;
|
||||
|
||||
/**
|
||||
* Value for last mkdir() call
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
*/
|
||||
protected $lastMkdir = '';
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
*/
|
||||
public function __construct(PagesVersions $pagesVersions) {
|
||||
$this->pagesVersions = $pagesVersions;
|
||||
parent::__construct();
|
||||
$pagesVersions->wire($this);
|
||||
$this->addHookAfter('PagefilesManager::path, PagefilesManager::url', $this, 'hookPagefilesManagerPath');
|
||||
$this->addHookAfter('Pages::savePageOrFieldReady', $this, 'hookPagesSaveReady');
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
* API SUPPORT METHODS
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copy files for given $page into version directory
|
||||
*
|
||||
* @param Page $page
|
||||
* @param $version
|
||||
* @return bool|int
|
||||
*
|
||||
*/
|
||||
public function copyPageVersionFiles(Page $page, $version) {
|
||||
|
||||
if(!$page->hasFilesPath()) return 0;
|
||||
|
||||
$files = $this->wire()->files;
|
||||
$filesManager = $page->filesManager();
|
||||
|
||||
$sourcePath = $filesManager->path();
|
||||
$targetPath = $this->versionFilesPath($filesManager->___path(), $version);
|
||||
|
||||
if($sourcePath === $targetPath) {
|
||||
// skipping copy
|
||||
$qty = 0;
|
||||
} else {
|
||||
$qty = $files->copy($sourcePath, $targetPath, [ 'recursive' => false ]);
|
||||
}
|
||||
|
||||
return $qty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete files for given version
|
||||
*
|
||||
* @param Page $page
|
||||
* @param int $version
|
||||
* @return bool
|
||||
*
|
||||
*/
|
||||
public function deletePageVersionFiles(Page $page, $version) {
|
||||
if(!$page->hasFilesPath()) return true;
|
||||
$path = $this->versionFilesPath($page->filesManager()->___path(), $version);
|
||||
if(!is_dir($path)) return true;
|
||||
return $this->wire()->files->rmdir($path, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore files from version into live $page
|
||||
*
|
||||
* @param Page $page
|
||||
* @param int $version
|
||||
* @return int
|
||||
*
|
||||
*/
|
||||
public function restorePageVersionFiles(Page $page, $version) {
|
||||
if(!$page->hasFilesPath()) return 0;
|
||||
$filesManager = $page->filesManager();
|
||||
$livePath = $filesManager->___path();
|
||||
$versionPath = $this->versionFilesPath($livePath, $version);
|
||||
if(!is_dir($versionPath)) return 0;
|
||||
$this->disableFileHooks = true;
|
||||
$filesManager->emptyPath(false, false);
|
||||
$qty = $filesManager->importFiles($versionPath);
|
||||
$this->disableFileHooks = false;
|
||||
return $qty;
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
* UTILITIES
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Update given files path for version
|
||||
*
|
||||
* #pw-internal
|
||||
*
|
||||
* @param string $path
|
||||
* @param int $version
|
||||
* @return string
|
||||
*
|
||||
*/
|
||||
public function versionFilesPath($path, $version) {
|
||||
if($path instanceof Page) $path = $path->filesManager()->___path();
|
||||
$version = (int) $version;
|
||||
return $path . self::dirPrefix . $version . '/';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the total size of all files in given version
|
||||
*
|
||||
* #pw-internal
|
||||
*
|
||||
* @param Page $page
|
||||
* @param int $version
|
||||
* @return int
|
||||
*
|
||||
*/
|
||||
public function getTotalVersionSize(Page $page, $version = 0) {
|
||||
if(!$page->hasFilesPath()) return 0;
|
||||
if(!$version) $version = $this->pagesVersions->pageVersionNumber($page);
|
||||
if($version) {
|
||||
$path = $this->versionFilesPath($page, $version);
|
||||
} else {
|
||||
$path = $page->filesPath();
|
||||
}
|
||||
$size = 0;
|
||||
foreach(new \DirectoryIterator($path) as $file) {
|
||||
if($file->isDir() || $file->isDot()) continue;
|
||||
$size += $file->getSize();
|
||||
}
|
||||
return $size;
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
* HOOKS
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Hook to PagefilesManager::path to update for version directories
|
||||
*
|
||||
* #pw-internal
|
||||
*
|
||||
* @param HookEvent $event
|
||||
*
|
||||
*/
|
||||
public function hookPagefilesManagerPath(HookEvent $event) {
|
||||
if($this->disableFileHooks) return;
|
||||
$manager = $event->object; /** @var PagefilesManager $manager */
|
||||
$page = $manager->page;
|
||||
if(!$page->get(PagesVersions::pageProperty)) return;
|
||||
$version = $this->pagesVersions->pageVersionNumber($page);
|
||||
if(!$version) return;
|
||||
$versionDir = self::dirPrefix . "$version/";
|
||||
$event->return .= $versionDir;
|
||||
if($event->method == 'path') {
|
||||
$path = $event->return;
|
||||
if($this->lastMkdir != $path && !is_dir($path)) {
|
||||
$this->wire()->files->mkdir($path, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook Pages::saveReady to restore version files when $action === 'restore'
|
||||
*
|
||||
* #pw-internal
|
||||
*
|
||||
* @param HookEvent $event
|
||||
*
|
||||
*/
|
||||
public function hookPagesSaveReady(HookEvent $event) {
|
||||
$page = $event->arguments(0);
|
||||
$info = $this->pagesVersions->pageVersionInfo($page);
|
||||
if($info->version && $info->getAction() === PageVersionInfo::actionRestore) {
|
||||
$this->restorePageVersionFiles($page, $info->version);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook before Pages::save or Pages::saveField to prevent save on a version page unless $action === 'restore'
|
||||
*
|
||||
* #pw-internal
|
||||
*
|
||||
* @param Page $page
|
||||
*
|
||||
*/
|
||||
public function hookBeforePagesSave(Page $page) {
|
||||
if(PagefilesManager::hasPath($page)) {
|
||||
// ensures files flagged for deletion get deleted
|
||||
// this hook doesn't get called by $pages since we replaced the call
|
||||
$page->filesManager->save();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user