getLang $this->setupLocale(); $this->currentDate = date("c"); $this->infoPlugin = $this->getInfo(); } /** * @return admin_plugin_404manager */ public static function get() { if (self::$manager404 == null) { self::$manager404 = new admin_plugin_404manager(); } return self::$manager404; } /** * Access for managers allowed */ function forAdminOnly() { return false; } /** * return sort order for position in admin menu */ function getMenuSort() { return 140; } /** * return prompt for admin menu * @param string $language * @return string */ function getMenuText($language) { $menuText = $this->lang['AdminPageName']; if ($menuText == '') { $menuText = $this->infoPlugin['name']; } return $menuText; } /** * handle user request */ function handle() { if ($_POST['Add']) { $this->redirectionSource = $_POST[self::FORM_NAME_SOURCE_PAGE]; $this->redirectionTarget = $_POST[self::FORM_NAME_TARGET_PAGE]; if ($this->redirectionSource == $this->redirectionTarget) { msg($this->lang['SameSourceAndTargetAndPage'] . ': ' . $this->redirectionSource . '', -1); return; } // This a direct redirection // If the source page exist, do nothing if (page_exists($this->redirectionSource)) { $title = false; global $conf; if ($conf['useheading']) { $title = p_get_first_heading($this->redirectionSource); } if (!$title) $title = $this->redirectionSource; msg($this->lang['SourcePageExist'] . ' : ' . hsc($title) . '', -1); return; } else { // Is this a direct redirection to a valid target page if (!page_exists($this->redirectionTarget)) { if ($this->isValidURL($this->redirectionTarget)) { $this->targetResourceType = 'Url'; } else { msg($this->lang['NotInternalOrUrlPage'] . ': ' . $this->redirectionTarget . '', -1); return; } } else { $this->targetResourceType = 'Internal Page'; } $this->addRedirection($this->redirectionSource, $this->redirectionTarget); msg($this->lang['Saved'], 1); } } if ($_POST['Delete']) { $redirectionId = $_POST['SourcePage']; $this->deleteRedirection($redirectionId); msg($this->lang['Deleted'], 1); } if ($_POST['Validate']) { $redirectionId = $_POST['SourcePage']; $this->validateRedirection($redirectionId); msg($this->lang['Validated'], 1); } } /** * output appropriate html */ function html() { global $conf; echo $this->locale_xhtml('intro'); // Add a redirection ptln('

' . $this->lang['AddModifyRedirection'] . '

'); ptln('
'); ptln('
'); ptln(''); ptln(''); ptln(' '); ptln(''); ptln(''); ptln(' '); ptln(' '); ptln(' '); ptln(' '); ptln(' '); ptln(''); ptln('
' . $this->lang['Field'] . '' . $this->lang['Value'] . ' ' . $this->lang['Information'] . '
' . $this->lang['source_page_info'] . '
' . $this->lang['target_page_info'] . '
'); ptln(' '); ptln(' '); ptln(' '); ptln('
'); ptln('
'); // Add the file add from the lang directory echo $this->locale_xhtml('add'); ptln('
'); // List of redirection ptln('

' . $this->lang['ListOfRedirection'] . '

'); ptln('
'); ptln('
'); ptln(''); ptln(' '); ptln(' '); ptln(' '); ptln(' '); ptln(' '); ptln(' '); ptln(' '); ptln(' '); ptln(' '); foreach ($this->getRedirections() as $key => $row) { if ($this->dataStoreType == self::DATA_STORE_TYPE_SQLITE) { $sourcePageId = $row['SOURCE']; $targetPageId = $row['TARGET']; $creationDate = $row['CREATION_TIMESTAMP']; } else { $sourcePageId = $key; $targetPageId = $row['TargetPage']; $creationDate = $row['CreationDate']; } $title = false; if ($conf['useheading']) { $title = p_get_first_heading($targetPageId); } if (!$title) $title = $targetPageId; ptln(' '); ptln(' '); print(' '); print ' '); ptln(' '); ptln(' '); } ptln(' '); ptln('
 ' . $this->lang['SourcePage'] . '' . $this->lang['TargetPage'] . '' . $this->lang['CreationDate'] . '
'); ptln('
'); ptln(' '); ptln(' '); ptln(' '); ptln('
'); ptln('
'); tpl_link(wl($sourcePageId), $this->truncateString($sourcePageId, 30), 'title="' . $sourcePageId . '" class="wikilink2" rel="nofollow"'); ptln(' '; tpl_link(wl($targetPageId), $this->truncateString($targetPageId, 30), 'title="' . hsc($title) . ' (' . $targetPageId . ')"'); ptln(' ' . $creationDate . '
'); ptln('
'); //End Table responsive ptln('
'); // End level 2 } /** * Generate a text with a max length of $length * and add ... if above */ function truncateString($myString, $length) { if (strlen($myString) > $length) { $myString = substr($myString, 0, $length) . ' ...'; } return $myString; } /** * Delete Redirection * @param string $sourcePageId */ function deleteRedirection($sourcePageId) { if ($this->dataStoreType == null) { $this->initDataStore(); } if ($this->dataStoreType == self::DATA_STORE_TYPE_CONF_FILE) { unset($this->pageRedirections[strtolower($sourcePageId)]); $this->savePageRedirections(); } else { $res = $this->sqlite->query('delete from redirections where source = ?', $sourcePageId); if (!$res) { $this->throwRuntimeException("Something went wrong when deleting the redirections"); } } } /** * Is Redirection of a page Id Present * @param string $sourcePageId * @return int */ function isRedirectionPresent($sourcePageId) { $sourcePageId = strtolower($sourcePageId); if ($this->dataStoreType == null) { $this->initDataStore(); } if ($this->dataStoreType == self::DATA_STORE_TYPE_CONF_FILE) { if (isset($this->pageRedirections[$sourcePageId])) { return 1; } else { return 0; } } else { $res = $this->sqlite->query("SELECT * FROM redirections"); $count = $this->sqlite->res2count($res); return $count; } } /** * @param $sourcePageId * @param $targetPageId */ function addRedirection($sourcePageId, $targetPageId) { $this->addRedirectionWithDate($sourcePageId, $targetPageId, $this->currentDate); } /** * Add Redirection * This function was needed to migrate the date of the file conf store * You would use normally the function addRedirection * @param string $sourcePageId * @param string $targetPageId * @param $creationDate */ function addRedirectionWithDate($sourcePageId, $targetPageId, $creationDate) { // Lower page name is the dokuwiki Id $sourcePageId = strtolower($sourcePageId); if ($this->dataStoreType == null) { $this->initDataStore(); } if ($this->dataStoreType == self::DATA_STORE_TYPE_CONF_FILE) { if (isset($this->pageRedirections[$sourcePageId])) { $this->throwRuntimeException('Redirection for page (' . $sourcePageId . 'already exist'); } $this->pageRedirections[$sourcePageId]['TargetPage'] = $targetPageId; $this->pageRedirections[$sourcePageId]['CreationDate'] = $creationDate; // If the call come from the admin page and not from the process function if (substr_count($_SERVER['HTTP_REFERER'], 'admin.php')) { $this->pageRedirections[$sourcePageId]['IsValidate'] = 'Y'; $this->pageRedirections[$sourcePageId]['CountOfRedirection'] = 0; $this->pageRedirections[$sourcePageId]['LastRedirectionDate'] = $this->lang['Never']; $this->pageRedirections[$sourcePageId]['LastReferrer'] = 'Never'; } else { $this->pageRedirections[$sourcePageId]['IsValidate'] = 'N'; $this->pageRedirections[$sourcePageId]['CountOfRedirection'] = 1; $this->pageRedirections[$sourcePageId]['LastRedirectionDate'] = $creationDate; if ($_SERVER['HTTP_REFERER'] <> '') { $this->pageRedirections[$sourcePageId]['LastReferrer'] = $_SERVER['HTTP_REFERER']; } else { $this->pageRedirections[$sourcePageId]['LastReferrer'] = $this->lang['Direct Access']; } } if (!$this->isValidURL($targetPageId)) { $this->pageRedirections[$sourcePageId]['TargetPageType'] = 'Internal Page'; } else { $this->pageRedirections[$sourcePageId]['TargetPageType'] = 'Url'; } $this->savePageRedirections(); } else { // Note the order is important // because it's used in the bin of the update statement $entry = array( 'target' => $targetPageId, 'creation_timestamp' => $creationDate, 'source' => $sourcePageId ); $statement = 'select * from redirections where source = ?'; $res = $this->sqlite->query($statement, $sourcePageId); $count = $this->sqlite->res2count($res); if ($count <> 1) { $res = $this->sqlite->storeEntry('redirections', $entry); if (!$res) { $this->throwRuntimeException("There was a problem during insertion"); } } else { // Primary key constraint, the storeEntry function does not use an UPSERT $statement = 'update redirections set target = ?, creation_timestamp = ? where source = ?'; $res = $this->sqlite->query($statement, $entry); if (!$res) { $this->throwRuntimeException("There was a problem during the update"); } } } } /** * Validate a Redirection * @param string $sourcePageId */ function validateRedirection($sourcePageId) { $sourcePageId = strtolower($sourcePageId); if ($this->dataStoreType == null) { $this->initDataStore(); } if ($this->dataStoreType == self::DATA_STORE_TYPE_CONF_FILE) { $this->pageRedirections[$sourcePageId]['IsValidate'] = 'Y'; $this->savePageRedirections(); } else { $this->throwRuntimeException('Not implemented for a SQLite data store'); } } /** * Get IsValidate Redirection * @param string $sourcePageId * @return string */ function getIsValidate($sourcePageId) { $sourcePageId = strtolower($sourcePageId); if ($this->dataStoreType == null) { $this->initDataStore(); } if ($this->dataStoreType == self::DATA_STORE_TYPE_CONF_FILE) { if ($this->pageRedirections[$sourcePageId]['IsValidate'] == null) { return 'N'; } else { return $this->pageRedirections[$sourcePageId]['IsValidate']; } } else { $this->throwRuntimeException("Not Yet implemented"); } } /** * Get TargetPageType * @param string $sourcePageId * @return * @throws Exception */ function getTargetPageType($sourcePageId) { if ($this->dataStoreType == null) { $this->initDataStore(); } if ($this->dataStoreType == self::DATA_STORE_TYPE_CONF_FILE) { $sourcePageId = strtolower($sourcePageId); return $this->pageRedirections[$sourcePageId]['TargetPageType']; } else { throw new Exception('Not Yet implemented'); } } /** * Get TargetResource (It can be an external URL as an intern page id * @param string $sourcePageId * @return * @throws Exception */ function getRedirectionTarget($sourcePageId) { if ($this->dataStoreType == null) { $this->initDataStore(); } if ($this->dataStoreType == self::DATA_STORE_TYPE_CONF_FILE) { $sourcePageId = strtolower($sourcePageId); return $this->pageRedirections[strtolower($sourcePageId)]['TargetPage']; } else { $res = $this->sqlite->query("select target from redirections where source = ?", $sourcePageId); if (!$res) { throw new RuntimeException("An exception has occurred with the query"); } $value = $this->sqlite->res2single($res); return $value; } } /** * * * For a conf file, it will update the Redirection Action Data as Referrer, Count Of Redirection, Redirection Date * * For a SQlite database, it will add a row into the log * * @param string $sourcePageId * @param $targetPageId * @param $type */ function logRedirection($sourcePageId, $targetPageId, $type) { if ($this->dataStoreType == null) { $this->initDataStore(); } if ($this->dataStoreType == self::DATA_STORE_TYPE_CONF_FILE) { $sourcePageId = strtolower($sourcePageId); $this->pageRedirections[$sourcePageId]['LastRedirectionDate'] = $this->currentDate; $this->pageRedirections[$sourcePageId]['LastReferrer'] = $_SERVER['HTTP_REFERER']; // This cause to add one after the first insert but yeah, this is going to dye anyway $this->pageRedirections[$sourcePageId]['CountOfRedirection'] += 1; $this->savePageRedirections(); } else { $row = array( "TIMESTAMP" => $this->currentDate, "SOURCE" => $sourcePageId, "TARGET" => $targetPageId, "REFERRER" => $_SERVER['HTTP_REFERER'], "TYPE" => $type ); $res = $this->sqlite->storeEntry('redirections_log', $row); if (!$res) { throw new RuntimeException("An error occurred"); } } } /** * Serialize and save the redirection data file * * ie Flush * */ function savePageRedirections() { if ($this->dataStoreType == null) { $this->initDataStore(); } if ($this->dataStoreType == self::DATA_STORE_TYPE_CONF_FILE) { io_saveFile(self::DATA_STORE_CONF_FILE_PATH, serialize($this->pageRedirections)); } else { $this->throwRuntimeException('SavePageRedirections must no be called for a SQLite data store'); } } /** * Validate URL * Allows for port, path and query string validations * @param string $url string containing url user input * @return boolean Returns TRUE/FALSE */ function isValidURL($url) { // of preg_match('/^https?:\/\//',$url) ? from redirect plugin return preg_match('|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i', $url); } /** * @param $inputExpression * @return false|int 1|0 * returns: * - 1 if the input expression is a pattern, * - 0 if not, * - FALSE if an error occurred. */ static function isRegularExpression($inputExpression) { $regularExpressionPattern = "/(\\/.*\\/[gmixXsuUAJ]?)/"; return preg_match($regularExpressionPattern, $inputExpression); } /** * * Set the data store type. The value must be one of the constants * * DATA_STORE_TYPE_CONF_FILE * * DATA_STORE_TYPE_SQLITE * * @param $dataStoreType * @return $this * */ public function setDataStoreType($dataStoreType) { $this->dataStoreType = $dataStoreType; $this->initDataStore(); return $this; } /** * Init the data store */ private function initDataStore() { if ($this->dataStoreType == null) { $this->sqlite = plugin_load('helper', 'sqlite'); if (!$this->sqlite) { $this->dataStoreType = self::DATA_STORE_TYPE_CONF_FILE; } else { $this->dataStoreType = self::DATA_STORE_TYPE_SQLITE; } } if ($this->getDataStoreType() == self::DATA_STORE_TYPE_CONF_FILE) { msg($this->getLang('SqliteMandatory'), MANAGER404_MSG_INFO, $allow = MSG_MANAGERS_ONLY); //Set the redirection data if (@file_exists(self::DATA_STORE_CONF_FILE_PATH)) { $this->pageRedirections = unserialize(io_readFile(self::DATA_STORE_CONF_FILE_PATH, false)); } } else { // initialize the database connection $pluginName = $this->infoPlugin['base']; if ($this->sqlite == null) { $this->sqlite = plugin_load('helper', 'sqlite'); if (!$this->sqlite) { $this->throwRuntimeException("Unable to load the sqlite plugin"); } } $init = $this->sqlite->init($pluginName, DOKU_PLUGIN . $pluginName . '/db/'); if (!$init) { msg($this->lang['SqliteUnableToInitialize'], MSG_MANAGERS_ONLY); return; } // Migration of the old store if (@file_exists(self::DATA_STORE_CONF_FILE_PATH)) { $this->dataStoreMigration(); } } } /** * Delete all redirections * Use with caution */ function deleteAllRedirections() { if ($this->dataStoreType == null) { $this->initDataStore(); } if ($this->dataStoreType == self::DATA_STORE_TYPE_SQLITE) { $res = $this->sqlite->query("delete from redirections"); if (!$res) { $this->throwRuntimeException('Errors during delete of all redirections'); } } else { if (file_exists(self::DATA_STORE_CONF_FILE_PATH)) { $res = unlink(self::DATA_STORE_CONF_FILE_PATH); if (!$res) { $this->throwRuntimeException('Unable to delete the file ' . self::DATA_STORE_TYPE_CONF_FILE); } } $this->pageRedirections = array(); } } /** * Return the number of redirections * @return integer */ function countRedirections() { if ($this->dataStoreType == null) { $this->initDataStore(); } if ($this->dataStoreType == self::DATA_STORE_TYPE_SQLITE) { $res = $this->sqlite->query("select count(1) from redirections"); if (!$res) { throw new RuntimeException('Errors during delete of all redirections'); } $value = $this->sqlite->res2single($res); return $value; } else { return count($this->pageRedirections); } } public function getDataStoreType() { if ($this->dataStoreType == null) { $this->initDataStore(); } return $this->dataStoreType; } /** * @return array */ private function getRedirections() { if ($this->dataStoreType == null) { $this->initDataStore(); } if ($this->dataStoreType == self::DATA_STORE_TYPE_SQLITE) { $res = $this->sqlite->query("select * from redirections"); if (!$res) { throw new RuntimeException('Errors during select of all redirections'); } $row = $this->sqlite->res2arr($res); return $row; } else { return $this->pageRedirections; } } /** * Dokuwiki will show a pink message when throwing an eexception * and it's difficult to see from where it comes * * This utility function will add the plugin name to it * * @param $message */ private function throwRuntimeException($message): void { throw new RuntimeException($this->getPluginName() . ' - ' . $message); } /** * Migrate from a conf file to sqlite */ function dataStoreMigration() { if (!file_exists(self::DATA_STORE_CONF_FILE_PATH)) { $this->throwRuntimeException("The file to migrate does not exist (" . self::DATA_STORE_CONF_FILE_PATH . ")"); } // We cannot use the getRedirections method because this is a sqlite data store // it will return nothing $pageRedirections = unserialize(io_readFile(self::DATA_STORE_CONF_FILE_PATH, false)); foreach ($pageRedirections as $key => $row) { $sourcePageId = $key; $targetPageId = $row['TargetPage']; $creationDate = $row['CreationDate']; $isValidate = $row['IsValidate']; if ($isValidate == 'Y') { $this->addRedirectionWithDate($sourcePageId, $targetPageId, $creationDate); } } rename(self::DATA_STORE_CONF_FILE_PATH, self::DATA_STORE_CONF_FILE_PATH . '.migrated'); } }