Merge pull request #5828 from nupplaphil/mode_class
Creating Friendica\App\Mode for encapsulating the App Mode
This commit is contained in:
commit
3564daf24d
9 changed files with 127 additions and 89 deletions
|
@ -54,7 +54,7 @@ require_once "include/dba.php";
|
||||||
|
|
||||||
$a = new App(dirname(__DIR__));
|
$a = new App(dirname(__DIR__));
|
||||||
|
|
||||||
if ($a->mode === App::MODE_NORMAL) {
|
if (App\Mode::isNormal()) {
|
||||||
$oAuth = new ExAuth();
|
$oAuth = new ExAuth();
|
||||||
$oAuth->readStdin();
|
$oAuth->readStdin();
|
||||||
}
|
}
|
12
index.php
12
index.php
|
@ -36,7 +36,7 @@ $a->backend = false;
|
||||||
require_once "include/dba.php";
|
require_once "include/dba.php";
|
||||||
|
|
||||||
// Missing DB connection: ERROR
|
// Missing DB connection: ERROR
|
||||||
if ($a->mode & App::MODE_LOCALCONFIGPRESENT && !($a->mode & App::MODE_DBAVAILABLE)) {
|
if (App\Mode::has(App\Mode::LOCALCONFIGPRESENT) && !App\Mode::has(App\Mode::DBAVAILABLE)) {
|
||||||
System::httpExit(500, ['title' => 'Error 500 - Internal Server Error', 'description' => 'Apologies but the website is unavailable at the moment.']);
|
System::httpExit(500, ['title' => 'Error 500 - Internal Server Error', 'description' => 'Apologies but the website is unavailable at the moment.']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ if ($a->isMaxProcessesReached() || $a->isMaxLoadReached()) {
|
||||||
System::httpExit(503, ['title' => 'Error 503 - Service Temporarily Unavailable', 'description' => 'System is currently overloaded. Please try again later.']);
|
System::httpExit(503, ['title' => 'Error 503 - Service Temporarily Unavailable', 'description' => 'System is currently overloaded. Please try again later.']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$a->isInstallMode()) {
|
if (!App\Mode::isInstall()) {
|
||||||
if (Config::get('system', 'force_ssl') && ($a->get_scheme() == "http")
|
if (Config::get('system', 'force_ssl') && ($a->get_scheme() == "http")
|
||||||
&& (intval(Config::get('system', 'ssl_policy')) == SSL_POLICY_FULL)
|
&& (intval(Config::get('system', 'ssl_policy')) == SSL_POLICY_FULL)
|
||||||
&& (substr(System::baseUrl(), 0, 8) == "https://")
|
&& (substr(System::baseUrl(), 0, 8) == "https://")
|
||||||
|
@ -107,7 +107,7 @@ if (!empty($_SESSION['language']) && $_SESSION['language'] !== $lang) {
|
||||||
L10n::loadTranslationTable($lang);
|
L10n::loadTranslationTable($lang);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($_GET['zrl']) && $a->mode == App::MODE_NORMAL) {
|
if (!empty($_GET['zrl']) && App\Mode::isNormal()) {
|
||||||
$a->query_string = Profile::stripZrls($a->query_string);
|
$a->query_string = Profile::stripZrls($a->query_string);
|
||||||
if (!local_user()) {
|
if (!local_user()) {
|
||||||
// Only continue when the given profile link seems valid
|
// Only continue when the given profile link seems valid
|
||||||
|
@ -130,7 +130,7 @@ if (!empty($_GET['zrl']) && $a->mode == App::MODE_NORMAL) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($_GET['owt']) && $a->mode == App::MODE_NORMAL) {
|
if (!empty($_GET['owt']) && App\Mode::isNormal()) {
|
||||||
$token = $_GET['owt'];
|
$token = $_GET['owt'];
|
||||||
$a->query_string = Profile::stripQueryParam($a->query_string, 'owt');
|
$a->query_string = Profile::stripQueryParam($a->query_string, 'owt');
|
||||||
Profile::openWebAuthInit($token);
|
Profile::openWebAuthInit($token);
|
||||||
|
@ -167,7 +167,7 @@ $_SESSION['last_updated'] = defaults($_SESSION, 'last_updated', []);
|
||||||
// but we need "view" module for stylesheet
|
// but we need "view" module for stylesheet
|
||||||
if ($a->isInstallMode() && $a->module!="view") {
|
if ($a->isInstallMode() && $a->module!="view") {
|
||||||
$a->module = 'install';
|
$a->module = 'install';
|
||||||
} elseif (!($a->mode & App::MODE_MAINTENANCEDISABLED) && $a->module != "view") {
|
} elseif (!App\Mode::has(App\Mode::MAINTENANCEDISABLED) && $a->module != "view") {
|
||||||
$a->module = 'maintenance';
|
$a->module = 'maintenance';
|
||||||
} else {
|
} else {
|
||||||
check_url($a);
|
check_url($a);
|
||||||
|
@ -320,7 +320,7 @@ if (file_exists($theme_info_file)) {
|
||||||
|
|
||||||
/* initialise content region */
|
/* initialise content region */
|
||||||
|
|
||||||
if ($a->mode == App::MODE_NORMAL) {
|
if (App\Mode::isNormal()) {
|
||||||
Addon::callHooks('page_content_top', $a->page['content']);
|
Addon::callHooks('page_content_top', $a->page['content']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
71
src/App.php
71
src/App.php
|
@ -31,21 +31,6 @@ require_once 'include/text.php';
|
||||||
*/
|
*/
|
||||||
class App
|
class App
|
||||||
{
|
{
|
||||||
const MODE_LOCALCONFIGPRESENT = 1;
|
|
||||||
const MODE_DBAVAILABLE = 2;
|
|
||||||
const MODE_DBCONFIGAVAILABLE = 4;
|
|
||||||
const MODE_MAINTENANCEDISABLED = 8;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated since version 2008.08 Use App->isInstallMode() instead to check for install mode.
|
|
||||||
*/
|
|
||||||
const MODE_INSTALL = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated since version 2008.08 Use the precise mode constant to check for a specific capability instead.
|
|
||||||
*/
|
|
||||||
const MODE_NORMAL = App::MODE_LOCALCONFIGPRESENT | App::MODE_DBAVAILABLE | App::MODE_DBCONFIGAVAILABLE | App::MODE_MAINTENANCEDISABLED;
|
|
||||||
|
|
||||||
public $module_loaded = false;
|
public $module_loaded = false;
|
||||||
public $module_class = null;
|
public $module_class = null;
|
||||||
public $query_string = '';
|
public $query_string = '';
|
||||||
|
@ -67,7 +52,6 @@ class App
|
||||||
public $argv;
|
public $argv;
|
||||||
public $argc;
|
public $argc;
|
||||||
public $module;
|
public $module;
|
||||||
public $mode = App::MODE_INSTALL;
|
|
||||||
public $strings;
|
public $strings;
|
||||||
public $basepath;
|
public $basepath;
|
||||||
public $urlpath;
|
public $urlpath;
|
||||||
|
@ -326,13 +310,13 @@ class App
|
||||||
|
|
||||||
$this->loadDatabase();
|
$this->loadDatabase();
|
||||||
|
|
||||||
$this->determineMode();
|
App\Mode::determine($this->basepath);
|
||||||
|
|
||||||
$this->determineUrlPath();
|
$this->determineUrlPath();
|
||||||
|
|
||||||
Config::load();
|
Config::load();
|
||||||
|
|
||||||
if ($this->mode & self::MODE_DBAVAILABLE) {
|
if (App\Mode::has(App\Mode::DBAVAILABLE)) {
|
||||||
Core\Addon::loadHooks();
|
Core\Addon::loadHooks();
|
||||||
|
|
||||||
$this->loadAddonConfig();
|
$this->loadAddonConfig();
|
||||||
|
@ -518,45 +502,6 @@ class App
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the App mode
|
|
||||||
*
|
|
||||||
* - App::MODE_INSTALL : Either the database connection can't be established or the config table doesn't exist
|
|
||||||
* - App::MODE_MAINTENANCE: The maintenance mode has been set
|
|
||||||
* - App::MODE_NORMAL : Normal run with all features enabled
|
|
||||||
*
|
|
||||||
* @return type
|
|
||||||
*/
|
|
||||||
private function determineMode()
|
|
||||||
{
|
|
||||||
$this->mode = 0;
|
|
||||||
|
|
||||||
if (!file_exists($this->basepath . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'local.ini.php')
|
|
||||||
&& !file_exists($this->basepath . DIRECTORY_SEPARATOR . '.htconfig.php')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->mode |= App::MODE_LOCALCONFIGPRESENT;
|
|
||||||
|
|
||||||
if (!DBA::connected()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->mode |= App::MODE_DBAVAILABLE;
|
|
||||||
|
|
||||||
if (DBA::fetchFirst("SHOW TABLES LIKE 'config'") === false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->mode |= App::MODE_DBCONFIGAVAILABLE;
|
|
||||||
|
|
||||||
if (Config::get('system', 'maintenance')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->mode |= App::MODE_MAINTENANCEDISABLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function loadDatabase()
|
public function loadDatabase()
|
||||||
{
|
{
|
||||||
if (DBA::connected()) {
|
if (DBA::connected()) {
|
||||||
|
@ -596,16 +541,6 @@ class App
|
||||||
$this->save_timestamp($stamp1, 'network');
|
$this->save_timestamp($stamp1, 'network');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Install mode is when the local config file is missing or the DB schema hasn't been installed yet.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function isInstallMode()
|
|
||||||
{
|
|
||||||
return !($this->mode & App::MODE_LOCALCONFIGPRESENT) || !($this->mode & App::MODE_DBCONFIGAVAILABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns the base filesystem path of the App
|
* @brief Returns the base filesystem path of the App
|
||||||
*
|
*
|
||||||
|
@ -1467,7 +1402,7 @@ class App
|
||||||
*/
|
*/
|
||||||
public function getCurrentTheme()
|
public function getCurrentTheme()
|
||||||
{
|
{
|
||||||
if ($this->isInstallMode()) {
|
if (App\Mode::isInstall()) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
103
src/App/Mode.php
Normal file
103
src/App/Mode.php
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Friendica\App;
|
||||||
|
|
||||||
|
use Friendica\Core\Config;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mode of the current Friendica Node
|
||||||
|
*
|
||||||
|
* @package Friendica\App
|
||||||
|
*/
|
||||||
|
class Mode
|
||||||
|
{
|
||||||
|
const LOCALCONFIGPRESENT = 1;
|
||||||
|
const DBAVAILABLE = 2;
|
||||||
|
const DBCONFIGAVAILABLE = 4;
|
||||||
|
const MAINTENANCEDISABLED = 8;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* @var int the mode of this Application
|
||||||
|
*
|
||||||
|
* Default is 0 (= not set)
|
||||||
|
*/
|
||||||
|
private static $mode = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the App mode
|
||||||
|
*
|
||||||
|
* - App::MODE_INSTALL : Either the database connection can't be established or the config table doesn't exist
|
||||||
|
* - App::MODE_MAINTENANCE: The maintenance mode has been set
|
||||||
|
* - App::MODE_NORMAL : Normal run with all features enabled
|
||||||
|
*
|
||||||
|
* @param string $basepath the Basepath of the Application
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static function determine($basepath)
|
||||||
|
{
|
||||||
|
self::$mode = 0;
|
||||||
|
|
||||||
|
if (!file_exists($basepath . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'local.ini.php')
|
||||||
|
&& !file_exists($basepath . DIRECTORY_SEPARATOR . '.htconfig.php')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$mode |= Mode::LOCALCONFIGPRESENT;
|
||||||
|
|
||||||
|
if (!DBA::connected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$mode |= Mode::DBAVAILABLE;
|
||||||
|
|
||||||
|
if (DBA::fetchFirst("SHOW TABLES LIKE 'config'") === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$mode |= Mode::DBCONFIGAVAILABLE;
|
||||||
|
|
||||||
|
if (Config::get('system', 'maintenance')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$mode |= Mode::MAINTENANCEDISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks, if the Friendica Node has the given mode
|
||||||
|
*
|
||||||
|
* @param int $mode A mode to test
|
||||||
|
*
|
||||||
|
* @return bool returns true, if the mode is set
|
||||||
|
*/
|
||||||
|
public static function has($mode)
|
||||||
|
{
|
||||||
|
return self::$mode & $mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install mode is when the local config file is missing or the DB schema hasn't been installed yet.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function isInstall()
|
||||||
|
{
|
||||||
|
return !self::has(Mode::LOCALCONFIGPRESENT) ||
|
||||||
|
!self::has(MODE::DBCONFIGAVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normal mode is when the local config file is set, the DB schema is installed and the maintenance mode is off.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function isNormal()
|
||||||
|
{
|
||||||
|
return self::has(Mode::LOCALCONFIGPRESENT) &&
|
||||||
|
self::has(Mode::DBAVAILABLE) &&
|
||||||
|
self::has(Mode::DBCONFIGAVAILABLE) &&
|
||||||
|
self::has(Mode::MAINTENANCEDISABLED);
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,7 +31,7 @@ class Config extends BaseObject
|
||||||
public static function init()
|
public static function init()
|
||||||
{
|
{
|
||||||
// Database isn't ready or populated yet
|
// Database isn't ready or populated yet
|
||||||
if (!(self::getApp()->mode & App::MODE_DBCONFIGAVAILABLE)) {
|
if (!App\Mode::has(App\Mode::DBCONFIGAVAILABLE)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ class Config extends BaseObject
|
||||||
public static function load($family = "config")
|
public static function load($family = "config")
|
||||||
{
|
{
|
||||||
// Database isn't ready or populated yet
|
// Database isn't ready or populated yet
|
||||||
if (!(self::getApp()->mode & App::MODE_DBCONFIGAVAILABLE)) {
|
if (!App\Mode::has(App\Mode::DBCONFIGAVAILABLE)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ class Config extends BaseObject
|
||||||
public static function get($family, $key, $default_value = null, $refresh = false)
|
public static function get($family, $key, $default_value = null, $refresh = false)
|
||||||
{
|
{
|
||||||
// Database isn't ready or populated yet, fallback to file config
|
// Database isn't ready or populated yet, fallback to file config
|
||||||
if (!(self::getApp()->mode & App::MODE_DBCONFIGAVAILABLE)) {
|
if (!App\Mode::has(App\Mode::DBCONFIGAVAILABLE)) {
|
||||||
return self::getApp()->getConfigValue($family, $key, $default_value);
|
return self::getApp()->getConfigValue($family, $key, $default_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ class Config extends BaseObject
|
||||||
public static function set($family, $key, $value)
|
public static function set($family, $key, $value)
|
||||||
{
|
{
|
||||||
// Database isn't ready or populated yet
|
// Database isn't ready or populated yet
|
||||||
if (!(self::getApp()->mode & App::MODE_DBCONFIGAVAILABLE)) {
|
if (!App\Mode::has(App\Mode::DBCONFIGAVAILABLE)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ class Config extends BaseObject
|
||||||
public static function delete($family, $key)
|
public static function delete($family, $key)
|
||||||
{
|
{
|
||||||
// Database isn't ready or populated yet
|
// Database isn't ready or populated yet
|
||||||
if (!(self::getApp()->mode & App::MODE_DBCONFIGAVAILABLE)) {
|
if (!App\Mode::has(App\Mode::DBCONFIGAVAILABLE)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ HELP;
|
||||||
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
|
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($a->mode === App::MODE_INSTALL) {
|
if (App\Mode::isInstall()) {
|
||||||
throw new RuntimeException('Friendica isn\'t properly installed yet.');
|
throw new RuntimeException('Friendica isn\'t properly installed yet.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ HELP;
|
||||||
$this->out('Options: ' . var_export($this->options, true));
|
$this->out('Options: ' . var_export($this->options, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!($a->mode & App::MODE_DBCONFIGAVAILABLE)) {
|
if (!App\Mode::has(App\Mode::DBCONFIGAVAILABLE)) {
|
||||||
$this->out('Database isn\'t ready or populated yet, database cache won\'t be available');
|
$this->out('Database isn\'t ready or populated yet, database cache won\'t be available');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ HELP;
|
||||||
throw new CommandArgsException('Too many arguments');
|
throw new CommandArgsException('Too many arguments');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!($a->mode & App::MODE_DBCONFIGAVAILABLE)) {
|
if (!App\Mode::has(App\Mode::DBCONFIGAVAILABLE)) {
|
||||||
$this->out('Database isn\'t ready or populated yet, showing file config only');
|
$this->out('Database isn\'t ready or populated yet, showing file config only');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ HELP;
|
||||||
if (count($this->args) == 0) {
|
if (count($this->args) == 0) {
|
||||||
Core\Config::load();
|
Core\Config::load();
|
||||||
|
|
||||||
if (Core\Config::get('system', 'config_adapter') == 'jit' && $a->mode & App::MODE_DBCONFIGAVAILABLE) {
|
if (Core\Config::get('system', 'config_adapter') == 'jit' && App\Mode::has(App\Mode::DBCONFIGAVAILABLE)) {
|
||||||
$this->out('Warning: The JIT (Just In Time) Config adapter doesn\'t support loading the entire configuration, showing file config only');
|
$this->out('Warning: The JIT (Just In Time) Config adapter doesn\'t support loading the entire configuration, showing file config only');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ class PConfig extends BaseObject
|
||||||
public static function init($uid)
|
public static function init($uid)
|
||||||
{
|
{
|
||||||
// Database isn't ready or populated yet
|
// Database isn't ready or populated yet
|
||||||
if (!(self::getApp()->mode & App::MODE_DBCONFIGAVAILABLE)) {
|
if (!App\Mode::has(App\Mode::DBCONFIGAVAILABLE)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ class PConfig extends BaseObject
|
||||||
public static function load($uid, $family)
|
public static function load($uid, $family)
|
||||||
{
|
{
|
||||||
// Database isn't ready or populated yet
|
// Database isn't ready or populated yet
|
||||||
if (!(self::getApp()->mode & App::MODE_DBCONFIGAVAILABLE)) {
|
if (!App\Mode::has(App\Mode::DBCONFIGAVAILABLE)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ class PConfig extends BaseObject
|
||||||
public static function get($uid, $family, $key, $default_value = null, $refresh = false)
|
public static function get($uid, $family, $key, $default_value = null, $refresh = false)
|
||||||
{
|
{
|
||||||
// Database isn't ready or populated yet
|
// Database isn't ready or populated yet
|
||||||
if (!(self::getApp()->mode & App::MODE_DBCONFIGAVAILABLE)) {
|
if (!App\Mode::has(App\Mode::DBCONFIGAVAILABLE)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ class PConfig extends BaseObject
|
||||||
public static function set($uid, $family, $key, $value)
|
public static function set($uid, $family, $key, $value)
|
||||||
{
|
{
|
||||||
// Database isn't ready or populated yet
|
// Database isn't ready or populated yet
|
||||||
if (!(self::getApp()->mode & App::MODE_DBCONFIGAVAILABLE)) {
|
if (!App\Mode::has(App\Mode::DBCONFIGAVAILABLE)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ class PConfig extends BaseObject
|
||||||
public static function delete($uid, $family, $key)
|
public static function delete($uid, $family, $key)
|
||||||
{
|
{
|
||||||
// Database isn't ready or populated yet
|
// Database isn't ready or populated yet
|
||||||
if (!(self::getApp()->mode & App::MODE_DBCONFIGAVAILABLE)) {
|
if (!App\Mode::has(App\Mode::DBCONFIGAVAILABLE)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue