use namespace
This commit is contained in:
parent
9ac6481d85
commit
9a55cd1f2c
6 changed files with 297 additions and 141 deletions
|
@ -10,24 +10,50 @@
|
||||||
* @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;
|
|
||||||
|
namespace Dotclear\Plugin\moreCSS;
|
||||||
|
|
||||||
|
use dcAdmin;
|
||||||
|
use dcAuth;
|
||||||
|
use dcCore;
|
||||||
|
use dcFavorites;
|
||||||
|
use dcNsProcess;
|
||||||
|
use dcPage;
|
||||||
|
|
||||||
|
class Backend extends dcNsProcess
|
||||||
|
{
|
||||||
|
public static function init(): bool
|
||||||
|
{
|
||||||
|
static::$init = defined('DC_CONTEXT_ADMIN');
|
||||||
|
|
||||||
|
return static::$init;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function process(): bool
|
||||||
|
{
|
||||||
|
if (!static::$init) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dcCore::app()->menu[dcAdmin::MENU_BLOG]->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([dcAuth::PERMISSION_CONTENT_ADMIN]), dcCore::app()->blog->id)
|
||||||
|
);
|
||||||
|
|
||||||
|
dcCore::app()->addBehavior('adminDashboardFavoritesV2', function (dcFavorites $favs): void {
|
||||||
|
$favs->register(My::id(), [
|
||||||
|
'title' => My::name(),
|
||||||
|
'url' => dcCore::app()->adminurl->get('admin.plugin.' . My::id()),
|
||||||
|
'small-icon' => [dcPage::getPF(My::id() . '/icon.png')],
|
||||||
|
'large-icon' => [dcPage::getPF(My::id() . '/icon-big.png')],
|
||||||
|
'permissions' => dcCore::app()->auth->makePermissions([dcAuth::PERMISSION_CONTENT_ADMIN]),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dcCore::app()->menu[dcAdmin::MENU_BLOG]->addItem(
|
|
||||||
__('Style sheet'),
|
|
||||||
dcCore::app()->adminurl->get('admin.plugin.' . basename(__DIR__)),
|
|
||||||
[dcPage::getPF(basename(__DIR__) . '/icon.png')],
|
|
||||||
preg_match('/' . preg_quote(dcCore::app()->adminurl->get('admin.plugin.' . basename(__DIR__))) . '(&.*)?$/', $_SERVER['REQUEST_URI']),
|
|
||||||
dcCore::app()->auth->check(dcCore::app()->auth->makePermissions([dcAuth::PERMISSION_CONTENT_ADMIN]), dcCore::app()->blog->id)
|
|
||||||
);
|
|
||||||
|
|
||||||
dcCore::app()->addBehavior('adminDashboardFavoritesV2', function (dcFavorites $favs) {
|
|
||||||
$favs->register(basename(__DIR__), [
|
|
||||||
'title' => __('Style sheet'),
|
|
||||||
'url' => dcCore::app()->adminurl->get('admin.plugin.' . basename(__DIR__)),
|
|
||||||
'small-icon' => [dcPage::getPF(basename(__DIR__) . '/icon.png')],
|
|
||||||
'large-icon' => [dcPage::getPF(basename(__DIR__) . '/icon-big.png')],
|
|
||||||
'permissions' => dcCore::app()->auth->makePermissions([dcAuth::PERMISSION_CONTENT_ADMIN]),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
|
@ -10,21 +10,40 @@
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dcCore::app()->blog->settings->get('themes')->get('morecss_active')) {
|
namespace Dotclear\Plugin\moreCSS;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
dcCore::app()->addBehavior('publicHeadContent', function () {
|
use dcCore;
|
||||||
$css = (string) base64_decode((string) dcCore::app()->blog->settings->get('themes')->get('morecss_min'));
|
use dcNsProcess;
|
||||||
if (!empty($css)) {
|
use dcUtils;
|
||||||
echo dcUtils::cssLoad(
|
|
||||||
dcCore::app()->blog->url . dcCore::app()->url->getURLFor(basename(__DIR__)),
|
class Frontend extends dcNsProcess
|
||||||
'screen',
|
{
|
||||||
md5($css) //no cache on content change
|
public static function init(): bool
|
||||||
);
|
{
|
||||||
|
static::$init = defined('DC_RC_PATH');
|
||||||
|
|
||||||
|
return static::$init;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
public static function process(): bool
|
||||||
|
{
|
||||||
|
if (!static::$init || !dcCore::app()->blog->settings->get('themes')->get('morecss_active')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dcCore::app()->addBehavior('publicHeadContent', function (): void {
|
||||||
|
$css = (string) base64_decode((string) dcCore::app()->blog->settings->get('themes')->get('morecss_min'));
|
||||||
|
if (!empty($css)) {
|
||||||
|
echo dcUtils::cssLoad(
|
||||||
|
dcCore::app()->blog->url . dcCore::app()->url->getURLFor(My::id()),
|
||||||
|
'screen',
|
||||||
|
md5($css) //no cache on content change
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -10,26 +10,41 @@
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
namespace Dotclear\Plugin\moreCSS;
|
||||||
if (!dcCore::app()->newVersion(
|
|
||||||
basename(__DIR__),
|
use dcCore;
|
||||||
dcCore::app()->plugins->moduleInfo(basename(__DIR__), 'version')
|
use dcNsProcess;
|
||||||
)) {
|
use Exception;
|
||||||
return null;
|
|
||||||
|
class Install extends dcNsProcess
|
||||||
|
{
|
||||||
|
public static function init(): bool
|
||||||
|
{
|
||||||
|
static::$init = defined('DC_CONTEXT_ADMIN')
|
||||||
|
&& dcCore::app()->newVersion(My::id(), dcCore::app()->plugins->moduleInfo(My::id(), 'version'));
|
||||||
|
|
||||||
|
return static::$init;
|
||||||
}
|
}
|
||||||
|
|
||||||
$s = dcCore::app()->blog->settings->get('themes');
|
public static function process(): bool
|
||||||
$s->put('morecss_active', true, 'boolean', 'Enable additionnal CSS for the active theme', false, true);
|
{
|
||||||
$s->put('morecss', '', 'string', 'Additionnal CSS for the active theme', false, true);
|
if (!static::$init) {
|
||||||
$s->put('morecss_min', '', 'string', 'Minified addtionnal CSS for the active theme', false, true);
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
try {
|
||||||
} catch (Exception $e) {
|
$s = dcCore::app()->blog->settings->get('themes');
|
||||||
dcCore::app()->error->add($e->getMessage());
|
$s->put('morecss_active', true, 'boolean', 'Enable additionnal CSS for the active theme', false, true);
|
||||||
|
$s->put('morecss', '', 'string', 'Additionnal CSS for the active theme', false, true);
|
||||||
|
$s->put('morecss_min', '', 'string', 'Minified addtionnal CSS for the active theme', false, true);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
dcCore::app()->error->add($e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
|
|
185
src/Manage.php
185
src/Manage.php
|
@ -10,86 +10,121 @@
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
|
|
||||||
dcPage::check(dcCore::app()->auth->makePermissions([
|
namespace Dotclear\Plugin\moreCSS;
|
||||||
dcAuth::PERMISSION_CONTENT_ADMIN,
|
|
||||||
]));
|
|
||||||
|
|
||||||
$s = dcCore::app()->blog->settings->get('themes');
|
use dcAuth;
|
||||||
|
use dcCore;
|
||||||
|
use dcNsProcess;
|
||||||
|
use dcPage;
|
||||||
|
use Exception;
|
||||||
|
use html;
|
||||||
|
use form;
|
||||||
|
|
||||||
if (isset($_POST['morecss'])) {
|
class Manage extends dcNsProcess
|
||||||
try {
|
{
|
||||||
// Save CSS
|
public static function init(): bool
|
||||||
$css = base64_encode($_POST['morecss']);
|
{
|
||||||
$s->put('morecss', $css);
|
static::$init = defined('DC_CONTEXT_ADMIN')
|
||||||
$s->put('morecss_active', !empty($_POST['morecss_active']));
|
&& dcCore::app()->auth->check(dcCore::app()->auth->makePermissions([
|
||||||
|
dcAuth::PERMISSION_CONTENT_ADMIN,
|
||||||
|
]), dcCore::app()->blog->id);
|
||||||
|
|
||||||
// Minify it
|
return static::$init;
|
||||||
$css_min = preg_replace('` {2,}`', ' ', $_POST['morecss']);
|
}
|
||||||
$css_min = preg_replace('/(\/\*[\s\S]*?\*\/)/', '', $css_min);
|
|
||||||
$css_min = preg_replace('/(\t|\r|\n)/', '', $css_min);
|
|
||||||
$css_min = str_replace([' { ', ' {', '{ '], '{', $css_min);
|
|
||||||
$css_min = str_replace([' } ', ' }', '} '], '}', $css_min);
|
|
||||||
$css_min = str_replace([' : ', ' :', ': '], ':', $css_min);
|
|
||||||
$css_min = str_replace([' ; ', ' ;', '; '], ';', $css_min);
|
|
||||||
$css_min = str_replace([' , ', ' ,', ', '], ',', $css_min);
|
|
||||||
$s->put('morecss_min', base64_encode($css_min));
|
|
||||||
|
|
||||||
dcAdminNotices::addSuccessNotice(
|
public static function process(): bool
|
||||||
__('Configuration successfully updated.')
|
{
|
||||||
|
if (!static::$init) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$s = dcCore::app()->blog->settings->get('themes');
|
||||||
|
|
||||||
|
if (isset($_POST['morecss'])) {
|
||||||
|
try {
|
||||||
|
// Save CSS
|
||||||
|
$css = base64_encode($_POST['morecss']);
|
||||||
|
$s->put('morecss', $css);
|
||||||
|
$s->put('morecss_active', !empty($_POST['morecss_active']));
|
||||||
|
|
||||||
|
// Minify it
|
||||||
|
$css_min = preg_replace('` {2,}`', ' ', $_POST['morecss']);
|
||||||
|
$css_min = preg_replace('/(\/\*[\s\S]*?\*\/)/', '', $css_min);
|
||||||
|
$css_min = preg_replace('/(\t|\r|\n)/', '', $css_min);
|
||||||
|
$css_min = str_replace([' { ', ' {', '{ '], '{', $css_min);
|
||||||
|
$css_min = str_replace([' } ', ' }', '} '], '}', $css_min);
|
||||||
|
$css_min = str_replace([' : ', ' :', ': '], ':', $css_min);
|
||||||
|
$css_min = str_replace([' ; ', ' ;', '; '], ';', $css_min);
|
||||||
|
$css_min = str_replace([' , ', ' ,', ', '], ',', $css_min);
|
||||||
|
$s->put('morecss_min', is_string($css_min) ? base64_encode($css_min) : '');
|
||||||
|
|
||||||
|
dcPage::addSuccessNotice(
|
||||||
|
__('Configuration successfully updated.')
|
||||||
|
);
|
||||||
|
dcCore::app()->adminurl->redirect(
|
||||||
|
'admin.plugin.' . My::id()
|
||||||
|
);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
dcCore::app()->error->add($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function render(): void
|
||||||
|
{
|
||||||
|
if (!static::$init) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$s = dcCore::app()->blog->settings->get('themes');
|
||||||
|
|
||||||
|
dcPage::openModule(
|
||||||
|
My::name(),
|
||||||
|
(
|
||||||
|
dcCore::app()->auth->user_prefs->get('interface')->get('colorsyntax') ?
|
||||||
|
dcPage::jsJson('dotclear_colorsyntax', ['colorsyntax' => dcCore::app()->auth->user_prefs->get('interface')->get('colorsyntax')]) .
|
||||||
|
dcPage::jsLoadCodeMirror(dcCore::app()->auth->user_prefs->get('interface')->get('colorsyntax_theme'))
|
||||||
|
: ''
|
||||||
|
)
|
||||||
);
|
);
|
||||||
dcCore::app()->adminurl->redirect(
|
|
||||||
'admin.plugin.' . basename(__DIR__)
|
echo
|
||||||
);
|
dcPage::breadcrumb([
|
||||||
} catch (Exception $e) {
|
html::escapeHTML(dcCore::app()->blog->name) => '',
|
||||||
dcCore::app()->error->add($e->getMessage());
|
My::name() => '',
|
||||||
|
]) .
|
||||||
|
dcPage::notices() . '
|
||||||
|
|
||||||
|
<form action="' . dcCore::app()->admin->getPageURL() . '" id="file-form" method="post">
|
||||||
|
|
||||||
|
<div><h3><label for="morecss">' . __('Style sheet:') . '</strong></label></h3>
|
||||||
|
<p>' . form::textarea('morecss', 72, 25, [
|
||||||
|
'default' => html::escapeHTML((string) base64_decode((string) $s->get('morecss'))),
|
||||||
|
'class' => 'maximal',
|
||||||
|
]) . '</p>
|
||||||
|
|
||||||
|
<p><label class="classic" for="morecss_active">' .
|
||||||
|
form::checkbox('morecss_active', 1, $s->get('morecss_active')) . ' ' .
|
||||||
|
__('Enable additionnal CSS for the active theme') .
|
||||||
|
'</label></p>
|
||||||
|
|
||||||
|
<p>' .
|
||||||
|
form::hidden('p', 'moreCSS') .
|
||||||
|
dcCore::app()->formNonce() . '
|
||||||
|
<input type="submit" name="write" value="' . __('Save') . ' (s)" accesskey="s" /></p>
|
||||||
|
</form>';
|
||||||
|
|
||||||
|
if (dcCore::app()->auth->user_prefs->get('interface')->get('colorsyntax')) {
|
||||||
|
echo
|
||||||
|
dcPage::jsJson('theme_editor_mode', ['mode' => 'css']) .
|
||||||
|
dcPage::jsModuleLoad('themeEditor/js/mode.js') .
|
||||||
|
dcPage::jsRunCodeMirror('editor', 'morecss', 'dotclear', dcCore::app()->auth->user_prefs->get('interface')->get('colorsyntax_theme'));
|
||||||
|
}
|
||||||
|
|
||||||
|
dcPage::closeModule();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
echo '
|
|
||||||
<html><head><title>' . __('Style sheet') . '</title>';
|
|
||||||
if (dcCore::app()->auth->user_prefs->interface->colorsyntax) {
|
|
||||||
echo
|
|
||||||
dcPage::jsJson('dotclear_colorsyntax', ['colorsyntax' => dcCore::app()->auth->user_prefs->interface->colorsyntax]) .
|
|
||||||
dcPage::jsLoadCodeMirror(dcCore::app()->auth->user_prefs->interface->colorsyntax_theme);
|
|
||||||
}
|
|
||||||
echo '
|
|
||||||
</head><body>' .
|
|
||||||
dcPage::breadcrumb([
|
|
||||||
html::escapeHTML(dcCore::app()->blog->name) => '',
|
|
||||||
__('Style sheet') => '',
|
|
||||||
]) .
|
|
||||||
dcPage::notices() . '
|
|
||||||
|
|
||||||
<form action="' . dcCore::app()->admin->getPageURL() . '" id="file-form" method="post">
|
|
||||||
|
|
||||||
<div><h3><label for="morecss">' . __('Style sheet:') . '</strong></label></h3>
|
|
||||||
<p>' . form::textarea('morecss', 72, 25, [
|
|
||||||
'default' => html::escapeHTML((string) base64_decode((string) $s->get('morecss'))),
|
|
||||||
'class' => 'maximal',
|
|
||||||
]) . '</p>
|
|
||||||
|
|
||||||
<p><label class="classic" for="morecss_active">' .
|
|
||||||
form::checkbox('morecss_active', 1, $s->get('morecss_active')) . ' ' .
|
|
||||||
__('Enable additionnal CSS for the active theme') .
|
|
||||||
'</label></p>
|
|
||||||
|
|
||||||
<p>' .
|
|
||||||
form::hidden('p', 'moreCSS') .
|
|
||||||
dcCore::app()->formNonce() . '
|
|
||||||
<input type="submit" name="write" value="' . __('Save') . ' (s)" accesskey="s" /></p>
|
|
||||||
</form>';
|
|
||||||
|
|
||||||
if (dcCore::app()->auth->user_prefs->interface->colorsyntax) {
|
|
||||||
echo
|
|
||||||
dcPage::jsJson('theme_editor_mode', ['mode' => 'css']) .
|
|
||||||
dcPage::jsModuleLoad('themeEditor/js/mode.js') .
|
|
||||||
dcPage::jsRunCodeMirror('editor', 'morecss', 'dotclear', dcCore::app()->auth->user_prefs->interface->colorsyntax_theme);
|
|
||||||
}
|
|
||||||
|
|
||||||
echo '
|
|
||||||
</body>
|
|
||||||
</html>';
|
|
||||||
|
|
39
src/My.php
Normal file
39
src/My.php
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @brief moreCSS, 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\moreCSS;
|
||||||
|
|
||||||
|
use dcCore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin definitions
|
||||||
|
*/
|
||||||
|
class My
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This module id
|
||||||
|
*/
|
||||||
|
public static function id(): string
|
||||||
|
{
|
||||||
|
return basename(dirname(__DIR__));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This module name
|
||||||
|
*/
|
||||||
|
public static function name(): string
|
||||||
|
{
|
||||||
|
return __((string) dcCore::app()->plugins->moduleInfo(self::id(), 'name'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,20 +10,42 @@
|
||||||
* @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()->url->register(
|
namespace Dotclear\Plugin\moreCSS;
|
||||||
basename(__DIR__),
|
|
||||||
'morecss.css',
|
|
||||||
'^morecss\.css(.*?)$',
|
|
||||||
function ($args) {
|
|
||||||
header('Content-Type: text/css; charset=UTF-8');
|
|
||||||
|
|
||||||
echo "/* CSS for plugin moreCss */ \n";
|
use dcCore;
|
||||||
echo (string) base64_decode((string) dcCore::app()->blog->settings->get('themes')->get('morecss_min'));
|
use dcNsProcess;
|
||||||
|
|
||||||
exit;
|
class Prepend extends dcNsProcess
|
||||||
|
{
|
||||||
|
public static function init(): bool
|
||||||
|
{
|
||||||
|
static::$init = defined('DC_RC_PATH');
|
||||||
|
|
||||||
|
return static::$init;
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
public static function process(): bool
|
||||||
|
{
|
||||||
|
if (!static::$init) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dcCore::app()->url->register(
|
||||||
|
My::id(),
|
||||||
|
'morecss.css',
|
||||||
|
'^morecss\.css(.*?)$',
|
||||||
|
function (string $args): void {
|
||||||
|
header('Content-Type: text/css; charset=UTF-8');
|
||||||
|
|
||||||
|
echo "/* CSS for plugin moreCss */ \n";
|
||||||
|
echo (string) base64_decode((string) dcCore::app()->blog->settings->get('themes')->get('morecss_min'));
|
||||||
|
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue