use namespace

This commit is contained in:
Jean-Christian Denis 2023-05-09 01:03:05 +02:00
parent c9790f2702
commit a487e89601
Signed by: JcDenis
GPG key ID: 1B5B8C5B90B6C951
12 changed files with 1319 additions and 856 deletions

View file

@ -10,7 +10,7 @@
* @copyright Jean-Christian Denis * @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
if (!defined('DC_RC_PATH')) { if (!defined('DC_RC_PATH') || is_null(dcCore::app()->auth)) {
return null; return null;
} }
@ -22,7 +22,7 @@ $this->registerModule(
[ [
'requires' => [['core', '2.24']], 'requires' => [['core', '2.24']],
'permissions' => dcCore::app()->auth->makePermissions([ 'permissions' => dcCore::app()->auth->makePermissions([
dcAuth::PERMISSION_CONTENT_ADMIN, dcCore::app()->auth::PERMISSION_CONTENT_ADMIN,
initTemplator::PERMISSION_TEMPLATOR, initTemplator::PERMISSION_TEMPLATOR,
]), ]),
'type' => 'plugin', 'type' => 'plugin',

View file

@ -10,161 +10,71 @@
* @copyright Jean-Christian Denis * @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
if (!defined('DC_CONTEXT_ADMIN')) { declare(strict_types=1);
return null;
}
dcCore::app()->auth->setPermissionType(initTemplator::PERMISSION_TEMPLATOR, __('manage templates')); namespace Dotclear\Plugin\templator;
dcCore::app()->menu[dcAdmin::MENU_PLUGINS]->addItem( use dcAdmin;
__('Templates engine'), use dcCore;
dcCore::app()->adminurl->get('admin.plugin.templator'), use dcMenu;
urldecode(dcPage::getPF('templator/icon.png')), use dcNsProcess;
preg_match('/' . preg_quote(dcCore::app()->adminurl->get('admin.plugin.templator')) . '(&.*)?$/', $_SERVER['REQUEST_URI']), use dcPage;
dcCore::app()->auth->check(dcCore::app()->auth->makePermissions([
dcAuth::PERMISSION_CONTENT_ADMIN,
initTemplator::PERMISSION_TEMPLATOR,
]), dcCore::app()->blog->id)
);
if (dcCore::app()->auth->check(dcCore::app()->auth->makePermissions([ class Backend extends dcNsProcess
dcAuth::PERMISSION_CONTENT_ADMIN,
initTemplator::PERMISSION_TEMPLATOR,
]), dcCore::app()->blog->id)) {
dcCore::app()->addBehavior('adminPostHeaders', ['templatorBehaviors','adminPostHeaders']);
dcCore::app()->addBehavior('adminPostFormItems', ['templatorBehaviors','adminPostFormItems']);
dcCore::app()->addBehavior('adminPageHeaders', ['templatorBehaviors','adminPostHeaders']);
dcCore::app()->addBehavior('adminPageFormItems', ['templatorBehaviors','adminPostFormItems']);
dcCore::app()->addBehavior('adminAfterPostCreate', ['templatorBehaviors','adminBeforePostUpdate']);
dcCore::app()->addBehavior('adminBeforePostUpdate', ['templatorBehaviors','adminBeforePostUpdate']);
dcCore::app()->addBehavior('adminAfterPageCreate', ['templatorBehaviors','adminBeforePostUpdate']);
dcCore::app()->addBehavior('adminBeforePageUpdate', ['templatorBehaviors','adminBeforePostUpdate']);
dcCore::app()->addBehavior('adminPostsActions', ['templatorBehaviors','adminPostsActions']);
dcCore::app()->addBehavior('adminPagesActions', ['templatorBehaviors','adminPostsActions']);
dcCore::app()->addBehavior('adminFiltersListsV2', function (ArrayObject $sorts) {
$sorts['templator'] = [
__('Templates engine'),
[
__('Date') => 'post_upddt',
__('Title') => 'post_title',
__('Category') => 'cat_id',
],
'post_upddt',
'desc',
[__('Entries per page'), 30],
];
});
}
class templatorBehaviors
{ {
public static function adminPostHeaders() public static function init(): bool
{ {
return dcPage::jsLoad(dcPage::getPF('templator/js/admin.js')); static::$init == defined('DC_CONTEXT_ADMIN')
&& My::phpCompliant()
&& !is_null(dcCore::app()->blog) && !is_null(dcCore::app()->auth)
&& dcCore::app()->auth->check(dcCore::app()->auth->makePermissions([
My::PERMISSION_TEMPLATOR,
dcCore::app()->auth::PERMISSION_CONTENT_ADMIN,
]), dcCore::app()->blog->id);
return static::$init;
} }
public static function adminPostFormItems(ArrayObject $main_items, ArrayObject $sidebar_items, $post) public static function process(): bool
{ {
$selected = ''; if (!static::$init) {
return false;
if ($post) {
$post_meta = dcCore::app()->meta->getMetadata(['meta_type' => 'template', 'post_id' => $post->post_id]);
$selected = $post_meta->isEmpty() ? '' : $post_meta->meta_id;
} }
$sidebar_items['options-box']['items']['templator'] = '<div id="templator">' . // nullsafe
'<h5>' . __('Template') . '</h5>' . if (is_null(dcCore::app()->auth) || is_null(dcCore::app()->blog) || is_null(dcCore::app()->adminurl)) {
'<p><label for="post_tpl">' . __('Select template:') . '</label>' . return false;
form::combo('post_tpl', self::getTemplateCombo(), $selected) . '</p>' .
'</div>';
}
public static function adminBeforePostUpdate($cur, $post_id)
{
$post_id = (int) $post_id;
if (isset($_POST['post_tpl'])) {
dcCore::app()->meta->delPostMeta($post_id, 'template');
if (!empty($_POST['post_tpl'])) {
dcCore::app()->meta->setPostMeta($post_id, 'template', $_POST['post_tpl']);
}
}
}
public static function adminPostsActions(dcPostsActions $pa)
{
$pa->addAction(
[
__('Appearance') => [
__('Select the template') => 'tpl',
],
],
['templatorBehaviors', 'adminPostsActionsCallback']
);
}
public static function adminPostsActionsCallback(dcPostsActions $pa, ArrayObject $post)
{
# No entry
$posts_ids = $pa->getIDs();
if (empty($posts_ids)) {
throw new Exception(__('No entry selected'));
} }
if (isset($post['post_tpl'])) { //backend sidebar menu icon
try { if ((dcCore::app()->menu[dcAdmin::MENU_PLUGINS] instanceof dcMenu)) {
foreach ($posts_ids as $post_id) { dcCore::app()->menu[dcAdmin::MENU_PLUGINS]->addItem(
dcCore::app()->meta->delPostMeta($post_id, 'template'); My::name(),
if (!empty($post['post_tpl'])) { dcCore::app()->adminurl->get('admin.plugin.' . My::id()),
dcCore::app()->meta->setPostMeta($post_id, 'template', $post['post_tpl']); urldecode(dcPage::getPF(My::id() . '/icon.png')),
} preg_match('/' . preg_quote(dcCore::app()->adminurl->get('admin.plugin.' . My::id())) . '(&.*)?$/', $_SERVER['REQUEST_URI']),
} dcCore::app()->auth->check(dcCore::app()->auth->makePermissions([
dcCore::app()->auth::PERMISSION_CONTENT_ADMIN,
dcAdminNotices::addSuccessNotice(__('Entries template updated.')); My::PERMISSION_TEMPLATOR,
$pa->redirect(true); ]), dcCore::app()->blog->id)
} catch (Exception $e) { );
dcCore::app()->error->add($e->getMessage());
}
} }
$pa->beginPage( dcCore::app()->addBehaviors([
dcPage::breadcrumb([ 'adminPostHeaders' => [BackendBehaviors::class,'adminPostHeaders'],
html::escapeHTML(dcCore::app()->blog->name) => '', 'adminPostFormItems' => [BackendBehaviors::class,'adminPostFormItems'],
$pa->getCallerTitle() => $pa->getRedirection(true), 'adminPageHeaders' => [BackendBehaviors::class,'adminPostHeaders'],
__('Entry template') => '', 'adminPageFormItems' => [BackendBehaviors::class,'adminPostFormItems'],
]) 'adminAfterPostCreate' => [BackendBehaviors::class,'adminBeforePostUpdate'],
); 'adminBeforePostUpdate' => [BackendBehaviors::class,'adminBeforePostUpdate'],
'adminAfterPageCreate' => [BackendBehaviors::class,'adminBeforePostUpdate'],
'adminBeforePageUpdate' => [BackendBehaviors::class,'adminBeforePostUpdate'],
'adminPostsActions' => [BackendBehaviors::class,'adminPostsActions'],
'adminPagesActions' => [BackendBehaviors::class,'adminPostsActions'],
'adminFiltersListsV2' => [BackendBehaviors::class, 'adminFiltersListsV2'],
'initWidgets' => [Widgets::class, 'initWidgets'],
]);
echo return true;
'<h2 class="page-title">' . __('Select template for the selection') . '</h2>' .
'<form action="' . $pa->getURI() . '" method="post">' .
$pa->getCheckboxes() .
'<p><label class="classic">' . __('Select template:') . '</label> ' .
form::combo('post_tpl', self::getTemplateCombo()) . '</p>' .
'<p>' .
$pa->getHiddenFields() .
dcCore::app()->formNonce() .
form::hidden(['action'], 'tpl') .
'<input type="submit" value="' . __('Save') . '" /></p>' .
'</form>';
$pa->endPage();
}
private static function getTemplateCombo()
{
$tpl = [__('No specific template') => ''];
foreach (dcCore::app()->templator->tpl as $k => $v) {
if (!preg_match('/^category-(.+)$/', $k) && !preg_match('/^list-(.+)$/', $k)) {
$tpl[$k] = $k;
}
}
return $tpl;
} }
} }

157
src/BackendBehaviors.php Normal file
View file

@ -0,0 +1,157 @@
<?php
/**
* @brief templator, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Osku and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\templator;
use ArrayObject;
use dcCore;
use dcPage;
use dcPostsActions;
use Dotclear\Database\Cursor;
use Dotclear\Database\MetaRecord;
use Dotclear\Helper\Html\Html;
use Exception;
use form;
class BackendBehaviors
{
public static function adminPostHeaders(): string
{
return dcPage::jsModuleLoad(My::id() . '/js/admin.js');
}
public static function adminPostFormItems(ArrayObject $main_items, ArrayObject $sidebar_items, ?MetaRecord $post): void
{
$selected = '';
if (!is_null($post)) {
$post_meta = dcCore::app()->meta->getMetadata(['meta_type' => 'template', 'post_id' => $post->f('post_id')]);
$selected = $post_meta->isEmpty() ? '' : $post_meta->f('meta_id');
}
$sidebar_items['options-box']['items']['templator'] = '<div id="templator">' .
'<h5>' . __('Template') . '</h5>' .
'<p><label for="post_tpl">' . __('Select template:') . '</label>' .
form::combo('post_tpl', self::getTemplateCombo(), $selected) . '</p>' .
'</div>';
}
public static function adminBeforePostUpdate(Cursor $cur, string|int $post_id): void
{
$post_id = (int) $post_id;
if (isset($_POST['post_tpl'])) {
dcCore::app()->meta->delPostMeta($post_id, 'template');
if (!empty($_POST['post_tpl'])) {
dcCore::app()->meta->setPostMeta($post_id, 'template', $_POST['post_tpl']);
}
}
}
public static function adminPostsActions(dcPostsActions $pa): void
{
$pa->addAction(
[
__('Appearance') => [
__('Select the template') => 'tpl',
],
],
['templatorBehaviors', 'adminPostsActionsCallback']
);
}
public static function adminPostsActionsCallback(dcPostsActions $pa, ArrayObject $post): void
{
# No entry
$posts_ids = $pa->getIDs();
if (empty($posts_ids)) {
$pa->error(new Exception(__('No entry selected')));
return;
}
if (isset($post['post_tpl']) && is_string($post['post_tpl'])) {
try {
foreach ($posts_ids as $post_id) {
dcCore::app()->meta->delPostMeta($post_id, 'template');
if (!empty($post['post_tpl'])) {
dcCore::app()->meta->setPostMeta($post_id, 'template', $post['post_tpl']);
}
}
dcPage::addSuccessNotice(__('Entries template updated.'));
$pa->redirect(true);
} catch (Exception $e) {
dcCore::app()->error->add($e->getMessage());
}
}
$pa->beginPage(
dcPage::breadcrumb([
Html::escapeHTML((string) dcCore::app()->blog?->name) => '',
$pa->getCallerTitle() => $pa->getRedirection(true),
__('Entry template') => '',
])
);
echo
'<h2 class="page-title">' . __('Select template for the selection') . '</h2>' .
'<form action="' . $pa->getURI() . '" method="post">' .
$pa->getCheckboxes() .
'<p><label class="classic">' . __('Select template:') . '</label> ' .
form::combo('post_tpl', self::getTemplateCombo()) . '</p>' .
'<p>' .
$pa->getHiddenFields() .
dcCore::app()->formNonce() .
form::hidden(['action'], 'tpl') .
'<input type="submit" value="' . __('Save') . '" /></p>' .
'</form>';
$pa->endPage();
}
public static function adminFiltersListsV2(ArrayObject $sorts): void
{
$sorts['templator'] = [
__('Templates engine'),
[
__('Date') => 'post_upddt',
__('Title') => 'post_title',
__('Category') => 'cat_id',
],
'post_upddt',
'desc',
[__('Entries per page'), 30],
];
}
/**
* @return array<string,string>
*/
private static function getTemplateCombo(): array
{
$tpl = [__('No specific template') => ''];
$tpls = Templator::instance()->getTpl();
foreach ($tpls as $k => $v) {
if (!preg_match('/^category-(.+)$/', $k) && !preg_match('/^list-(.+)$/', $k)) {
$tpl[$k] = $k;
}
}
return $tpl;
}
}

View file

@ -10,33 +10,69 @@
* @copyright Jean-Christian Denis * @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
if (!defined('DC_RC_PATH')) { declare(strict_types=1);
return null;
}
dcCore::app()->tpl->setPath(dcCore::app()->tpl->getPath(), dcCore::app()->templator->path); namespace Dotclear\Plugin\templator;
dcCore::app()->addBehavior('urlHandlerBeforeGetData', ['publicTemplatorBehaviors','BeforeGetData']);
class publicTemplatorBehaviors use dcCore;
use dcNsProcess;
use Dotclear\Database\MetaRecord;
/**
* Frontend prepend.
*/
class Frontend extends dcNsProcess
{ {
public static function BeforeGetData($_) public static function init(): bool
{ {
if (array_key_exists(dcCore::app()->url->type, dcCore::app()->getPostTypes()) || dcCore::app()->url->type == 'pages') { static::$init = My::phpCompliant();
$params = [];
$params['meta_type'] = 'template';
$params['post_id'] = dcCore::app()->ctx->posts->post_id;
$post_meta = dcCore::app()->meta->getMetadata($params);
if (!$post_meta->isEmpty() && dcCore::app()->tpl->getFilePath($post_meta->meta_id)) { return static::$init;
dcCore::app()->ctx->current_tpl = $post_meta->meta_id; }
}
public static function process(): bool
{
if (!static::$init) {
return false;
} }
if (dcCore::app()->ctx->current_tpl == 'category.html' && preg_match('/^[0-9]{1,}/', dcCore::app()->ctx->categories->cat_id, $cat_id)) { dcCore::app()->tpl->setPath(
$tpl = 'category-' . $cat_id[0] . '.html'; dcCore::app()->tpl->getPath(),
if (dcCore::app()->tpl->getFilePath($tpl)) { Templator::instance()->getPath()
dcCore::app()->ctx->current_tpl = $tpl; );
}
} dcCore::app()->addBehaviors([
'urlHandlerBeforeGetData' => function ($_): void {
if (is_null(dcCore::app()->ctx)) {
return;
}
if ((dcCore::app()->ctx->__get('posts') instanceof MetaRecord)
&& (array_key_exists(dcCore::app()->url->type, dcCore::app()->getPostTypes()) || dcCore::app()->url->type == 'pages')) {
$params = [];
$params['meta_type'] = 'template';
$params['post_id'] = dcCore::app()->ctx->__get('posts')->f('post_id');
$post_meta = dcCore::app()->meta->getMetadata($params);
if (!$post_meta->isEmpty() && is_string($post_meta->f('meta_id')) && dcCore::app()->tpl->getFilePath($post_meta->f('meta_id'))) {
dcCore::app()->ctx->__set('current_tpl', $post_meta->f('meta_id'));
}
}
if (dcCore::app()->ctx->__get('current_tpl') == 'category.html'
&& (dcCore::app()->ctx->__get('categories') instanceof MetaRecord)
&& is_string(dcCore::app()->ctx->__get('categories')->f('cat_id'))
&& preg_match('/^[0-9]{1,}/', dcCore::app()->ctx->__get('categories')->f('cat_id'), $cat_id)
) {
$tpl = 'category-' . $cat_id[0] . '.html';
if (dcCore::app()->tpl->getFilePath($tpl)) {
dcCore::app()->ctx->__set('current_tpl', $tpl);
}
}
},
'initWidgets' => [Widgets::class, 'initWidgets'],
]);
return true;
} }
} }

File diff suppressed because it is too large Load diff

147
src/ManageVars.php Normal file
View file

@ -0,0 +1,147 @@
<?php
/**
* @brief templator, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Osku and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\templator;
use dcCore;
use Dotclear\Helper\File\File;
use Dotclear\Helper\Html\Html;
use Exception;
use initPages;
class ManageVars
{
/** @var ManageVars $container Self instance */
private static $container;
/** @var string $name The requested manage part name*/
public readonly string $name;
/** @var string $part The requested manage part */
public readonly string $part;
/** @var Media $media The limited media instance */
public readonly Media $media;
/** @var array<int,File> $items The media items */
public readonly array $items;
/** @var array<string,int> $categories The blog categories list */
public readonly array $categories;
/** @var bool $has_categories Blog has categories */
public readonly bool $has_categories;
/** @var array<string,string> $sources The templates list */
public readonly array $sources;
/**
* Constructo sets properties.
*/
public function __construct()
{
// manage page
$name = $this->getPartName(($_REQUEST['part'] ?? ''));
$this->name = empty($name) ? __('Home') : $name;
$this->part = empty($name) ? '' : $_REQUEST['part'];
// Extend dcMedia to change settings to allow .html vs media_exclusion
$this->media = new Media();
$this->media->chdir(Templator::MY_TPL_DIR);
// For users with only templator permission, we use sudo.
dcCore::app()->auth?->sudo([$this->media,'getDir']);
$dir = $this->media->dir;
$this->items = array_values($dir['files']);
// categories
$categories_combo = [];
$has_categories = false;
try {
$categories = dcCore::app()->blog?->getCategories(['post_type' => 'post']);
if (!is_null($categories)) {
$l = is_numeric($categories->f('level')) ? (int) $categories->f('level') : 1;
$full_name = [is_string($categories->f('cat_title')) ? $categories->f('cat_title') : ''];
while ($categories->fetch()) {
$id = is_numeric($categories->f('cat_id')) ? (int) $categories->f('cat_id') : 1;
$level = is_numeric($categories->f('level')) ? (int) $categories->f('level') : 1;
$title = is_string($categories->f('cat_title')) ? $categories->f('cat_title') : '';
if ($level < $l) {
$full_name = [];
} elseif ($level == $l) {
array_pop($full_name);
}
$full_name[] = Html::escapeHTML($title);
$categories_combo[implode(' &rsaquo; ', $full_name)] = $id;
$l = $level;
}
$has_categories = !$categories->isEmpty();
}
} catch (Exception $e) {
}
$this->categories = $categories_combo;
$this->has_categories = $has_categories;
// sources
$sources_combo = [
__('Empty template') => 'empty',
'post.html' => 'post',
];
if (dcCore::app()->plugins->moduleExists('pages')
&& dcCore::app()->auth?->check(dcCore::app()->auth->makePermissions([initPages::PERMISSION_PAGES]), dcCore::app()->blog?->id)
) {
$sources_combo['page.html'] = 'page';
}
if ($has_categories) {
$sources_combo['category.html'] = 'category';
}
$this->sources = $sources_combo;
}
/**
* Get self instance.
*
* @return ManageVars Self instance
*/
public static function instance(): ManageVars
{
if (!(self::$container instanceof self)) {
self::$container = new self();
}
return self::$container;
}
private function getPartName(string $part): string
{
$parts = [
'new' => __('New template'),
'copy' => __('Copy available template'),
'copycat' => __('Copy available category template'),
'delete' => __('Delete available template'),
'files' => __('Available templates'),
'used' => __('Used templates'),
'edit' => __('Edit template'),
'posts' => __('Unselect template'),
];
return array_key_exists($part, $parts) ? $parts[$part] : '';
}
}

View file

@ -10,7 +10,13 @@
* @copyright Jean-Christian Denis * @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
class templatorMedia extends dcMedia declare(strict_types=1);
namespace Dotclear\Plugin\templator;
use dcMedia;
class Media extends dcMedia
{ {
// limit to html files // limit to html files
protected function isFileExclude(string $file): bool protected function isFileExclude(string $file): bool

63
src/My.php Normal file
View file

@ -0,0 +1,63 @@
<?php
/**
* @brief templator, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Osku and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\templator;
use dcCore;
/**
* This module definitions.
*/
class My
{
/** @var string This module permission */
public const PERMISSION_TEMPLATOR = 'templator';
/** @var string This module required php version */
public const PHP_MIN = '8.1';
/**
* This module id.
*/
public static function id(): string
{
return basename(dirname(__DIR__));
}
/**
* This module name.
*/
public static function name(): string
{
$name = dcCore::app()->plugins->moduleInfo(self::id(), 'name');
return __(is_string($name) ? $name : self::id());
}
/**
* This module path.
*/
public static function path(): string
{
return dirname(__DIR__);
}
/**
* Check this module PHP version compliant.
*/
public static function phpCompliant(): bool
{
return version_compare(phpversion(), self::PHP_MIN, '>=');
}
}

View file

@ -10,14 +10,24 @@
* @copyright Jean-Christian Denis * @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
if (!defined('DC_RC_PATH')) { declare(strict_types=1);
return;
}
class templatorPager namespace Dotclear\Plugin\templator;
use dcCore;
use dcPage;
use Dotclear\Helper\File\File;
use Dotclear\Helper\File\Files;
use Exception;
class Pager
{ {
public static function templatorItemLine($f, $i) public static function line(File $f, int $i): string
{ {
if (is_null(dcCore::app()->blog) || is_null(dcCore::app()->adminurl)) {
return '';
}
$p_url = dcCore::app()->admin->getPageURL(); $p_url = dcCore::app()->admin->getPageURL();
$fname = $f->basename; $fname = $f->basename;
$count = ''; $count = '';
@ -36,9 +46,9 @@ class templatorPager
$full_name = ''; $full_name = '';
$cat_parents = dcCore::app()->blog->getCategoryParents($cat_id); $cat_parents = dcCore::app()->blog->getCategoryParents($cat_id);
while ($cat_parents->fetch()) { while ($cat_parents->fetch()) {
$full_name = $cat_parents->cat_title . ' &rsaquo; '; $full_name = $cat_parents->f('cat_title') . ' &rsaquo; ';
}; };
$fname = '<strong>' . __('Category') . '</strong> :&nbsp;' . $full_name . dcCore::app()->blog->getCategory($cat_id)->cat_title; $fname = '<strong>' . __('Category') . '</strong> :&nbsp;' . $full_name . $category->f('cat_title');
$params['cat_id'] = $cat_id; $params['cat_id'] = $cat_id;
$params['post_type'] = ''; $params['post_type'] = '';
$icon = dcPage::getPF('templator/img/template-alt.png'); $icon = dcPage::getPF('templator/img/template-alt.png');
@ -66,18 +76,19 @@ class templatorPager
$params['post_type'] = ''; $params['post_type'] = '';
try { try {
$counter = dcCore::app()->meta->getPostsByMeta($params, true); $counter = dcCore::app()->meta->getPostsByMeta($params, true)?->f(0);
$counter = is_numeric($counter) ? (int) $counter : 0;
$url = dcCore::app()->adminurl->get('admin.plugin.templator', [ $url = dcCore::app()->adminurl->get('admin.plugin.templator', [
'part' => 'posts', 'part' => 'posts',
'file' => $fname, 'file' => $fname,
'redir' => dcCore::app()->adminurl->get('admin.plugin.templator', ['part' => 'files']), 'redir' => dcCore::app()->adminurl->get('admin.plugin.templator', ['part' => 'files']),
]); ]);
if ($counter->f(0) == 0) { if ($counter == 0) {
$count = __('No entry'); $count = __('No entry');
} elseif ($counter->f(0) == 1) { } elseif ($counter == 1) {
$count = '<strong>' . $counter->f(0) . '</strong> <a href="' . $url . '">' . __('entry') . '</a>'; $count = '<strong>' . $counter . '</strong> <a href="' . $url . '">' . __('entry') . '</a>';
} else { } else {
$count = '<strong>' . $counter->f(0) . '</strong> <a href="' . $url . '">' . __('entries') . '</a>'; $count = '<strong>' . $counter . '</strong> <a href="' . $url . '">' . __('entries') . '</a>';
} }
} catch (Exception $e) { } catch (Exception $e) {
dcCore::app()->error->add($e->getMessage()); dcCore::app()->error->add($e->getMessage());
@ -100,7 +111,7 @@ class templatorPager
$res .= '<li>' . $count . '</li>' . $res .= '<li>' . $count . '</li>' .
'<li>' . '<li>' .
$f->media_dtstr . ' - ' . $f->media_dtstr . ' - ' .
files::size($f->size) . Files::size($f->size) .
$details . $details .
'</li>'; '</li>';
} }

View file

@ -10,40 +10,33 @@
* @copyright Jean-Christian Denis * @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
if (!defined('DC_RC_PATH')) { declare(strict_types=1);
return null;
}
Clearbricks::lib()->autoload([
'dcTemplator' => __DIR__ . '/inc/class.templator.php',
'templatorMedia' => __DIR__ . '/inc/class.templator.media.php',
'templatorPager' => __DIR__ . '/inc/class.templator.pager.php',
]);
dcCore::app()->templator = new dcTemplator(); namespace Dotclear\Plugin\templator;
dcCore::app()->addBehavior('initWidgets', ['templatorWidgets', 'initWidgets']); use dcCore;
use dcNsProcess;
class templatorWidgets class Prepend extends dcNsProcess
{ {
public static function initWidgets($w) public static function init(): bool
{ {
$w->create('templatorWidget', __('Templator Rendering'), ['widgetTemplator', 'getDataTpl']); static::$init = My::phpCompliant();
$tpl = ['&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.html' => ''];
foreach (dcCore::app()->templator->tpl as $k => $v) { return static::$init;
if (preg_match('/^widget-(.+)$/', $k)) { }
$tpl = array_merge($tpl, [$k => $k]);
} public static function process(): bool
} {
$w->templatorWidget->setting('template', __('Template:'), '', 'combo', $tpl); if (!static::$init) {
} return false;
}
class widgetTemplator
{
public static function getDataTpl($w)
{
if (dcCore::app()->tpl->getFilePath($w->template)) {
echo dcCore::app()->tpl->getData($w->template);
} }
dcCore::app()->auth?->setPermissionType(
My::PERMISSION_TEMPLATOR,
__('manage templates')
);
return true;
} }
} }

View file

@ -10,55 +10,116 @@
* @copyright Jean-Christian Denis * @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html * @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/ */
if (!defined('DC_RC_PATH')) { declare(strict_types=1);
return null;
}
class dcTemplator namespace Dotclear\Plugin\templator;
use dcCore;
use Dotclear\Helper\File\Files;
use Dotclear\Helper\File\Path;
use Dotclear\Helper\Html\Html;
use Exception;
/**
* Templator main class.
*/
class Templator
{ {
protected $tpls_default_name = 'dotty'; /** @var string This plugin folder for templates */
protected $post_default_name = 'post.html'; public const MY_TPL_DIR = 'other-templates';
protected $page_default_name = 'page.html';
protected $category_default_name = 'category.html';
public $template_dir_name = 'other-templates'; /** @var string The dotclear folder for templates */
public $path; public const DC_TPL_DIR = 'default-templates';
public $tpl = []; /** @var string The themes folder for templates */
public $theme_tpl = []; public const THEME_TPL_DIR = 'tpl';
/** @var string The default tplset */
public const DEFAULT_TPLSET = 'dotty';
/** @var string The default post template */
public const DEFAULT_TPL_POST = 'post.html';
/** @var string The default page template */
public const DEFAULT_TPL_PAGE = 'page.html';
/** @var string The default category tempalte */
public const DEFAULT_TPL_CATEGORY = 'category.html';
/** @var Templator Self instance */
private static $instance;
/** @var string $path This plugin templates directory path */
private string $path = '';
/** @var array<string,string> The known templates files */
private array $tpl = [];
private string $file_tpl_post = '';
private string $file_tpl_page = '';
private string $file_tpl_category = '';
private string $user_path_theme = '';
private string $user_tpl_post = '';
private string $user_tpl_category = '';
private string $user_tpl_page = '';
/** /**
* * Constructor sets properties.
*/ */
public function __construct() public function __construct()
{ {
$this->path = dcCore::app()->blog->public_path . '/' . $this->template_dir_name; if (is_null(dcCore::app()->blog)) {
throw new Exception(__('Blog is not set'));
// Initial templates
$this->post_tpl = DC_ROOT . '/inc/public/default-templates/' . $this->tpls_default_name . '/' . $this->post_default_name;
$this->category_tpl = DC_ROOT . '/inc/public/default-templates/' . $this->tpls_default_name . '/' . $this->category_default_name;
if (dcCore::app()->plugins->moduleExists('pages')) {
$plugin_page = dcCore::app()->plugins->getModules('pages');
$this->page_tpl = path::real($plugin_page['root'] . '/default-templates/' . $this->tpls_default_name . '/' . $this->page_default_name);
} }
$this->user_theme = dcCore::app()->blog->themes_path . '/' . dcCore::app()->blog->settings->system->theme; $page_root = dcCore::app()->plugins->getDefine('pages')->get('root');
$this->user_post_tpl = path::real($this->user_theme . '/tpl/' . $this->post_default_name);
$this->user_category_tpl = path::real($this->user_theme . '/tpl/' . $this->category_default_name); // Initial templates
$this->user_page_tpl = path::real($this->user_theme . '/tpl/' . $this->page_default_name); $this->path = implode(DIRECTORY_SEPARATOR, [dcCore::app()->blog->public_path, self::MY_TPL_DIR]);
$this->file_tpl_post = implode(DIRECTORY_SEPARATOR, [DC_ROOT, 'inc', 'public', self::DC_TPL_DIR, self::DEFAULT_TPLSET, self::DEFAULT_TPL_POST]);
$this->file_tpl_category = implode(DIRECTORY_SEPARATOR, [DC_ROOT, 'inc', 'public', self::DC_TPL_DIR, self::DEFAULT_TPLSET, self::DEFAULT_TPL_CATEGORY]);
$this->file_tpl_page = Path::real(implode(DIRECTORY_SEPARATOR, [$page_root, self::DC_TPL_DIR, self::DEFAULT_TPLSET, self::DEFAULT_TPL_PAGE])) ?: '';
// user templates
$this->user_path_theme = dcCore::app()->blog->themes_path . DIRECTORY_SEPARATOR . dcCore::app()->blog->settings->get('system')->get('theme');
$this->user_tpl_post = Path::real(implode(DIRECTORY_SEPARATOR, [$this->user_path_theme, self::THEME_TPL_DIR, self::DEFAULT_TPL_POST])) ?: '';
$this->user_tpl_category = Path::real(implode(DIRECTORY_SEPARATOR, [$this->user_path_theme, self::THEME_TPL_DIR, self::DEFAULT_TPL_CATEGORY])) ?: '';
$this->user_tpl_page = Path::real(implode(DIRECTORY_SEPARATOR, [$this->user_path_theme, self::THEME_TPL_DIR, self::DEFAULT_TPL_PAGE])) ?: '';
$this->findTemplates(); $this->findTemplates();
} }
public static function instance(): Templator
{
if (!(self::$instance instanceof Templator)) {
self::$instance = new Templator();
}
return self::$instance;
}
public function getPath(): string
{
return $this->path;
}
/**
* @return array<string,string>
*/
public function getTpl(): array
{
return $this->tpl;
}
/** /**
* *
*/ */
public function canUseRessources($create = false) public function canUseRessources(bool $create = false): bool
{ {
if (!is_dir($this->path)) { if (!is_dir($this->path)) {
if ($create) { if ($create) {
files::makeDir($this->path); Files::makeDir($this->path);
} }
return true; return true;
@ -80,9 +141,9 @@ class dcTemplator
} }
/** /**
* * @return array{c: string, w: bool, f: string}
*/ */
public function getSourceContent($f) public function getSourceContent(string $f): array
{ {
$source = $this->tpl; $source = $this->tpl;
@ -96,8 +157,8 @@ class dcTemplator
} }
return [ return [
'c' => file_get_contents($source[$f]), 'c' => (string) file_get_contents($source[$f]),
'w' => $this->getDestinationFile($f) !== false, 'w' => !empty($this->getDestinationFile($f)),
'f' => $f, 'f' => $f,
]; ];
} }
@ -105,7 +166,7 @@ class dcTemplator
/** /**
* *
*/ */
public function filesList($item = '%1$s') public function filesList(string $item = '%1$s'): string
{ {
$files = $this->tpl; $files = $this->tpl;
@ -117,7 +178,7 @@ class dcTemplator
foreach ($files as $k => $v) { foreach ($files as $k => $v) {
$li = sprintf('<li>%s</li>', $item); $li = sprintf('<li>%s</li>', $item);
$list .= sprintf($li, $k, html::escapeHTML($k)); $list .= sprintf($li, $k, Html::escapeHTML($k));
} }
return sprintf('<ul>%s</ul>', $list); return sprintf('<ul>%s</ul>', $list);
@ -126,31 +187,31 @@ class dcTemplator
/** /**
* *
*/ */
public function initializeTpl($name, $type) public function initializeTpl(string $name, string $type): void
{ {
if ($type == 'category') { if ($type == 'category') {
if ($this->user_category_tpl) { if ($this->user_tpl_category) {
$base = $this->user_category_tpl; $base = $this->user_tpl_category;
} else { } else {
$base = $this->category_tpl; $base = $this->file_tpl_category;
} }
} elseif ($type == 'page') { } elseif ($type == 'page') {
if ($this->user_page_tpl) { if ($this->user_tpl_page) {
$base = $this->user_page_tpl; $base = $this->user_tpl_page;
} else { } else {
$base = $this->page_tpl; $base = $this->file_tpl_page;
} }
} else { } else {
if ($this->user_post_tpl) { if ($this->user_tpl_post) {
$base = $this->user_post_tpl; $base = $this->user_tpl_post;
} else { } else {
$base = $this->post_tpl; $base = $this->file_tpl_post;
} }
} }
$source = [ $source = [
'c' => file_get_contents($base), 'c' => (string) file_get_contents($base),
'w' => $this->getDestinationFile($name) !== false, 'w' => !empty($this->getDestinationFile($name)),
]; ];
if (!$source['w']) { if (!$source['w']) {
@ -171,7 +232,7 @@ class dcTemplator
$content = $source['c']; $content = $source['c'];
if (!is_dir(dirname($dest))) { if (!is_dir(dirname($dest))) {
files::makeDir(dirname($dest)); Files::makeDir(dirname($dest));
} }
$fp = @fopen($dest, 'wb'); $fp = @fopen($dest, 'wb');
@ -179,8 +240,8 @@ class dcTemplator
throw new Exception('tocatch'); throw new Exception('tocatch');
} }
$content = preg_replace('/(\r?\n)/m', "\n", $content); $content = (string) preg_replace('/(\r?\n)/m', "\n", $content);
$content = preg_replace('/\r/m', "\n", $content); $content = (string) preg_replace('/\r/m', "\n", $content);
fwrite($fp, $content); fwrite($fp, $content);
fclose($fp); fclose($fp);
@ -192,7 +253,7 @@ class dcTemplator
/** /**
* *
*/ */
public function copypasteTpl($name, $source) public function copypasteTpl(string $name, string $source): void
{ {
if ($name == $source) { if ($name == $source) {
throw new Exception(__('File already exists.')); throw new Exception(__('File already exists.'));
@ -202,7 +263,7 @@ class dcTemplator
$data = [ $data = [
'c' => $file['c'], 'c' => $file['c'],
'w' => $this->getDestinationFile($name) !== false, 'w' => !empty($this->getDestinationFile($name)),
]; ];
if (!$data['w']) { if (!$data['w']) {
@ -219,7 +280,7 @@ class dcTemplator
$content = $data['c']; $content = $data['c'];
if (!is_dir(dirname($dest))) { if (!is_dir(dirname($dest))) {
files::makeDir(dirname($dest)); Files::makeDir(dirname($dest));
} }
$fp = @fopen($dest, 'wb'); $fp = @fopen($dest, 'wb');
@ -227,8 +288,8 @@ class dcTemplator
throw new Exception('tocatch'); throw new Exception('tocatch');
} }
$content = preg_replace('/(\r?\n)/m', "\n", $content); $content = (string) preg_replace('/(\r?\n)/m', "\n", $content);
$content = preg_replace('/\r/m', "\n", $content); $content = (string) preg_replace('/\r/m', "\n", $content);
fwrite($fp, $content); fwrite($fp, $content);
fclose($fp); fclose($fp);
@ -240,7 +301,7 @@ class dcTemplator
/** /**
* *
*/ */
public function writeTpl($name, $content) public function writeTpl(string $name, string $content): void
{ {
try { try {
$dest = $this->getDestinationFile($name); $dest = $this->getDestinationFile($name);
@ -250,16 +311,17 @@ class dcTemplator
} }
if (!is_dir(dirname($dest))) { if (!is_dir(dirname($dest))) {
files::makeDir(dirname($dest)); Files::makeDir(dirname($dest));
} }
$fp = @fopen($dest, 'wb'); $fp = @fopen($dest, 'wb');
if (!$fp) { if (!$fp) {
//throw new Exception('tocatch'); //throw new Exception('tocatch');
return;
} }
$content = preg_replace('/(\r?\n)/m', "\n", $content); $content = (string) preg_replace('/(\r?\n)/m', "\n", $content);
$content = preg_replace('/\r/m', "\n", $content); $content = (string) preg_replace('/\r/m', "\n", $content);
fwrite($fp, $content); fwrite($fp, $content);
fclose($fp); fclose($fp);
@ -271,7 +333,7 @@ class dcTemplator
/** /**
* *
*/ */
public function copyTpl($name) public function copyTpl(string $name): void
{ {
try { try {
$file = $this->getSourceContent($name); $file = $this->getSourceContent($name);
@ -282,7 +344,7 @@ class dcTemplator
} }
if (!is_dir(dirname($dest))) { if (!is_dir(dirname($dest))) {
files::makeDir(dirname($dest)); Files::makeDir(dirname($dest));
} }
$fp = @fopen($dest, 'wb'); $fp = @fopen($dest, 'wb');
@ -290,7 +352,7 @@ class dcTemplator
throw new Exception('tocatch'); throw new Exception('tocatch');
} }
$content = preg_replace('/(\r?\n)/m', "\n", $file['c']); $content = (string) preg_replace('/(\r?\n)/m', "\n", $file['c']);
$content = preg_replace('/\r/m', "\n", $file['c']); $content = preg_replace('/\r/m', "\n", $file['c']);
fwrite($fp, $file['c']); fwrite($fp, $file['c']);
@ -300,11 +362,14 @@ class dcTemplator
} }
} }
protected function getDestinationFile($f, $totheme = false) /**
* @return string The destination or empty string on error
*/
protected function getDestinationFile(string $f, bool $totheme = false): string
{ {
$dest = $this->path . '/' . $f; $dest = $this->path . '/' . $f;
if ($totheme) { if ($totheme) {
$dest = $this->user_theme . '/tpl/' . $f; $dest = implode(DIRECTORY_SEPARATOR, [$this->user_path_theme, self::THEME_TPL_DIR, $f]);
} }
if (file_exists($dest) && is_writable($dest)) { if (file_exists($dest) && is_writable($dest)) {
@ -315,27 +380,31 @@ class dcTemplator
return $dest; return $dest;
} }
return false; return '';
} }
protected function findTemplates() protected function findTemplates(): void
{ {
$this->tpl = $this->getFilesInDir($this->path); $this->tpl = $this->getFilesInDir($this->path);
//$this->theme_tpl = $this->getFilesInDir(path::real($this->user_theme).'/tpl');
uksort($this->tpl, [$this,'sortFilesHelper']); uksort($this->tpl, [$this,'sortFilesHelper']);
//uksort($this->theme_tpl,array($this,'sortFilesHelper'));
} }
protected function getFilesInDir($dir) /**
* @return array<string,string>
*/
protected function getFilesInDir(string $dir): array
{ {
$dir = path::real($dir); $res = [];
$dir = Path::real($dir);
if (!$dir || !is_dir($dir) || !is_readable($dir)) { if (!$dir || !is_dir($dir) || !is_readable($dir)) {
return []; return $res;
} }
$d = dir($dir); $d = dir($dir);
$res = []; if (!$d) {
return $res;
}
while (($f = $d->read()) !== false) { while (($f = $d->read()) !== false) {
if (is_file($dir . '/' . $f) && !preg_match('/^\./', $f)) { if (is_file($dir . '/' . $f) && !preg_match('/^\./', $f)) {
$res[$f] = $dir . '/' . $f; $res[$f] = $dir . '/' . $f;
@ -345,14 +414,14 @@ class dcTemplator
return $res; return $res;
} }
protected function sortFilesHelper($a, $b) protected function sortFilesHelper(string $a, string $b): int
{ {
if ($a == $b) { if ($a == $b) {
return 0; return 0;
} }
$ext_a = files::getExtension($a); $ext_a = Files::getExtension($a);
$ext_b = files::getExtension($b); $ext_b = Files::getExtension($b);
return strcmp($ext_a . '.' . $a, $ext_b . '.' . $b); return strcmp($ext_a . '.' . $a, $ext_b . '.' . $b);
} }

59
src/Widgets.php Normal file
View file

@ -0,0 +1,59 @@
<?php
/**
* @brief templator, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Osku and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
declare(strict_types=1);
namespace Dotclear\Plugin\templator;
use dcCore;
use Dotclear\Plugin\widgets\WidgetsStack;
use Dotclear\Plugin\widgets\WidgetsElement;
/**
* Widgets.
*/
class Widgets
{
/**
* @param WidgetsStack $w WidgetsStack instance
*/
public static function initWidgets(WidgetsStack $w): void
{
$tpl = ['&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.html' => ''];
$tpls = Templator::instance()->getTpl();
foreach ($tpls as $k => $v) {
if (preg_match('/^widget-(.+)$/', $k)) {
$tpl = array_merge($tpl, [$k => $k]);
}
}
$w
->create(
'templatorWidget',
__('Templator Rendering'),
[self::class, 'getDataTpl']
)
->setting(
'template',
__('Template:'),
'',
'combo',
$tpl
);
}
public static function getDataTpl(WidgetsElement $w): string
{
return is_string($w->__get('template')) && dcCore::app()->tpl->getFilePath($w->__get('template')) ?
dcCore::app()->tpl->getData($w->__get('template')) : '';
}
}