full rewrite for namespace

master
Jean-Christian Paul Denis 2023-05-04 22:51:48 +02:00
parent 6fc52fc514
commit 7daccacae7
Signed by: JcDenis
GPG Key ID: 1B5B8C5B90B6C951
28 changed files with 1561 additions and 1061 deletions

View File

@ -10,6 +10,10 @@
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!class_exists('dcCore') || is_null(dcCore::app()->auth)) {
return null;
}
$this->registerModule(
'Noodles',
'Add users gravatars everywhere',
@ -18,13 +22,13 @@ $this->registerModule(
[
'requires' => [['core', '2.24']],
'permissions' => dcCore::app()->auth->makePermissions([
dcAuth::PERMISSION_CONTENT_ADMIN,
dcCore::app()->auth::PERMISSION_CONTENT_ADMIN,
]),
'type' => 'plugin',
'support' => 'https://github.com/JcDenis/noodles',
'details' => 'http://plugins.dotaddict.org/dc2/details/noodles',
'repository' => 'https://raw.githubusercontent.com/JcDenis/noodles/master/dcstore.xml',
'settings' => [
'type' => 'plugin',
'support' => 'https://github.com/JcDenis/' . basename(__DIR__),
'details' => 'http://plugins.dotaddict.org/dc2/details/' . basename(__DIR__),
'repository' => 'https://raw.githubusercontent.com/JcDenis/' . basename(__DIR__) . '/master/dcstore.xml',
'settings' => [
'self' => '',
],
]

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -1,105 +0,0 @@
<?php
/**
* @brief noodles, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis and contributors
*
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_RC_PATH')) {
return null;
}
require __DIR__ . '/class.noodles.php';
global $__default_noodles;
$__default_noodles = new noodles();
# Posts (by public behavior)
$__default_noodles
->add('posts', __('Entries'), '', ['othersNoodles', 'publicPosts'])
->size(48)
->css('float:right;margin:4px;');
# Comments (by public behavior)
$__default_noodles
->add('comments', __('Comments'), '', ['othersNoodles', 'publicComments'])
->active(true)
->size(48)
->css('float:left;margin:4px;');
# Block with post title link (like homepage posts)
$__default_noodles
->add('titlesposts', __('Entries titles'), ['genericNoodles', 'postURL'])
->target('.post-title a')
->css('margin-right:2px;');
if (dcCore::app()->plugins->moduleExists('widgets')) {
# Widget Selected entries
$__default_noodles
->add('bestof', __('Selected entries'), ['genericNoodles', 'postURL'])
->target('.selected li a')
->css('margin-right:2px;');
# Widget Last entries
$__default_noodles
->add('lastposts', __('Last entries'), ['genericNoodles', 'postURL'])
->target('.lastposts li a')
->css('margin-right:2px;');
# Widget Last comments
$__default_noodles
->add('lastcomments', __('Last comments'), ['widgetsNoodles', 'lastcomments'])
->active(true)
->target('.lastcomments li a')
->css('margin-right:2px;');
}
# Plugin auhtorMode
if (dcCore::app()->plugins->moduleExists('authorMode')
&& dcCore::app()->blog->settings->authormode->authormode_active
) {
$__default_noodles
->add('authorswidget', __('Authors widget'), ['authormodeNoodles', 'authors'])
->target('#authors ul li a')
->css('margin-right:2px;');
$__default_noodles
->add('author', __('Author'), '', ['authormodeNoodles', 'author'])
->active(true)
->size(48)
->target('.dc-author #content-info h2')
->css('clear:left; float:left;margin-right:2px;');
$__default_noodles
->add('authors', __('Authors'), ['authormodeNoodles', 'authors'])
->active(true)
->size(32)
->target('.dc-authors .author-info h2 a')
->css('clear:left; float:left; margin:4px;');
}
# Plugin rateIt
if (dcCore::app()->plugins->moduleExists('rateIt')
&& dcCore::app()->blog->settings->rateit->rateit_active
) {
$__default_noodles
->add('rateitpostsrank', __('Top rated entries'), ['genericNoodles', 'postURL'])
->target('.rateitpostsrank.rateittypepost ul li a') // Only "post" type
->css('margin-right:2px;');
}
# Plugin lastpostsExtend
if (dcCore::app()->plugins->moduleExists('lastpostsExtend')) {
$__default_noodles
->add('lastpostsextend', __('Last entries (extend)'), ['genericNoodles', 'postURL'])
->target('.lastpostsextend ul li a')
->css('margin-right:2px;');
}
# --BEHAVIOR-- initDefaultNoodles
dcCore::app()->callBehavior('initDefaultNoodles', $__default_noodles);

View File

@ -0,0 +1,39 @@
<?php
/**
* @package Dotclear
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
#
# DOT NOT MODIFY THIS FILE !
#
use Dotclear\Helper\L10n;
L10n::$locales['Entries titles'] = 'Titres des billets';
L10n::$locales['Authors widget'] = 'Widgets des auteurs';
L10n::$locales['Begin'] = 'Début';
L10n::$locales['End'] = 'Fin';
L10n::$locales['Before'] = 'Avant';
L10n::$locales['After'] = 'Après';
L10n::$locales['Maximum file size exceeded'] = 'Taille maximum de fichier dépassée';
L10n::$locales['Something went wrong while download file'] = 'Une erreur est survenue lors du téléchargement de l\'image';
L10n::$locales['Image must be in png format'] = 'L\'image doit être au format png';
L10n::$locales['Failed to save image'] = 'Impossible de sauvegarder l\'image';
L10n::$locales['Something went wrong while search file'] = 'Une erreur est survenue pendant la recherche de fichier';
L10n::$locales['Enable plugin noodles on this blog'] = 'Activer le plugin Noodles sur ce blog';
L10n::$locales['Select default avatar to display on unknown users.'] = 'Selectionner l\'avatar par défaut à afficher pour les utilisateurs inconnus.';
L10n::$locales['Blog default image'] = 'Image du blog';
L10n::$locales['Upload a new avatar'] = 'Téléverser un nouvel avatar';
L10n::$locales['Image must be in "png" format and have a maximum file size of 30Ko'] = 'L\'image doit être au format "png" et avoir une taille maximum de 30Ko';
L10n::$locales['API default image'] = 'Image par défaut de l\'API';
L10n::$locales['Public directory is not writable, you can not use custom gravatar.'] = 'Le répertoire public n\'est pas accissble en écriture, vous ne pouvez pas utiliser les avatars customisés';
L10n::$locales['Search area'] = 'Zone de recherche';
L10n::$locales['Size'] = 'Taille';
L10n::$locales['Rating'] = 'Évaluation';
L10n::$locales['Target'] = 'Cible';
L10n::$locales['Place'] = 'Emplacement';
L10n::$locales['Adjust avatar CSS'] = 'Ajuster la CSS de l\'image';
L10n::$locales['Target and Place are for javascript.'] = 'Les cible et emplacement sont utilisés par les scripts javascript.';
L10n::$locales['Add users gravatars everywhere'] = 'Ajouter les avatars partout';

View File

@ -10,14 +10,52 @@
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_CONTEXT_ADMIN')) {
return;
}
declare(strict_types=1);
dcCore::app()->menu[dcAdmin::MENU_PLUGINS]->addItem(
__('Noodles'),
dcCore::app()->adminurl->get('admin.plugin.noodles'),
dcPage::getPF('noodles/icon.png'),
preg_match('/' . preg_quote(dcCore::app()->adminurl->get('admin.plugin.noodles')) . '(&.*)?$/', $_SERVER['REQUEST_URI']),
dcCore::app()->auth->check(dcCore::app()->auth->makePermissions([dcAuth::PERMISSION_CONTENT_ADMIN]), dcCore::app()->blog->id)
);
namespace Dotclear\Plugin\noodles;
use dcAdmin;
use dcCore;
use dcMenu;
use dcNsProcess;
use dcPage;
class Backend extends dcNsProcess
{
public static function init(): bool
{
static::$init = defined('DC_CONTEXT_ADMIN')
&& My::phpCompliant()
&& !is_null(dcCore::app()->auth) && !is_null(dcCore::app()->blog)
&& dcCore::app()->auth->check(dcCore::app()->auth->makePermissions([
dcCore::app()->auth::PERMISSION_CONTENT_ADMIN,
]), dcCore::app()->blog->id);
return static::$init;
}
public static function process(): bool
{
if (!static::$init) {
return false;
}
if (is_null(dcCore::app()->auth)
|| is_null(dcCore::app()->blog)
|| is_null(dcCore::app()->adminurl)
|| !(dcCore::app()->menu[dcAdmin::MENU_PLUGINS] instanceof dcMenu)
) {
return false;
}
dcCore::app()->menu[dcAdmin::MENU_PLUGINS]->addItem(
My::name(),
dcCore::app()->adminurl->get('admin.plugin.' . My::id()),
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]), dcCore::app()->blog->id)
);
return true;
}
}

84
src/Combo.php 100644
View File

@ -0,0 +1,84 @@
<?php
/**
* @brief noodles, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis 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\noodles;
class Combo
{
/**
* @return array<string,string>
*/
public static function api(): array
{
return [
'gravatar' => 'http://www.gravatar.com/',
'libravatar' => 'http://cdn.libravatar.org/',
];
}
/**
* @return array<string,int>
*/
public static function local(): array
{
return [
__('API') => 0,
__('Local') => 1,
];
}
/**
* @return array<string,string>
*/
public static function place(): array
{
return [
__('Begin') => 'prepend',
__('End') => 'append',
__('Before') => 'before',
__('After') => 'after',
];
}
/**
* @return array<string,string>
*/
public static function rating(): array
{
return [
'G' => 'g',
'PG' => 'pg',
'R' => 'r',
'X' => 'x',
];
}
/**
* @return array<string,int>
*/
public static function size(): array
{
return [
'16px' => 16,
'24px' => 24,
'32px' => 32,
'48px' => 48,
'56px' => 56,
'64px' => 64,
'92px' => 92,
'128px' => 128,
'256px' => 256,
];
}
}

View File

@ -10,234 +10,54 @@
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_RC_PATH')) {
return null;
}
declare(strict_types=1);
if (!dcCore::app()->blog->settings->noodles->noodles_active) {
return null;
}
namespace Dotclear\Plugin\noodles;
include __DIR__ . '/inc/_default_noodles.php';
require_once __DIR__ . '/inc/_noodles_functions.php';
use dcCore;
use dcNsProcess;
use dcUtils;
dcCore::app()->addBehavior('publicHeadContent', ['publicNoodles', 'publicHeadContent']);
dcCore::app()->tpl->setPath(dcCore::app()->tpl->getPath(), __DIR__ . '/default-templates');
global $__noodles;
$__noodles = noodles::decode(dcCore::app()->blog->settings->noodles->noodles_object);
if ($__noodles->isEmpty()) {
$__noodles = $__default_noodles;
}
//$GLOBALS['__noodles'] =& $__noodles;
foreach ($__noodles->noodles() as $noodle) {
if ($noodle->active && $noodle->hasPhpCallback()) {
$noodle->phpCallback(dcCore::app());
}
}
class publicNoodles
class Frontend extends dcNsProcess
{
public static function publicHeadContent()
public static function init(): bool
{
echo
dcUtils::cssLoad(dcCore::app()->blog->url . dcCore::app()->url->getURLFor('noodlescss')) .
dcUtils::jsLoad(dcCore::app()->blog->url . dcCore::app()->url->getBase('noodlesmodule') . '/js/jquery.noodles.js') .
dcUtils::jsLoad(dcCore::app()->blog->url . dcCore::app()->url->getURLFor('noodlesjs'));
}
}
class urlNoodles extends dcUrlHandlers
{
public static function css($args)
{
global $__noodles;
$css = '';
foreach ($__noodles->noodles() as $noodle) {
if (!$noodle->active || !$noodle->hasJsCallback()) {
continue;
}
$css .= '.noodles-' . $noodle->id() . '{' . $noodle->css . '}' . "\n";
}
header('Content-Type: text/css; charset=UTF-8');
echo $css;
exit;
}
public static function js($args)
{
global $__noodles;
$targets = [];
foreach ($__noodles->noodles() as $noodle) {
if (!$noodle->active || !$noodle->hasJsCallback()) {
continue;
}
$targets[] = '$(\'' . html::escapeJS($noodle->target) . '\').noodles({' .
' imgId:\'' . html::escapeJS($noodle->id()) . '\',' .
' imgPlace:\'' . html::escapeJS($noodle->place) . '\'' .
'});';
}
header('Content-Type: text/javascript; charset=UTF-8');
echo
"\$(function(){if(!document.getElementById){return;} \n" .
"\$.fn.noodles.defaults.service_url = '" . html::escapeJS(dcCore::app()->blog->url . dcCore::app()->url->getBase('noodlesservice') . '/') . "'; \n" .
"\$.fn.noodles.defaults.service_func = '" . html::escapeJS('getNoodle') . "'; \n" .
implode("\n", $targets) .
"})\n";
exit;
}
public static function service($args)
{
header('Content-Type: text/xml; charset=UTF-8');
$rsp = new xmlTag('rsp');
$i = !empty($_POST['noodleId']) ? $_POST['noodleId'] : null;
$c = !empty($_POST['noodleContent']) ? $_POST['noodleContent'] : null;
if (!dcCore::app()->blog->settings->noodles->noodles_active) {
$rsp->status = 'failed';
$rsp->message(__('noodles is disabled on this blog'));
echo $rsp->toXML(true);
return false;
}
if ($i === null || $c === null) {
$rsp->status = 'failed';
$rsp->message(__('noodles failed because of missing informations'));
echo $rsp->toXML(true);
return false;
}
try {
$__noodles = noodles::decode(dcCore::app()->blog->settings->noodles->noodles_object);
if ($__noodles->isEmpty()) {
$__noodles = $GLOBALS['__default_noodles'];
}
} catch (Excetpion $e) {
$rsp->status = 'failed';
$rsp->message(__('Failed to load default noodles'));
echo $rsp->toXML(true);
return false;
}
if (!$__noodles->exists($i)) {
$rsp->status = 'failed';
$rsp->message(__('Failed to load noodle'));
echo $rsp->toXML(true);
return false;
}
$m = $__noodles->get($i)->jsCallback($__noodles->get($i), $c);
$s = $__noodles->get($i)->size;
$r = $__noodles->get($i)->rating;
$d = dcCore::app()->blog->settings->noodles->noodles_image ?
urlencode(noodlesLibImagePath::getUrl('noodles')) : '';
$u = dcCore::app()->blog->settings->noodles->noodles_api;
if (empty($u)) {
$u = 'http://www.gravatar.com/';
}
if (!$m) {
$m = 'nobody@nowhere.tld';
}
if (!$s) {
$s = 32;
}
if (!$r) {
$r = 'g';
}
$m = md5(strtolower(trim($m)));
$im = new xmlTag('noodle');
$im->size = $s;
$im->src = sprintf('%savatar/%s?s=%s&amp;r=%s&amp;d=%s', $u, $m, $s, $r, $d);
$rsp->insertNode($im);
$rsp->status = 'ok';
echo $rsp->toXML(true);
exit;
}
public static function noodles($args)
{
if (!dcCore::app()->blog->settings->noodles->noodles_active) {
self::p404();
return;
}
if (!preg_match('#^(.*?)$#', $args, $m)) {
self::p404();
return;
}
$f = $m[1];
if (!($f = self::searchTplFiles($f))) {
self::p404();
return;
}
$allowed_types = ['png', 'jpg', 'jpeg', 'gif', 'css', 'js', 'swf'];
if (!in_array(files::getExtension($f), $allowed_types)) {
self::p404();
return;
}
$type = files::getMimeType($f);
header('Content-Type: ' . $type . '; charset=UTF-8');
header('Content-Length: ' . filesize($f));
if ($type != 'text/css' || dcCore::app()->blog->settings->system->url_scan == 'path_info') {
readfile($f);
} else {
echo preg_replace(
'#url\((?!(http:)|/)#',
'url(' . dcCore::app()->blog->url . dcCore::app()->url->getBase('noodlesmodule') . '/',
file_get_contents($f)
);
}
exit;
}
# Search noodles files like JS, CSS in default-templates subdirectories
private static function searchTplFiles($file)
{
if (strstr($file, '..') !== false) {
return false;
}
$paths = dcCore::app()->tpl->getPath();
foreach ($paths as $path) {
if (preg_match('/tpl(\/|)$/', $path)) {
$path = path::real($path . '/..');
}
if (file_exists($path . '/' . $file)) {
return $path . '/' . $file;
}
}
return false;
static::$init = My::phpCompliant();
return static::$init;
}
public static function process(): bool
{
if (!static::$init) {
return false;
}
$targets = Targets::instance();
if (!$targets->active) {
return false;
}
dcCore::app()->tpl->setPath(dcCore::app()->tpl->getPath(), My::path() . '/default-templates');
foreach ($targets->dump() as $target) {
if ($target->active() && $target->hasPhpCallback()) {
$target->phpCallback();
}
}
dcCore::app()->addBehavior('publicHeadContent', function (): void {
if (is_null(dcCore::app()->blog)) {
return;
}
echo
dcUtils::cssLoad(dcCore::app()->blog->url . dcCore::app()->url->getURLFor('noodles_css')) .
dcUtils::jsLoad(dcCore::app()->blog->url . dcCore::app()->url->getBase('noodles_file') . '/js/jquery.noodles.js') .
dcUtils::jsLoad(dcCore::app()->blog->url . dcCore::app()->url->getURLFor('noodles_js'));
});
return true;
}
}

View File

@ -10,18 +10,25 @@
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_RC_PATH')) {
return null;
}
declare(strict_types=1);
class noodlesLibImagePath
namespace Dotclear\Plugin\noodles;
use dcCore;
use Dotclear\Helper\File\Path;
class Image
{
public static $version = '1.1';
/** @var string The current class major version */
public const VERSION = '2';
public static function getArray($m = '')
/**
* @return array<string, array{dir: null|string, url: null|string}> The Image possible paths
*/
public static function getArray(): array
{
if (!dcCore::app()->plugins->moduleExists($m)
|| !dcCore::app()->url->getBase($m . 'module')
if (is_null(dcCore::app()->blog)
|| !dcCore::app()->url->getBase('noodles_file')
) {
return [
'theme' => ['dir' => null, 'url' => null],
@ -30,27 +37,35 @@ class noodlesLibImagePath
];
}
$public_url = dcCore::app()->blog->settings->get('system')->get('public_url');
if (!is_string($public_url)) {
$public_url = '';
}
return [
'theme' => [
'dir' => path::real(dcCore::app()->blog->themes_path . '/' . dcCore::app()->blog->settings->system->theme . '/img') . '/' . $m . '-default-image.png',
'url' => dcCore::app()->blog->settings->system->themes_url . dcCore::app()->blog->settings->system->theme . '/img/' . $m . '-default-image.png',
'dir' => Path::real(dcCore::app()->blog->themes_path . '/' . dcCore::app()->blog->settings->get('system')->get('theme') . '/img') . '/' . My::IMAGE,
'url' => dcCore::app()->blog->settings->get('system')->get('themes_url') . dcCore::app()->blog->settings->get('system')->get('theme') . '/img/' . My::IMAGE,
],
'public' => [
'dir' => path::real(dcCore::app()->blog->public_path) . '/' . $m . '-default-image.png',
'url' => dcCore::app()->blog->host . path::clean(dcCore::app()->blog->settings->system->public_url) . '/' . $m . '-default-image.png',
'dir' => Path::real(dcCore::app()->blog->public_path) . '/' . My::IMAGE,
'url' => dcCore::app()->blog->host . Path::clean($public_url) . '/' . My::IMAGE,
],
'module' => [
'dir' => path::real(dcCore::app()->plugins->moduleRoot($m) . '/default-templates/img') . '/' . $m . '-default-image.png',
'url' => dcCore::app()->blog->url . dcCore::app()->url->getBase($m . 'module') . '/img/' . $m . '-default-image.png',
'dir' => Path::real(My::path() . '/default-templates/img') . '/' . My::IMAGE,
'url' => dcCore::app()->blog->url . dcCore::app()->url->getBase('noodles_file') . '/img/' . My::IMAGE,
],
];
}
public static function getUrl($m = '')
/**
* @return null|string The image URL
*/
public static function getUrl(): ?string
{
$files = self::getArray($m);
$files = self::getArray();
foreach ($files as $k => $file) {
if (file_exists($files[$k]['dir'])) {
if (is_string($files[$k]['dir']) && file_exists($files[$k]['dir'])) {
return $files[$k]['url'];
}
}
@ -58,25 +73,18 @@ class noodlesLibImagePath
return null;
}
public static function getPath($m = '')
/**
* @return null|string The image path
*/
public static function getPath(): ?string
{
$files = self::getArray($m);
$files = self::getArray();
foreach ($files as $k => $file) {
if (file_exists($files[$k]['dir'])) {
if (is_string($files[$k]['dir']) && file_exists($files[$k]['dir'])) {
return $files[$k]['dir'];
}
}
return null;
}
public static function getSize($m = '')
{
if (!($img = self::getPath($m))) {
return ['w' => 16, 'h' => 16];
}
$info = getimagesize($img);
return ['w' => $info[0], 'h' => floor($info[1] / 3)];
}
}

View File

@ -10,62 +10,48 @@
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_CONTEXT_ADMIN')) {
return null;
declare(strict_types=1);
namespace Dotclear\Plugin\noodles;
use dcCore;
use dcNsProcess;
use Exception;
class Install extends dcNsProcess
{
public static function init(): bool
{
static::$init = defined('DC_CONTEXT_ADMIN')
&& My::phpCompliant()
&& is_string(dcCore::app()->plugins->moduleInfo(My::id(), 'version'))
&& dcCore::app()->newVersion(My::id(), dcCore::app()->plugins->moduleInfo(My::id(), 'version'));
return static::$init;
}
public static function process(): bool
{
if (!static::$init) {
return false;
}
try {
$s = dcCore::app()->blog?->settings->get(My::id());
if (is_null($s)) {
return false;
}
# Set module settings
$s->put('active', false, 'boolean', 'enable module', false, true);
$s->put('api', 'http://www.gravatar.com/', 'string', 'external API', false, true);
$s->put('local', false, 'boolean', 'use local image', false, true);
$s->put('settings', '[]', 'string', 'noodles settings', false, true);
return true;
} catch (Exception $e) {
dcCore::app()->error->add($e->getMessage());
return false;
}
}
}
$mod_conf = [
[
'noodles_active',
false,
'boolean',
'Enable extension',
],
[
'noodles_api',
'http://www.gravatar.com/',
'string',
'external API to use',
],
[
'noodles_image',
'',
'string',
'Image filename',
],
[
'noodles_object',
'',
'string',
'Noodles behaviors',
],
];
# -- Nothing to change below --
try {
# Check module version
if (!dcCore::app()->newVersion(
basename(__DIR__),
dcCore::app()->plugins->moduleInfo(basename(__DIR__), 'version')
)) {
return null;
}
# Set module settings
dcCore::app()->blog->settings->addNamespace(basename(__DIR__));
foreach ($mod_conf as $v) {
dcCore::app()->blog->settings->__get(basename(__DIR__))->put(
$v[0],
$v[1],
$v[2],
$v[3],
false,
true
);
}
return true;
} catch (Exception $e) {
dcCore::app()->error->add($e->getMessage());
return false;
}

View File

@ -10,262 +10,168 @@
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_CONTEXT_ADMIN')) {
return null;
}
declare(strict_types=1);
dcPage::check(dcCore::app()->auth->makePermissions([dcAuth::PERMISSION_CONTENT_ADMIN]));
namespace Dotclear\Plugin\noodles;
include __DIR__ . '/inc/_default_noodles.php';
use dcCore;
use dcNsProcess;
use dcPage;
use Exception;
$s = dcCore::app()->blog->settings->noodles;
use form;
$__noodles = noodles::decode($s->noodles_object);
if ($__noodles->isEmpty()) {
$__noodles = $__default_noodles;
} else {
$default_noodles_array = $__default_noodles->noodles();
foreach ($default_noodles_array as $id => $noodle) {
if ($__noodles->exists($id)) {
continue;
}
$__noodles->set($id, $noodle);
class Manage extends dcNsProcess
{
public static function init(): bool
{
static::$init = defined('DC_CONTEXT_ADMIN')
&& My::phpCompliant()
&& !is_null(dcCore::app()->auth) && !is_null(dcCore::app()->blog)
&& dcCore::app()->auth->check(dcCore::app()->auth->makePermissions([
dcCore::app()->auth::PERMISSION_CONTENT_ADMIN,
]), dcCore::app()->blog->id);
return static::$init;
}
}
$public_path = path::real(dcCore::app()->blog->public_path);
if (!is_dir($public_path) || !is_writable($public_path)) {
$public_path = false;
}
$default_images = files::scandir(__DIR__ . '/default-templates/img/');
if (!is_array($default_images)) {
$default_images = [];
}
$default_image = $s->noodles_image;
$combo_api = [
'gravatar' => 'http://www.gravatar.com/',
'libravatar' => 'http://cdn.libravatar.org/',
];
$combo_place = [
__('Begin') => 'prepend',
__('End') => 'append',
__('Before') => 'before',
__('After') => 'after',
];
$combo_rating = [
'G' => 'g',
'PG' => 'pg',
'R' => 'r',
'X' => 'x',
];
$combo_size = [
'16px' => 16,
'24px' => 24,
'32px' => 32,
'48px' => 48,
'56px' => 56,
'64px' => 64,
'92px' => 92,
'128px' => 128,
'256px' => 256,
];
if (!empty($_POST['save'])) {
try {
$public_file = $public_path . '/noodles-default-image.png';
$s->put('noodles_active', !empty($_POST['noodles_active']), 'boolean');
$s->put('noodles_api', $_POST['noodles_api'], 'string');
// user upload image
if ($_POST['noodles_image'] == 'user' && !empty($public_path)) {
if (2 == $_FILES['noodlesuserfile']['error']) {
throw new Exception(__('Maximum file size exceeded'));
}
if (0 != $_FILES['noodlesuserfile']['error']) {
throw new Exception(__('Something went wrong while download file'));
}
if (!in_array($_FILES['noodlesuserfile']['type'], ['image/png', 'image/x-png'])) {
throw new Exception(__('Image must be in png format'));
}
if (move_uploaded_file($_FILES['noodlesuserfile']['tmp_name'], $public_file)) {
$s->put('noodles_image', 1, 'boolean');
} else {
throw new Exception(__('Failed to save image'));
}
// Default gravatar.com avatar
} elseif ($_POST['noodles_image'] == 'gravatar.com') {
$s->put('noodles_image', 0, 'boolean');
// existsing noodles image on blog
} elseif ($_POST['noodles_image'] == 'existing') {
$s->put('noodles_image', 1, 'boolean');
// noodles image
} elseif (preg_match('/^gravatar-[0-9]+.png$/', $_POST['noodles_image']) && !empty($public_path)) {
$source = dirname(__FILE__) . '/default-templates/img/' . $_POST['noodles_image'];
if (!file_exists($source)) {
throw new Exception(__('Something went wrong while search file'));
}
if (files::putContent($public_file, file_get_contents($source))) {
$s->put('noodles_image', 1, 'boolean');
}
// Default gravatar.com avatar
} else { //if ($_POST['noodles_image'] == 'gravatar.com') {
$s->put('noodles_image', 0, 'boolean');
public static function process(): bool
{
if (!static::$init) {
return false;
}
// behaviors
foreach ($_POST['noodle'] as $id => $bloc) {
$__noodles->get($id)
->set('active', !empty($bloc['active']))
->set('rating', $bloc['rating'] ?? 'g')
->set('size', $bloc['size'] ?? '16')
->set('css', $bloc['css'] ?? '')
->set('target', $bloc['target'] ?? '')
->set('place', $bloc['place'] ?? 'prepend')
;
// nullsafe check
if (is_null(dcCore::app()->blog) || is_null(dcCore::app()->adminurl)) {
return false;
}
$s->put('noodles_object', $__noodles->encode(), 'string');
dcCore::app()->blog->triggerBlog();
dcAdminNotices::addSuccessNotice(__('Configuration successfully updated'));
dcCore::app()->adminurl->redirect('admin.plugin.noodles');
} catch (Exception $e) {
dcCore::app()->error->add($e->getMessage());
if (empty($_POST['save'])) {
return true;
}
$s = dcCore::app()->blog->settings->get(My::id());
$targets = Targets::instance();
try {
$s->put('active', !empty($_POST['noodles_active']), 'boolean');
$s->put('api', $_POST['noodles_api'], 'string');
$s->put('local', !empty($_POST['noodles_local']), 'boolean');
// behaviors
foreach ($_POST['noodle'] as $id => $bloc) {
$target = $targets->get($id);
if (is_null($target)) {
continue;
}
$target
->setActive(!empty($bloc['active']))
->setRating($bloc['rating'] ?? 'g')
->setSize($bloc['size'] ?? '16')
->setCss($bloc['css'] ?? '')
->setTarget($bloc['target'] ?? '')
->setPlace($bloc['place'] ?? 'prepend')
;
}
$targets->export();
dcCore::app()->blog->triggerBlog();
dcPage::addSuccessNotice(__('Configuration successfully updated'));
dcCore::app()->adminurl->redirect('admin.plugin.noodles');
} catch (Exception $e) {
dcCore::app()->error->add($e->getMessage());
}
return true;
}
}
echo '<html><head><title>' . __('Noodles') . '</title></head><body>' .
dcPage::breadcrumb([
html::escapeHTML(dcCore::app()->blog->name) => '',
__('Noodles') => '',
__('Plugin configuration') => '',
]) .
dcPage::notices() . '
<form id="module_config" action="' .
dcCore::app()->adminurl->get('admin.plugin.noodles') .
'" method="post" enctype="multipart/form-data">
<h3>' . sprintf(__('Configure "%s"'), __('Noodles')) . '</h3>
<div class="fieldset"><h4>' . __('Activation') . '</h4>
<p><label for="noodles_active">' .
form::checkbox('noodles_active', 1, $s->noodles_active) .
__('Enable plugin noodles on this blog') . '</label></p>
<p><label for="noodles_api" class="classic">' . __('API:') . ' </label>' .
form::combo('noodles_api', $combo_api, $s->noodles_api) . '</p>
</div>
<div class="fieldset"><h4>' . __('Avatar') . '</h4>
<p>' . __('Select default avatar to display on unknown users.') . '</p>';
if (!empty($public_path)) {
echo '<div class="one-box">';
sort($default_images);
$i = 0;
foreach ($default_images as $f) {
if (!preg_match('/gravatar-[0-9]+.png/', $f)) {
continue;
public static function render(): void
{
if (!static::$init) {
return;
}
$i++;
$sz = getimagesize(dirname(__FILE__) . '/default-templates/img/' . $f);
$sz[2] = files::size(filesize(dirname(__FILE__) . '/default-templates/img/' . $f));
// nullsafe check
if (is_null(dcCore::app()->blog) || is_null(dcCore::app()->adminurl)) {
return;
}
$targets = Targets::instance();
$path = Image::getPath();
if (is_null($path)) {
$note = __('Local default avatar is not reachable!');
} else {
$note = '<a href="' . Image::getURL() . '">' . __('See local default avatar.') . '</a>';
}
dcPage::openModule(My::name());
echo
dcPage::breadcrumb([
__('Plugin') => '',
My::name() => '',
]) .
dcPage::notices() . '
<form id="module_config" action="' .
dcCore::app()->adminurl->get('admin.plugin.' . My::id()) .
'" method="post" enctype="multipart/form-data">
<h3>' . sprintf(__('Configure "%s"'), __('Noodles')) . '</h3>
<div class="fieldset"><h4>' . __('Settings') . '</h4>
<p><label for="noodles_active">' .
form::checkbox('noodles_active', 1, $targets->active) .
__('Enable plugin noodles on this blog') . '</label></p>
<p class="field"><label for="noodles_api" class="classic">' . __('Image API:') . ' </label>' .
form::combo('noodles_api', Combo::api(), $targets->api) . '</p>
<p class="field"><label for="noodles_local" class="classic">' . __('Default image:') . ' </label>' .
form::combo('noodles_local', Combo::local(), $targets->local) . '</p>
<p class="form-note">' . $note . '</p>
<p class="form-note">' . sprintf(__('You can add your own default avatar by adding file "%s" in media manager.'), 'img/' . My::IMAGE) . '</p>
</div>
<div class="fieldset"><h4>' . __('Behaviors') . '</h4>
<div class="table-outer">
<table><caption class="hidden">' . __('Behaviors list') . '</caption><tbody><tr>
<th colspan="2" class="first">' . __('Search area') . '</th>
<th scope="col">' . __('Size') . '</th>
<th scope="col">' . __('Rating') . '</th>
<th scope="col">' . __('PHP') . '</th>
<th scope="col">' . __('JS') . '</th>
<th scope="col">' . __('Target') . '</th>
<th scope="col">' . __('Place') . '</th>
<th colspan="2" scope="col">' . __('Adjust avatar CSS') . '</th>
</tr>';
foreach ($targets->dump() as $target) {
echo '
<tr class="line">
<td>' . form::checkbox(['noodle[' . $target->id . '][active]', 'ck_' . $target->id], 1, $target->active()) . '</td>
<td class="nowrap" scope="row"><label for="ck_' . $target->id . '">' . $target->name . '</label></td>
<td>' . form::combo(['noodle[' . $target->id . '][size]'], Combo::size(), $target->size()) . '</td>
<td>' . form::combo(['noodle[' . $target->id . '][rating]'], Combo::rating(), $target->rating()) . '</td>
<td>' . (
$target->hasPhpCallback() ?
'<img alt="ok" src="images/check-on.png" />' :
'<img alt="nok" src="images/check-off.png" />'
) . '</td>
<td><img alt="ok" src="images/check-on.png" /></td>
<td>' . form::field(['noodle[' . $target->id . '][target]'], 20, 255, $target->target()) . '</td>
<td>' . form::combo(['noodle[' . $target->id . '][place]'], Combo::place(), $target->place()) . '</td>
<td>' . form::field(['noodle[' . $target->id . '][css]'], 50, 255, $target->css()) . '</td>
<td> .noodles-' . $target->id . '{}</td>
</tr>';
}
echo '
<div class="fieldset box">
<p>' . form::radio(['noodles_image', 'noodles_image_' . $i], $f) . '
<label class="classic" for="noodles_image_' . $i . '">' . basename($f) . '</label></p>
<div class="two-box"><div class="box">
<p><img src="' . dcPage::getPF('noodles/default-templates/img/' . $f) . '" alt="" /></p>
</div><div class="box">
<p>' . $sz[0] . 'x' . $sz[1] . '<br />' . $sz[2] . '</p>
</div></div>
</div>';
</tbody></table></div>
<p class="form-note">' . __('Target and Place are for javascript.') . '</p>
</div>
<p class="clear">
<input type="submit" value="' . __('Save') . ' (s)" accesskey="s" name="save" /> ' .
dcCore::app()->formNonce() . '</p>
</form>';
dcPage::closeModule();
}
echo '</div>';
}
echo '<div class="one-box">';
if (null !== ($default_image_path = noodlesLibImagePath::getPath('noodles'))) {
$sz = getimagesize($default_image_path);
$sz[2] = files::size(filesize($default_image_path));
echo '
<div class="fieldset box">
<p>' . form::radio(['noodles_image', 'public_image'], 'existing', !empty($default_image)) . '
<label class="classic" for="public_image">' . __('Blog default image') . '</label></p>
<div class="two-box"><div class="box">
<p><img src="' . noodlesLibImagePath::getUrl('noodles') . '?' . rand() . '" alt="" /></p>
</div><div class="box">
<p>' . $sz[0] . 'x' . $sz[1] . '<br />' . $sz[2] . '</p>
</div></div>
</div>';
}
if (!empty($public_path)) {
echo '
<div class="fieldset box">
<p>' . form::radio(['noodles_image', 'upload_image'], 'user') . '
<label class="classic" for="upload_image">' . __('Upload a new avatar') . '</label></p>
<p>' . form::hidden(['MAX_FILE_SIZE'], 30000) . '
<input type="file" name="noodlesuserfile" />
<p class="form-note">' . __('Image must be in "png" format and have a maximum file size of 30Ko') . '</p>
</div>';
}
echo '
<div class="fieldset box">
<p>' . form::radio(['noodles_image', 'com_image'], 'gravatar.com', empty($default_image)) . '
<label class="classic">' . __('API default image') . '</label></p>
</div>';
if (empty($public_path)) {
echo '<p class="info">' . __('Public directory is not writable, you can not use custom gravatar.') . '</p>';
}
echo '
</div></div>
<div class="fieldset"><h4>' . __('Behaviors') . '</h4>
<div class="table-outer">
<table><caption class="hidden">' . __('Behaviors list') . '</caption><tbody><tr>
<th colspan="2" class="first">' . __('Search area') . '</th>
<th scope="col">' . __('Size') . '</th>
<th scope="col">' . __('Rating') . '</th>
<th scope="col">' . __('PHP') . '</th>
<th scope="col">' . __('JS') . '</th>
<th scope="col">' . __('Target') . '</th>
<th scope="col">' . __('Place') . '</th>
<th colspan="2" scope="col">' . __('Adjust avatar CSS') . '</th>
</tr>';
foreach ($__noodles->noodles() as $noodle) {
echo '
<tr class="line">
<td>' . form::checkbox(['noodle[' . $noodle->id() . '][active]', 'ck_' . $noodle->id()], 1, $noodle->active) . '</td>
<td class="nowrap" scope="row"><label for="ck_' . $noodle->id() . '">' . $noodle->name() . '</label></td>
<td>' . form::combo(['noodle[' . $noodle->id() . '][size]'], $combo_size, $noodle->size) . '</td>
<td>' . form::combo(['noodle[' . $noodle->id() . '][rating]'], $combo_rating, $noodle->rating) . '</td>
<td>' . (
$noodle->hasPhpCallback() ?
'<img alt="ok" src="images/check-on.png" />' :
'<img alt="nok" src="images/check-off.png" />'
) . '</td>
<td><img alt="ok" src="images/check-on.png" /></td>
<td>' . form::field(['noodle[' . $noodle->id() . '][target]'], 20, 255, $noodle->target) . '</td>
<td>' . form::combo(['noodle[' . $noodle->id() . '][place]'], $combo_place, $noodle->place) . '</td>
<td>' . form::field(['noodle[' . $noodle->id() . '][css]'], 50, 255, $noodle->css) . '</td>
<td> .noodles-' . $noodle->id() . '{}</td>
</tr>';
}
echo '
</tbody></table></div>
<p class="form-note">' . __('Target and Place are for javascript.') . '</p>
</div>
<p class="clear">
<input type="submit" value="' . __('Save') . ' (s)" accesskey="s" name="save" /> ' .
dcCore::app()->formNonce() . '</p>
</form>
</body></html>';

63
src/My.php 100644
View File

@ -0,0 +1,63 @@
<?php
/**
* @brief noodles, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis 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\noodles;
use dcCore;
/**
* Plugin definitions
*/
class My
{
/** @var string Required php version */
public const PHP_MIN = '8.1';
/** @var string Default image name */
public const IMAGE = 'default-avatar.png';
/**
* 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 php version
*/
public static function phpCompliant(): bool
{
return version_compare(phpversion(), self::PHP_MIN, '>=');
}
}

View File

@ -10,38 +10,57 @@
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_RC_PATH')) {
return null;
declare(strict_types=1);
namespace Dotclear\Plugin\noodles;
use dcCore;
use dcNsProcess;
class Prepend extends dcNsProcess
{
public static function init(): bool
{
static::$init = My::phpCompliant();
return static::$init;
}
public static function process(): bool
{
if (!static::$init) {
return false;
}
// public URL for noodles files
dcCore::app()->url->register(
'noodles_file',
'noodles',
'^noodles/(.+)$',
[UrlHandler::class, 'file']
);
// public URL for noodles service
dcCore::app()->url->register(
'noodles_service',
'noodle',
'^noodle/$',
[UrlHandler::class, 'service']
);
// public URL for targets CSS contents
dcCore::app()->url->register(
'noodles_css',
'noodles.css',
'^noodles\.css',
[UrlHandler::class, 'css']
);
// public URL for targets JS contents
dcCore::app()->url->register(
'noodles_js',
'noodles.js',
'^noodles\.js',
[UrlHandler::class, 'js']
);
return true;
}
}
Clearbricks::lib()->autoload([
'noodlesImg' => __DIR__ . '/inc/lib.noodles.img.php',
'noodlesLibImagePath' => __DIR__ . '/inc/lib.image.path.php',
]);
dcCore::app()->blog->settings->addNamespace('noodles');
dcCore::app()->url->register(
'noodlesmodule',
'noodles',
'^noodles/(.+)$',
['urlNoodles', 'noodles']
);
dcCore::app()->url->register(
'noodlesservice',
'noodle',
'^noodle/$',
['urlNoodles', 'service']
);
dcCore::app()->url->register(
'noodlescss',
'noodles.css',
'^noodles\.css',
['urlNoodles', 'css']
);
dcCore::app()->url->register(
'noodlesjs',
'noodles.js',
'^noodles\.js',
['urlNoodles', 'js']
);

198
src/Target.php 100644
View File

@ -0,0 +1,198 @@
<?php
/**
* @brief noodles, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis 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\noodles;
/**
* Target definition.
*/
class Target
{
/** @var bool The noodle activation */
private bool $active = false;
/** @var string The noodle rating */
private string $rating = 'g';
/** @var int The noodle size */
private int $size = 16;
/** @var string The noodle css */
private string $css = '';
/** @var string The noodle target */
private string $target = '';
/** @var string The noodle place */
private string $place = 'prepend';
/**
* Constructor sets main properties.
*
* @param string $id The noodle ID
* @param string $name The noodle name
* @param mixed $js_callback The js callback
* @param mixed $php_callback The php callback
*/
public function __construct(
public readonly string $id,
public readonly string $name,
public readonly mixed $js_callback = null,
public readonly mixed $php_callback = null
) {
}
/**
* Export settings in order to save it to blog settings.
*
* @return array<string,mixed> The settings
*/
public function exportSettings(): array
{
return get_object_vars($this);
}
/**
* Import noodle settings from blog settings.
*
* @param array<string,mixed> $settings The settings
*
* @return bool True on success
*/
public function importSettings(array $settings): bool
{
if ($this->id !== ($settings['id'] ?? null)) {
return false;
}
$this->setActive($settings['active'] ?? $this->active());
$this->setRating($settings['rating'] ?? $this->rating());
$this->setSize($settings['size'] ?? $this->size());
$this->setCss($settings['css'] ?? $this->css());
$this->settarget($settings['target'] ?? $this->target());
$this->setPlace($settings['place'] ?? $this->place());
return true;
}
public function jsCallback(string $content = ''): string
{
if (is_callable($this->js_callback)) {
$res = call_user_func($this->js_callback, $this, $content);
return is_string($res) ? $res : '';
}
return '';
}
public function hasJsCallback(): bool
{
return !empty($this->js_callback);
}
public function phpCallback(): void
{
if (is_callable($this->php_callback)) {
call_user_func($this->php_callback, $this);
}
}
public function hasPhpCallback(): bool
{
return !empty($this->php_callback);
}
public function active(): bool
{
return $this->active;
}
public function setActive(mixed $value): Target
{
$this->active = !empty($value);
return $this;
}
public function rating(): string
{
return $this->rating;
}
public function setRating(mixed $value): Target
{
if (is_string($value) && in_array($value, ['g', 'pg', 'r', 'x'])) {
$this->rating = $value;
}
return $this;
}
public function size(): int
{
return (int) $this->size;
}
public function setSize(mixed $value): Target
{
if (is_numeric($value) && in_array((int) $value, [16, 24, 32, 48, 56, 64, 92, 128, 256])) {
$this->size = (int) $value;
}
return $this;
}
public function css(): string
{
return $this->css;
}
public function setCss(mixed $value): Target
{
if (is_string($value)) {
$this->css = $value;
}
return $this;
}
public function target(): string
{
return $this->target;
}
public function setTarget(mixed $value): Target
{
if (is_string($value)) {
$this->target = $value;
}
return $this;
}
public function place(): string
{
return $this->place;
}
public function setPlace(mixed $value): Target
{
if (is_string($value) && in_array($value, ['append', 'prepend', 'before', 'after'])) {
$this->place = $value;
}
return $this;
}
}

View File

@ -0,0 +1,93 @@
<?php
/**
* @brief noodles, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis 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\noodles\Target;
use dcCore;
use Dotclear\Database\MetaRecord;
use Dotclear\Plugin\noodles\{
Image,
My,
Targets,
Target
};
/**
* Target rendering adapt to plugin authorMode.
*/
class AuthorMode
{
public static function authors(Target $target, string $content = ''): string
{
$ok = preg_match('@\/([^\/]*?)$@', $content, $m);
if (!$ok || !$m[1]) {
return '';
}
$rs = dcCore::app()->getUser($m[1]);
if ($rs->isEmpty()) {
return '';
}
$res = $rs->f('user_email');
return is_string($res) ? $res : '';
}
public static function author(Target $target): void
{
if ($target->active()) {
dcCore::app()->addBehavior('publicHeadContent', [self::class, 'publicHeadContent']);
}
}
public static function publicHeadContent(): void
{
$targets = Targets::instance();
$target = $targets->get('author');
if (is_null($target)
|| is_null(dcCore::app()->ctx)
|| is_null(dcCore::app()->blog)
|| dcCore::app()->ctx->__get('current_tpl') != 'author.html'
) {
return;
}
$u = dcCore::app()->ctx->__get('users');
if (!($u instanceof MetaRecord)) {
return;
}
$u = $u->f('user_id');
if (!is_string($u)) {
return;
}
$u = dcCore::app()->getUser($u)->f('user_email');
if (!is_string($u)) {
$u = '';
}
$d = $targets->local ? urlencode((string) Image::getUrl()) : '';
echo
'<script type="text/javascript">' . "\n" .
"//<![CDATA[\n" .
"$(function(){if(!document.getElementById){return;}\n" .
"$('" . $target->target() . "')." . $target->place() . "('" .
'<img class="noodles-comments" style="width:' . $target->size() . 'px;height:' . $target->size() . 'px;' . $target->css() . '"' .
'src="http://www.gravatar.com/avatar/' . md5($u) .
'?s=' . $target->size() . '&amp;r=' . $target->rating() . '&amp;d=' . $d . '" alt="" />' .
"');});" .
"\n//]]>\n" .
"</script>\n";
}
}

View File

@ -10,14 +10,23 @@
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_RC_PATH')) {
return null;
}
declare(strict_types=1);
class genericNoodles
namespace Dotclear\Plugin\noodles\Target;
use dcCore;
use Dotclear\Plugin\noodles\Target;
/**
* Target generic rendering.
*/
class Generic
{
public static function postURL($noodle, $content = '')
public static function postURL(Target $target, string $content = ''): string
{
if (is_null(dcCore::app()->blog)) {
return '';
}
$types = dcCore::app()->getPostTypes();
$reg = '@^' . str_replace('%s', '(.*?)', preg_quote(dcCore::app()->blog->url . $types['post']['public_url'])) . '$@';
$ok = preg_match($reg, $content, $m);
@ -29,140 +38,8 @@ class genericNoodles
return '';
}
return $rs->user_email;
}
}
# Miscellaneous
class othersNoodles
{
public static function publicPosts($noodle)
{
if (!$noodle->active) {
return null;
}
$bhv = $noodle->place == 'prepend' || $noodle->place == 'before' ?
'publicEntryBeforeContent' : 'publicEntryAfterContent';
dcCore::app()->addBehavior($bhv, ['othersNoodles', 'publicEntryContent']);
}
public static function publicEntryContent()
{
global $__noodles;
$m = dcCore::app()->ctx->posts->getAuthorEmail(false);
$c = $__noodles->posts->css;
$s = $__noodles->posts->size;
$r = $__noodles->posts->rating;
$d = dcCore::app()->blog->settings->noodles->noodles_image ?
urlencode(noodlesLibImagePath::getUrl('noodles')) : '';
echo
'<img class="noodles-posts" style="width:' . $s . 'px;height:' . $s . 'px;' . $c . '"' .
'src="http://www.gravatar.com/avatar/' . md5($m) .
'?s=' . $s . '&amp;r=' . $r . '&amp;d=' . $d . '" alt="" />';
}
public static function publicComments($noodle)
{
if (!$noodle->active) {
return null;
}
$bhv = $noodle->place == 'prepend' || $noodle->place == 'before' ?
'publicCommentBeforeContent' : 'publicCommentAfterContent';
dcCore::app()->addBehavior($bhv, ['othersNoodles', 'publicCommentContent']);
}
public static function publicCommentContent()
{
global $__noodles;
$m = dcCore::app()->ctx->comments->getEmail(false);
$c = $__noodles->comments->css;
$s = $__noodles->comments->size;
$r = $__noodles->comments->rating;
$d = dcCore::app()->blog->settings->noodles->noodles_image ?
urlencode(noodlesLibImagePath::getUrl('noodles')) : '';
echo
'<img class="noodles-comments" style="width:' . $s . 'px;height:' . $s . 'px;' . $c . '"' .
'src="http://www.gravatar.com/avatar/' . md5($m) .
'?s=' . $s . '&amp;r=' . $r . '&amp;d=' . $d . '" alt="" />';
}
}
# Plugin Widgets
class widgetsNoodles
{
public static function lastcomments($noodle, $content = '')
{
$ok = preg_match('@\#c([0-9]+)$@', urldecode($content), $m);
if (!$ok || !$m[1]) {
return '';
}
$rs = dcCore::app()->blog->getComments(['no_content' => 1, 'comment_id' => $m[1], 'limit' => 1]);
if (!$rs->isEmpty()) {
return $rs->comment_email;
}
return '';
}
}
# Plugin authorMode
class authormodeNoodles
{
public static function authors($noodle, $content = '')
{
$ok = preg_match('@\/([^\/]*?)$@', $content, $m);
if (!$ok || !$m[1]) {
return '';
}
$rs = dcCore::app()->getUser($m[1]);
if ($rs->isEmpty()) {
return '';
}
return $rs->user_email;
}
public static function author($noodle)
{
if ($noodle->active) {
dcCore::app()->addBehavior('publicHeadContent', ['authormodeNoodles', 'publicHeadContent']);
}
}
public static function publicHeadContent()
{
global $__noodles;
if (dcCore::app()->ctx->current_tpl != 'author.html') {
return null;
}
$id = dcCore::app()->ctx->users->user_id;
$u = dcCore::app()->getUser($id);
$m = $u->user_email;
$c = $__noodles->author->css;
$s = $__noodles->author->size;
$r = $__noodles->author->rating;
$d = dcCore::app()->blog->settings->noodles->noodles_image ?
urlencode(noodlesLibImagePath::getUrl('noodles')) : '';
echo
'<script type="text/javascript">' . "\n" .
"//<![CDATA[\n" .
"$(function(){if(!document.getElementById){return;}\n" .
"$('" . $__noodles->author->target . "')." . $__noodles->author->place . "('" .
'<img class="noodles-comments" style="width:' . $s . 'px;height:' . $s . 'px;' . $c . '"' .
'src="http://www.gravatar.com/avatar/' . md5($m) .
'?s=' . $s . '&amp;r=' . $r . '&amp;d=' . $d . '" alt="" />' .
"');});" .
"\n//]]>\n" .
"</script>\n";
$res = $rs->f('user_email');
return is_string($res) ? $res : '';
}
}

View File

@ -0,0 +1,109 @@
<?php
/**
* @brief noodles, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis 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\noodles\Target;
use dcCore;
use Dotclear\Database\MetaRecord;
use Dotclear\Plugin\noodles\{
Image,
My,
Targets,
Target
};
/**
* Target complete rendering.
*/
class Other
{
public static function publicPosts(Target $target): void
{
if (!$target->active()) {
return;
}
$bhv = $target->place() == 'prepend' || $target->place() == 'before' ?
'publicEntryBeforeContent' : 'publicEntryAfterContent';
dcCore::app()->addBehavior($bhv, [self::class, 'publicEntryContent']);
}
public static function publicEntryContent(): void
{
$targets = Targets::instance();
$target = $targets->get('posts');
if (is_null($target)
|| is_null(dcCore::app()->ctx)
|| is_null(dcCore::app()->blog)
|| dcCore::app()->ctx->__get('current_tpl') != 'post.html'
) {
return;
}
$m = dcCore::app()->ctx->__get('posts');
if (!($m instanceof MetaRecord)) {
return;
}
$m = $m->__call('getAuthorEmail', [false]);
if (!is_string($m)) {
return;
}
echo
'<img class="noodles-posts" style="width:' . $target->size() . 'px;height:' . $target->size() . 'px;' . $target->css() . '"' .
'src="http://www.gravatar.com/avatar/' . md5($m) .
'?s=' . $target->size() . '&amp;r=' . $target->rating() . '&amp;d=' . ($targets->local ? urlencode((string) Image::getUrl()) : '') . '" alt="" />';
}
public static function publicComments(Target $target): void
{
if (!$target->active()) {
return;
}
$bhv = $target->place() == 'prepend' || $target->place() == 'before' ?
'publicCommentBeforeContent' : 'publicCommentAfterContent';
dcCore::app()->addBehavior($bhv, [self::class, 'publicCommentContent']);
}
public static function publicCommentContent(): void
{
$targets = Targets::instance();
$target = $targets->get('comments');
if (is_null($target)
|| is_null(dcCore::app()->ctx)
|| is_null(dcCore::app()->blog)
|| dcCore::app()->ctx->__get('current_tpl') != 'post.html'
) {
return;
}
$m = dcCore::app()->ctx->__get('comments');
if (!($m instanceof MetaRecord)) {
return;
}
$m = $m->__call('getEmail', [false]);
if (!is_string($m)) {
return;
}
echo
'<img class="noodles-comments" style="width:' . $target->size() . 'px;height:' . $target->size() . 'px;' . $target->css() . '"' .
'src="http://www.gravatar.com/avatar/' . md5($m) .
'?s=' . $target->size() . '&amp;r=' . $target->rating() . '&amp;d=' . ($targets->local ? urlencode((string) Image::getUrl()) : '') . '" alt="" />';
}
}

View File

@ -0,0 +1,43 @@
<?php
/**
* @brief noodles, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis 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\noodles\Target;
use dcCore;
use Dotclear\Plugin\noodles\Target;
/**
* Target rendering adapt to plugin Widgets.
*/
class Widgets
{
public static function lastcomments(Target $target, string $content = ''): string
{
if (is_null(dcCore::app()->blog)) {
return '';
}
$ok = preg_match('@\#c([0-9]+)$@', urldecode($content), $m);
if (!$ok || !$m[1]) {
return '';
}
$rs = dcCore::app()->blog->getComments(['no_content' => 1, 'comment_id' => $m[1], 'limit' => 1]);
if ($rs->isEmpty()) {
return '';
}
$res = $rs->f('comment_email');
return is_string($res) ? $res : '';
}
}

View File

@ -10,211 +10,313 @@
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_RC_PATH')) {
return null;
}
declare(strict_types=1);
class noodles
namespace Dotclear\Plugin\noodles;
use dcCore;
use Exception;
/**
* Targets stack.
*/
final class Targets
{
private $noodles = [];
/** @var Targets The noodles instance */
private static $instance;
public static function decode($s)
/** @ var bool The activation */
public readonly bool $active;
/** @ var string The API URL */
public readonly string $api;
/** @ var bool Use local image */
public readonly bool $local;
/** @var array<string,Target> The noodles stack */
private array $targets = [];
/**
* Constructor load noodles from behaviors.
*/
protected function __construct()
{
$o = @unserialize(base64_decode($s));
if ($o instanceof noodles) {
return $o;
// try to read main settings
$active = $api = $local = null;
if (!is_null(dcCore::app()->blog)) {
$s = dcCore::app()->blog->settings->get(My::id());
$active = $s->get('active');
$api = $s->get('api');
$local = $s->get('local');
}
return new self();
// set main settings
$this->active = is_bool($active) ? $active : false;
$this->api = is_string($api) ? $api : 'http://www.gravatar.com/';
$this->local = is_bool($local) ? $local : false;
// add noodles
dcCore::app()->callBehavior('TargetsConstruct', $this);
// add default noodles
$this->registerDefault();
// set noodles settings
$this->import();
}
public function encode()
protected function __clone()
{
return base64_encode(serialize($this));
throw new Exception('nope');
}
public function add($id, $name, $js_callback, $php_callback = null)
public function __wakeup()
{
$this->noodles[$id] = new noodle($id, $name, $js_callback, $php_callback);
return $this->noodles[$id];
throw new Exception('nope');
}
public function get($id)
/**
* Get singleton instance.
*
* @return Targets Targets instance
*/
public static function instance(): Targets
{
return $this->noodles[$id] ?? null;
}
public function __get($id)
{
return $this->get($id);
}
public function set($id, $noodle)
{
return $this->noodles[$id] = $noodle;
}
public function __set($id, $noodle)
{
return $this->set($id, $noodle);
}
public function exists($id)
{
return isset($this->noodles[$id]);
}
public function isEmpty()
{
return !count($this->noodles);
}
public function noodles()
{
return $this->noodles;
}
}
class noodle
{
private $id;
private $name;
private $js_callback;
private $php_callback;
private $settings = [
'active' => 0,
'rating' => 'g',
'size' => 16,
'target' => '',
'place' => 'prepend',
];
public function __construct($id, $name, $js_callback, $php_callback = null)
{
$this->id = $id;
$this->name = $name;
$this->js_callback = $js_callback;
$this->php_callback = $php_callback;
}
public function id()
{
return $this->id;
}
public function name()
{
return $this->name;
}
public function jsCallback($g, $content = '')
{
if (!is_callable($this->js_callback)) {
return null;
if (!is_a(self::$instance, Targets::class)) {
self::$instance = new Targets();
}
return call_user_func($this->js_callback, $g, $content);
return self::$instance;
}
public function hasJsCallback()
/**
* Import noodles settings from blog settings.
*/
public function import(): void
{
return !empty($this->js_callback);
}
public function phpCallback()
{
if (!is_callable($this->php_callback)) {
return null;
$s = dcCore::app()->blog?->settings->get(My::id())->get('settings');
if (!is_string($s)) {
return;
}
return call_user_func($this->php_callback, $this);
}
public function hasPhpCallback()
{
return !empty($this->php_callback);
}
public function set($type, $value)
{
switch ($type) {
case 'active':
$this->settings['active'] = abs((int) $value);
break;
case 'rating':
$this->settings['rating'] = in_array($value, ['g', 'pg', 'r', 'x']) ? $value : 'g';
break;
case 'size':
$this->settings['size'] = in_array($value, [16, 24, 32, 48, 56, 64, 92, 128, 256]) ? $value : 16;
break;
case 'css':
$this->settings['css'] = (string) $value;
break;
case 'target':
$this->settings['target'] = (string) $value;
break;
case 'place':
$this->settings['place'] = in_array($value, ['append', 'prepend', 'before', 'after']) ? $value : 'prepend';
break;
$targets = json_decode($s, true);
if (!is_array($targets)) {
return;
}
foreach ($targets as $id => $settings) {
if (is_array($settings) && isset($this->targets[$id])) {
$this->targets[$id]->importSettings($settings);
}
}
}
/**
* Export noodles settings to blog settings.
*/
public function export(): void
{
$targets = [];
foreach ($this->targets as $target) {
$targets[$target->id] = $target->exportSettings();
}
dcCore::app()->blog?->settings->get(My::id())->put('settings', json_encode($targets), 'string');
}
/**
* Get a noodle.
*
* @param string $id The noodle ID
*
* @return null|Target The noodle instance
*/
public function get(string $id): ?Target
{
return $this->targets[$id] ?? null;
}
/**
* Add a noodle to the stack.
*
* @param Target $target The noodle instance
*
* @return Targets The noodles stack
*/
public function set(Target $target): Targets
{
$this->targets[$target->id] = $target;
return $this;
}
public function active($value)
/**
* Check if a noodle exists.
*
* @param string $id The noodle ID
*
* @return bool True on exists
*/
public function exists(string $id): bool
{
return $this->set('active', $value);
return isset($this->targets[$id]);
}
public function rating($value)
/**
* Get all noodles.
*
* @return array<string,Target> The noodles stack
*/
public function dump(): array
{
return $this->set('rating', $value);
return $this->targets;
}
public function size($value)
/**
* Add default noodles to the stack.
*/
private function registerDefault(): void
{
return $this->set('size', $value);
}
if (is_null(dcCore::app()->blog)) {
return;
}
public function css($value)
{
return $this->set('css', $value);
}
# Posts (by public behavior)
$this->set(
(new Target(
id: 'posts',
name: __('Entries'),
php_callback: [Target\Other::class, 'publicPosts']
))
->setSize(48)
->setCss('float:right;margin:4px;')
);
public function target($value)
{
return $this->set('target', $value);
}
# Comments (by public behavior)
$this->set(
(new Target(
id: 'comments',
name: __('Comments'),
php_callback: [Target\Other::class, 'publicComments']
))
->setActive(true)
->setSize(48)
->setCss('float:left;margin:4px;')
);
public function place($value)
{
return $this->set('place', $value);
}
# Block with post title link (like homepage posts)
$this->set(
(new Target(
id: 'titlesposts',
name: __('Entries titles'),
js_callback: [Target\Generic::class, 'postURL']
))
->setTarget('.post-title a')
->setCss('margin-right:2px;')
);
public function __set($type, $value)
{
$this->set($type, $value);
}
if (dcCore::app()->plugins->moduleExists('widgets')) {
# Widget Selected entries
$this->set(
(new Target(
id: 'bestof',
name: __('Selected entries'),
js_callback: [Target\Generic::class, 'postURL']
))
->setTarget('.selected li a')
->setCss('margin-right:2px;')
);
public function get($type)
{
return $this->settings[$type] ?? null;
}
# Widget Last entries
$this->set(
(new Target(
id: 'lastposts',
name: __('Last entries'),
js_callback: [Target\Generic::class, 'postURL']
))
->setTarget('.lastposts li a')
->setCss('margin-right:2px;')
);
public function __get($type)
{
return $this->get($type);
# Widget Last comments
$this->set(
(new Target(
id: 'lastcomments',
name: __('Last comments'),
js_callback: [Target\Widgets::class, 'lastcomments']
))
->setActive(true)
->setTarget('.lastcomments li a')
->setCss('margin-right:2px;')
);
}
# Plugin auhtorMode
if (dcCore::app()->plugins->moduleExists('authorMode')
&& dcCore::app()->blog->settings->get('authormode')->get('authormode_active')
) {
$this->set(
(new Target(
id: 'authorswidget',
name: __('Authors widget'),
js_callback: [Target\AuthorMode::class, 'authors']
))
->setTarget('#authors ul li a')
->setCss('margin-right:2px;')
);
$this->set(
(new Target(
id: 'author',
name: __('Author'),
php_callback: [Target\AuthorMode::class, 'author']
))
->setActive(true)
->setSize(48)
->setTarget('.dc-author #content-info h2')
->setCss('clear:left; float:left;margin-right:2px;')
);
$this->set(
(new Target(
id: 'authors',
name: __('Authors'),
js_callback: [Target\AuthorMode::class, 'authors']
))
->setActive(true)
->setSize(32)
->setTarget('.dc-authors .author-info h2 a')
->setCss('clear:left; float:left; margin:4px;')
);
}
# Plugin rateIt
if (dcCore::app()->plugins->moduleExists('rateIt')
&& dcCore::app()->blog->settings->get('rateit')->get('rateit_active')
) {
$this->set(
(new Target(
id: 'rateitpostsrank',
name: __('Top rated entries'),
js_callback: [Target\Generic::class, 'postURL']
))
->setTarget('.rateitpostsrank.rateittypepost ul li a') // Only "post" type
->setCss('margin-right:2px;')
);
}
# Plugin lastpostsExtend
if (dcCore::app()->plugins->moduleExists('lastpostsExtend')) {
$this->set(
(new Target(
id: 'lastpostsextend',
name: __('Last entries (extend)'),
js_callback: [Target\Generic::class, 'postURL']
))
->setTarget('.lastpostsextend ul li a')
->setCss('margin-right:2px;')
);
}
}
}

View File

@ -10,50 +10,63 @@
* @copyright Jean-Christian Denis
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined('DC_CONTEXT_ADMIN')) {
return null;
declare(strict_types=1);
namespace Dotclear\Plugin\noodles;
use dcCore;
use dcNsProcess;
use Dotclear\Plugin\Uninstaller\Uninstaller;
class Uninstall extends dcNsProcess
{
public static function init(): bool
{
static::$init = defined('DC_CONTEXT_ADMIN');
return static::$init;
}
public static function process(): bool
{
if (!static::$init || !dcCore::app()->plugins->moduleExists('Uninstaller')) {
return false;
}
Uninstaller::instance()
->addUserAction(
'settings',
'delete_all',
My::id()
)
->addUserAction(
'plugins',
'delete',
My::id()
)
->addUserAction(
'versions',
'delete',
My::id()
)
->addDirectAction(
'settings',
'delete_all',
My::id()
)
->addDirectAction(
'plugins',
'delete',
My::id()
)
->addDirectAction(
'versions',
'delete',
My::id()
)
;
// no custom action
return false;
}
}
$this->addUserAction(
/* type */
'settings',
/* action */
'delete_all',
/* ns */
'noodles',
/* description */
__('delete all settings')
);
$this->addUserAction(
/* type */
'plugins',
/* action */
'delete',
/* ns */
'noodles',
/* description */
__('delete plugin files')
);
$this->addDirectAction(
/* type */
'settings',
/* action */
'delete_all',
/* ns */
'noodles',
/* description */
sprintf(__('delete all %s settings'), 'noodles')
);
$this->addDirectAction(
/* type */
'plugins',
/* action */
'delete',
/* ns */
'noodles',
/* description */
sprintf(__('delete %s plugin files'), 'noodles')
);

203
src/UrlHandler.php 100644
View File

@ -0,0 +1,203 @@
<?php
/**
* @brief noodles, a plugin for Dotclear 2
*
* @package Dotclear
* @subpackage Plugin
*
* @author Jean-Christian Denis 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\noodles;
use dcCore;
use dcUrlHandlers;
use Dotclear\Helper\File\Files;
use Dotclear\Helper\File\Path;
use Dotclear\Helper\Html\Html;
use Dotclear\Helper\Html\XmlTag;
use Exception;
class UrlHandler extends dcUrlHandlers
{
public static function css(?string $args): void
{
$css = '';
foreach (Targets::instance()->dump() as $target) {
if (!$target->active() || !$target->hasJsCallback()) {
continue;
}
$css .= '.noodles-' . $target->id . '{' . $target->css() . '}' . "\n";
}
header('Content-Type: text/css; charset=UTF-8');
echo $css;
exit;
}
public static function js(?string $args): void
{
if (is_null(dcCore::app()->blog)) {
self::p404();
}
$targets = [];
foreach (Targets::instance()->dump() as $target) {
if (!$target->active() || !$target->hasJsCallback()) {
continue;
}
$targets[] = '$(\'' . Html::escapeJS($target->target()) . '\').noodles({' .
' imgId:\'' . Html::escapeJS($target->id) . '\',' .
' imgPlace:\'' . Html::escapeJS($target->place()) . '\'' .
'});';
}
header('Content-Type: text/javascript; charset=UTF-8');
echo
"\$(function(){if(!document.getElementById){return;} \n" .
"\$.fn.noodles.defaults.service_url = '" . Html::escapeJS(dcCore::app()->blog->url . dcCore::app()->url->getBase('noodles_service') . '/') . "'; \n" .
"\$.fn.noodles.defaults.service_func = '" . Html::escapeJS('getNoodle') . "'; \n" .
implode("\n", $targets) .
"})\n";
exit;
}
public static function service(string $args): void
{
if (is_null(dcCore::app()->blog)) {
self::p404();
}
header('Content-Type: text/xml; charset=UTF-8');
$targets = Targets::instance();
$rsp = new XmlTag('rsp');
$i = !empty($_POST['noodleId']) ? $_POST['noodleId'] : null;
$c = !empty($_POST['noodleContent']) ? $_POST['noodleContent'] : null;
if (!$targets->active) {
$rsp->insertAttr('status', 'failed');
$rsp->insertNode(new XmlTag('message', __('noodles is disabled on this blog')));
echo $rsp->toXML(true);
return;
}
if ($i === null || $c === null) {
$rsp->insertAttr('status', 'failed');
$rsp->insertNode(new XmlTag('message', __('noodles failed because of missing informations')));
echo $rsp->toXML(true);
return;
}
try {
$target = $targets->get($i);
} catch (Exception $e) {
$rsp->insertAttr('status', 'failed');
$rsp->insertNode(new XmlTag('message', __('Failed to load default noodles')));
echo $rsp->toXML(true);
return;
}
if (is_null($target)) {
$rsp->insertAttr('status', 'failed');
$rsp->insertNode(new XmlTag('message', __('Failed to load noodle')));
echo $rsp->toXML(true);
return;
}
$m = $target->jsCallback($c);
$s = $target->size();
$r = $target->rating();
$d = $targets->local ? urlencode((string) Image::getUrl()) : '';
$u = $targets->api;
if (!$m) {
$m = 'nobody@nowhere.tld';
}
if (!$s) {
$s = 32;
}
if (!$r) {
$r = 'g';
}
$m = (string) md5(strtolower(trim($m)));
$im = new XmlTag('noodle');
$im->insertAttr('size', $s);
$im->insertAttr('src', sprintf('%savatar/%s?s=%s&amp;r=%s&amp;d=%s', $u, $m, $s, $r, $d));
$rsp->insertNode($im);
$rsp->insertAttr('status', 'ok');
echo $rsp->toXML(true);
exit;
}
public static function file(string $args): void
{
if (is_null(dcCore::app()->blog)
|| !Targets::instance()->active
|| str_contains('..', $args)
|| !preg_match('#^([^\?]*)#', $args, $m)
) {
self::p404();
}
$f = self::searchTplFiles($m[1]);
if (empty($f)) {
self::p404();
}
$allowed_types = ['png', 'jpg', 'jpeg', 'gif', 'css', 'js', 'swf'];
if (!in_array(Files::getExtension($f), $allowed_types)) {
self::p404();
}
$type = Files::getMimeType($f);
header('Content-Type: ' . $type . '; charset=UTF-8');
header('Content-Length: ' . filesize($f));
if ($type != 'text/css' || dcCore::app()->blog->settings->get('system')->get('url_scan') == 'path_info') {
readfile($f);
} else {
echo preg_replace(
'#url\((?!(http:)|/)#',
'url(' . dcCore::app()->blog->url . dcCore::app()->url->getBase('noodles_file') . '/',
(string) file_get_contents($f)
);
}
exit;
}
# Search noodles files like JS, CSS in default-templates subdirectories
private static function searchTplFiles(string $file): string
{
if (str_contains($file, '..')) {
return '';
}
$paths = dcCore::app()->tpl->getPath();
foreach ($paths as $path) {
if (preg_match('/tpl(\/|)$/', $path)) {
$path = Path::real($path . '/..');
}
if (file_exists($path . '/' . $file)) {
return $path . '/' . $file;
}
}
return '';
}
}