From a878cdfc00ec4fdc501082cc353eb481a79550c7 Mon Sep 17 00:00:00 2001 From: Kenji ITO Date: Mon, 18 Jan 2016 11:33:47 +0900 Subject: [PATCH 01/81] Added simple URL routing feature to public_html/index.php --- public_html/index.php | 6 ++ system/classes/router.class.php | 167 ++++++++++++++++++++++++++++++++ system/lib-database.php | 1 + 3 files changed, 174 insertions(+) create mode 100644 system/classes/router.class.php diff --git a/public_html/index.php b/public_html/index.php index 0608aee55..ef5d883e9 100644 --- a/public_html/index.php +++ b/public_html/index.php @@ -83,6 +83,12 @@ function fixTopic(&$A, $tid_list) } } +// If URL routing is enabled, then let the router handle the request +if (isset($_CONF['url_routing']) && !empty($_CONF['url_routing'])) { + require_once $_CONF['path_system'] . 'classes/router.class.php'; + Router::dispatch(); +} + // See if user has access to view topic else display message. // This check has already been done in lib-common so re check to figure out if // 404 message needs to be displayed. diff --git a/system/classes/router.class.php b/system/classes/router.class.php new file mode 100644 index 000000000..2bbe336ba --- /dev/null +++ b/system/classes/router.class.php @@ -0,0 +1,167 @@ + Date: Wed, 20 Jan 2016 12:31:09 +0900 Subject: [PATCH 02/81] Added convertUrl method to Router class --- system/classes/router.class.php | 150 +++++++++++++++++++++++++++++--- 1 file changed, 136 insertions(+), 14 deletions(-) diff --git a/system/classes/router.class.php b/system/classes/router.class.php index 2bbe336ba..8c53c5888 100644 --- a/system/classes/router.class.php +++ b/system/classes/router.class.php @@ -17,6 +17,9 @@ class Router const ROUTING_WITH_INDEX_PHP = 1; const ROUTING_WITHOUT_INDEX_PHP = 2; + // Placeholder pattern + const PATTERN_PLACEHOLDER = '|(@[a-zA-Z][0-9a-zA-Z_-]*)|'; + /** * @var bool */ @@ -90,15 +93,17 @@ public static function dispatch() default: // Unsupported method COM_errorLog(__METHOD__ . ': unknown HTTP request method "' . $_SERVER['REQUEST_METHOD'] . '" was supplied'); + return false; } // Get routing rules and routes from database - $sql = "SELECT * FROM {$_TABLES['routes']} WHERE method = {$method} ORDER BY priority "; + $sql = "SELECT * FROM {$_TABLES['routes']} WHERE method = " . DB_escapeString($method) . " ORDER BY priority "; $result = DB_query($sql); if (DB_error()) { COM_errorLog(__METHOD__ . ': ' . DB_error()); + return false; } @@ -111,7 +116,21 @@ public static function dispatch() COM_errorLog(__METHOD__ . ': route = ' . $route); } - if (preg_match_all('|/(@[a-z][0-9a-z_.-]*)|i', $rule, $matches, PREG_SET_ORDER)) { + // Try simple comparison without placeholders + if (strcasecmp($rule, $pathInfo) === 0) { + if ($routingType === self::ROUTING_WITH_INDEX_PHP) { + $route = $_CONF['site_url'] . $route; + } + + if (self::$debug) { + COM_errorLog(__METHOD__ . ': matched with simple comparison rule "' . $A['rule'] . '"'); + } + + header('Location: ' . $route); + } + + // Try comparison with placeholders + if (preg_match_all(self::PATTERN_PLACEHOLDER, $rule, $matches, PREG_SET_ORDER)) { $placeHolders = array(); // Replace placeholders in a rule with ones for regular expressions @@ -139,29 +158,132 @@ public static function dispatch() } if ($routingType === self::ROUTING_WITH_INDEX_PHP) { - $route = $_CONF['site_url'] . $route; + $route = $_CONF['site_url'] . $route; } if (self::$debug) { - COM_errorLog(__METHOD__ . ': matched with regx "' . $rule . '"'); + COM_errorLog(__METHOD__ . ': matched with regular expression rule "' . $A['rule'] . '"'); } header('Location: ' . $route); - } else { - if (strcasecmp($rule, $pathInfo) === 0) { - if ($routingType === self::ROUTING_WITH_INDEX_PHP) { - $route = $_CONF['site_url'] . $route; - } + } + } + + return false; + } + + /** + * Convert a URL + * e.g. [SITE_URL]/article.php?story=welcome -> [SITE_URL]/index.php/article/welcome or [SITE_URL]/article/welcome + * + * @param string $url + * @param int $requestMethod + * @return string + */ + public static function convertUrl($url, $requestMethod = self::HTTP_REQUEST_GET) + { + global $_CONF, $_TABLES; + + // URL routing is not supported + if (!isset($_CONF['url_routing'])) { + return $url; + } + + $routingType = intval($_CONF['url_routing'], 10); + + // URL routing is disabled + if ($routingType === self::ROUTING_DISABLED) { + return $url; + } elseif (self::$debug) { + COM_errorLog(__METHOD__ . ': routing type = ' . $routingType); + } + + // Strip $url of $_CONF['site_url'] + $url = str_ireplace($_CONF['site_url'], '', $url); + + // Check for $requestMethod + $requestMethod = intval($requestMethod, 10); + + if (($requestMethod < self::HTTP_REQUEST_GET) || ($requestMethod > self::HTTP_REQUEST_HEAD)) { + COM_errorLog(__METHOD__ . ': unknown request method "' . $requestMethod . '" was given'); + + return $url; + } + + // Get routing rules and routes from database + $sql = "SELECT * FROM {$_TABLES['routes']} WHERE method = " . DB_escapeString($requestMethod) . " ORDER BY priority "; + $result = DB_query($sql); + + if (DB_error()) { + COM_errorLog(__METHOD__ . ': ' . DB_error()); - if (self::$debug) { - COM_errorLog(__METHOD__ . ': matched with simple comparison "' . $rule . '"'); - } + return $url; + } + + while (($A = DB_fetchArray($result, false)) !== false) { + $rule = $A['rule']; + $route = $A['route']; + + if (self::$debug) { + COM_errorLog(__METHOD__ . ': rule = ' . $rule); + COM_errorLog(__METHOD__ . ': route = ' . $route); + } + + // Try simple comparison without placeholders + if (strcasecmp($route, $url) === 0) { + if ($routingType === self::ROUTING_WITH_INDEX_PHP) { + $rule = $_CONF['site_url'] . $rule; + } + + if (self::$debug) { + COM_errorLog(__METHOD__ . ': matched with simple comparison rule "' . $A['route'] . '"'); + } + + return $rule; + } + + // Try comparison with placeholders + if (preg_match_all(self::PATTERN_PLACEHOLDER, $route, $matches, PREG_SET_ORDER)) { + $placeHolders = array(); + + // Replace placeholders in a rule with ones for regular expressions + foreach ($matches as $match) { + $placeHolders[] = $match[1]; + $route = str_ireplace($match[1], '([^/]+)', $route); + } + + // Escape a period and a question mark so that they can safely be used in a regular expression + $route = str_replace(array('.', '?'), array('\.', '\?'), $route); + $route = '|' . $route . '|'; + + if (!preg_match($route, $url, $matches)) { + continue; + } - header('Location: ' . $route); + array_shift($matches); + + if (count($placeHolders) !== count($matches)) { + continue; } + + foreach ($matches as $match) { + $match = urlencode($match); + $placeHolder = array_shift($placeHolders); + $url = str_ireplace($placeHolder, $match, $url); + } + + if ($routingType === self::ROUTING_WITH_INDEX_PHP) { + $url = $_CONF['site_url'] . $url; + } + + if (self::$debug) { + COM_errorLog(__METHOD__ . ': matched with regular expression rule "' . $A['route'] . '"'); + } + + return $url; } } - return false; + return $url; } } From 7239b33623b9df04d69c8087d6d285d50952bfd7 Mon Sep 17 00:00:00 2001 From: Kenji ITO Date: Wed, 20 Jan 2016 14:07:29 +0900 Subject: [PATCH 03/81] COM_buildURL now builds a URL based on $_CONF['url_routing'] --- public_html/index.php | 3 +- public_html/lib-common.php | 5 +- system/classes/router.class.php | 32 +++- system/classes/url.class.php | 257 +++++++++++++++----------------- 4 files changed, 153 insertions(+), 144 deletions(-) diff --git a/public_html/index.php b/public_html/index.php index ef5d883e9..0b3d3a116 100644 --- a/public_html/index.php +++ b/public_html/index.php @@ -84,8 +84,7 @@ function fixTopic(&$A, $tid_list) } // If URL routing is enabled, then let the router handle the request -if (isset($_CONF['url_routing']) && !empty($_CONF['url_routing'])) { - require_once $_CONF['path_system'] . 'classes/router.class.php'; +if ($_CONF['url_rewrite'] && isset($_CONF['url_routing']) && !empty($_CONF['url_routing'])) { Router::dispatch(); } diff --git a/public_html/lib-common.php b/public_html/lib-common.php index 7aff2c940..ce5f6613e 100755 --- a/public_html/lib-common.php +++ b/public_html/lib-common.php @@ -199,8 +199,9 @@ * This provides optional URL rewriting functionality. */ -require_once( $_CONF['path_system'] . 'classes/url.class.php' ); -$_URL = new url( $_CONF['url_rewrite'] ); +require_once $_CONF['path_system'] . 'classes/router.class.php'; +require_once $_CONF['path_system'] . 'classes/url.class.php'; +$_URL = new Url($_CONF['url_rewrite'], $_CONF['url_routing']); /** * This is our HTML template class. It is the same one found in PHPLib and is diff --git a/system/classes/router.class.php b/system/classes/router.class.php index 8c53c5888..7d29d0ab8 100644 --- a/system/classes/router.class.php +++ b/system/classes/router.class.php @@ -44,6 +44,11 @@ public static function dispatch() { global $_CONF, $_TABLES; + // URL rewrite is disabled + if (!$_CONF['url_rewrite']) { + return false; + } + // URL routing is not supported if (!isset($_CONF['url_routing'])) { return false; @@ -184,6 +189,11 @@ public static function convertUrl($url, $requestMethod = self::HTTP_REQUEST_GET) { global $_CONF, $_TABLES; + // URL rewrite is disabled + if (!$_CONF['url_rewrite']) { + return $url; + } + // URL routing is not supported if (!isset($_CONF['url_routing'])) { return $url; @@ -231,22 +241,26 @@ public static function convertUrl($url, $requestMethod = self::HTTP_REQUEST_GET) // Try simple comparison without placeholders if (strcasecmp($route, $url) === 0) { + $retval = $rule; + if ($routingType === self::ROUTING_WITH_INDEX_PHP) { - $rule = $_CONF['site_url'] . $rule; + $retval = '/index.php' . $retval; } + $retval = $_CONF['site_url'] . $retval; + if (self::$debug) { - COM_errorLog(__METHOD__ . ': matched with simple comparison rule "' . $A['route'] . '"'); + COM_errorLog(__METHOD__ . ': matched with simple comparison route "' . $A['route'] . '"'); } - return $rule; + return $retval; } // Try comparison with placeholders if (preg_match_all(self::PATTERN_PLACEHOLDER, $route, $matches, PREG_SET_ORDER)) { $placeHolders = array(); - // Replace placeholders in a rule with ones for regular expressions + // Replace placeholders in a route with ones for regular expressions foreach ($matches as $match) { $placeHolders[] = $match[1]; $route = str_ireplace($match[1], '([^/]+)', $route); @@ -269,18 +283,22 @@ public static function convertUrl($url, $requestMethod = self::HTTP_REQUEST_GET) foreach ($matches as $match) { $match = urlencode($match); $placeHolder = array_shift($placeHolders); - $url = str_ireplace($placeHolder, $match, $url); + $rule = str_ireplace($placeHolder, $match, $rule); } + $retval = $rule; + if ($routingType === self::ROUTING_WITH_INDEX_PHP) { - $url = $_CONF['site_url'] . $url; + $retval = '/index.php' . $retval; } + $retval = $_CONF['site_url'] . $retval; + if (self::$debug) { COM_errorLog(__METHOD__ . ': matched with regular expression rule "' . $A['route'] . '"'); } - return $url; + return $retval; } } diff --git a/system/classes/url.class.php b/system/classes/url.class.php index 482ce5b09..d858eb3c6 100644 --- a/system/classes/url.class.php +++ b/system/classes/url.class.php @@ -2,7 +2,7 @@ /* Reminder: always indent with 4 spaces (no tabs). */ // +---------------------------------------------------------------------------+ -// | Geeklog 1.8 | +// | Geeklog 2.1 | // +---------------------------------------------------------------------------+ // | url.class.php | // | | @@ -30,152 +30,103 @@ // +---------------------------------------------------------------------------+ /** -* This class will allow you to use friendlier URL's, like: -* http://www.example.com/index.php/arg_value_1/arg_value_2/ instead of -* uglier http://www.example.com?arg1=value1&arg2=value2. -* NOTE: this does not currently work under windows as there is a well documented -* bug with IIS and PATH_INFO. Not sure yet if this will work with windows under -* apache. This was built so you could use this class and just disable it -* if you are an IIS user. -* -* @author Tony Bibbs -* -*/ -class url { + * This class will allow you to use friendlier URL's, like: + * http://www.example.com/index.php/arg_value_1/arg_value_2/ instead of + * uglier http://www.example.com?arg1=value1&arg2=value2. + * NOTE: this does not currently work under windows as there is a well documented + * bug with IIS and PATH_INFO. Not sure yet if this will work with windows under + * apache. This was built so you could use this class and just disable it + * if you are an IIS user. + * + * @author Tony Bibbs + */ +class Url +{ /** - * @access private - */ - private $_arguments = array(); // Array of argument names + * @var array + */ + private $arguments = array(); // Array of argument names + /** - * @access private - */ - private $_enabled = true; + * @var bool + */ + private $urlRewrite = true; /** - * Constructor - * - * @param boolean $enabled whether rewriting is enabled - * - */ - function url($enabled=true) - { - $this->setEnabled($enabled); - $this->_arguments = array(); - if ($this->_enabled) { - $this->_getArguments(); - } - } + * @var int + */ + private $urlRouting; /** - * Grabs any variables from the query string - * - * @access private - */ - function _getArguments() + * Constructor + * + * @param bool $urlRewrite whether rewriting is enabled + * @param int $urlRouting URL routing mode, see Router class + */ + public function __construct($urlRewrite = true, $urlRouting = Router::ROUTING_DISABLED) { - if (isset($_SERVER['PATH_INFO'])) { - if ($_SERVER['PATH_INFO'] == '') { - if (isset($_ENV['ORIG_PATH_INFO'])) { - $this->_arguments = explode('/', $_ENV['ORIG_PATH_INFO']); - } else { - $this->_arguments = array(); - } - } else { - $this->_arguments = explode('/', $_SERVER['PATH_INFO']); - } - array_shift($this->_arguments); - } elseif (isset($_ENV['ORIG_PATH_INFO'])) { - $this->_arguments = explode('/', substr($_ENV['ORIG_PATH_INFO'], 1)); - } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { - $this->_arguments = explode('/', substr($_SERVER['ORIG_PATH_INFO'], 1)); - - // Added for IIS 7 to work in FastCGI mode - array_shift ($this->_arguments); - if ( $this->_arguments[0] == substr($_SERVER['SCRIPT_NAME'],1) ) { - array_shift($this->_arguments); - } - // end of add + $this->urlRewrite = (bool) $urlRewrite; + $urlRouting = intval($urlRouting, 10); + if (($urlRouting >= Router::ROUTING_DISABLED) && ($urlRouting <= Router::ROUTING_WITHOUT_INDEX_PHP)) { + $this->urlRouting = $urlRouting; } else { - $this->_arguments = array(); + $this->urlRouting = Router::ROUTING_DISABLED; } - } - /** - * Enables url rewriting, otherwise URL's are passed back - * - * @param boolean $switch turns URL rewriting on/off - * - */ - function setEnabled($switch) - { - if ($switch) { - $this->_enabled = true; - } else { - $this->_enabled = false; - } - } + $this->arguments = array(); - /** - * Returns whether or not URL rewriting is enabled - * - * @return boolean true if URl rewriting is enabled, otherwise false - * - */ - function isEnabled() - { - return $this->_enabled; + if ($this->urlRewrite) { + $this->getArguments(); + } } /** - * Returns the number of variables found in query string - * - * This is particularly useful just before calling setArgNames() method - * - * @return int Number of arguments found in URL - * - */ - function numArguments() + * Returns the number of variables found in query string + * This is particularly useful just before calling setArgNames() method + * + * @return int Number of arguments found in URL + */ + public function numArguments() { - return count($this->_arguments); + return count($this->arguments); } /** - * Assigns logical names to query string variables - * - * @param array $names String array of names to assign to variables pulled from query string - * @return boolean true on success otherwise false - * - */ - function setArgNames($names) + * Assigns logical names to query string variables + * + * @param array $names String array of names to assign to variables pulled from query string + * @return boolean true on success otherwise false + */ + public function setArgNames($names) { - if (count($names) < count($this->_arguments)) { + if (count($names) < count($this->arguments)) { print "URL Class: number of names passed to setArgNames must be equal or greater than number of arguments found in URL"; exit; } if (is_array($names)) { $newArray = array(); - for ($i = 1; $i <= count($this->_arguments); $i++) { - $newArray[current($names)] = current($this->_arguments); + for ($i = 1; $i <= count($this->arguments); $i++) { + $newArray[current($names)] = current($this->arguments); next($names); - next($this->_arguments); + next($this->arguments); } - $this->_arguments = $newArray; - reset($this->_arguments); + $this->arguments = $newArray; + reset($this->arguments); } else { return false; } + return true; } /** - * Gets the value for an argument - * - * @param string $name Name of argument to fetch value for - * @return mixed returns value for a given argument - * - */ - function getArgument($name) + * Gets the value for an argument + * + * @param string $name Name of argument to fetch value for + * @return mixed returns value for a given argument + */ + public function getArgument($name) { // if in GET VARS array return it if (!empty($_GET[$name])) { @@ -190,35 +141,43 @@ function getArgument($name) // end of add // ok, pull from query string - if (in_array($name,array_keys($this->_arguments))) { - return $this->_arguments[$name]; + if (in_array($name, array_keys($this->arguments))) { + return $this->arguments[$name]; } return ''; } /** - * Builds crawler friendly URL if URL rewriting is enabled - * - * This function will attempt to build a crawler friendly URL. If this feature is - * disabled because of platform issue it just returns original $url value - * - * @param string $url URL to try and convert - * @return string rewritten if _isenabled is true otherwise original url - * - */ - function buildURL($url) + * Builds crawler friendly URL if URL rewriting is enabled + * This function will attempt to build a crawler friendly URL. If this feature is + * disabled because of platform issue it just returns original $url value + * + * @param string $url URL to try and convert + * @return string rewritten if $this->urlRewrite is true otherwise original url + */ + public function buildURL($url) { - if (!$this->isEnabled()) { + if (!$this->urlRewrite) { return $url; } - $pos = strpos($url,'?'); - $query_string = substr($url,$pos+1); + if (($this->urlRouting === Router::ROUTING_WITH_INDEX_PHP) || + ($this->urlRouting === Router::ROUTING_WITHOUT_INDEX_PHP)) { + $newUrl = Router::convertUrl($url); + + if ($newUrl !== $url) { + return $newUrl; + } + } + + $pos = strpos($url, '?'); + $query_string = substr($url, $pos + 1); $finalList = array(); - $paramList = explode('&',$query_string); + $paramList = explode('&', $query_string); + for ($i = 1; $i <= count($paramList); $i++) { - $keyValuePairs = explode('=',current($paramList)); + $keyValuePairs = explode('=', current($paramList)); if (is_array($keyValuePairs)) { $argName = current($keyValuePairs); next($keyValuePairs); @@ -226,6 +185,7 @@ function buildURL($url) } next($paramList); } + $newArgs = '/'; for ($i = 1; $i <= count($finalList); $i++) { $newArgs .= current($finalList); @@ -234,8 +194,39 @@ function buildURL($url) } next($finalList); } - return str_replace('?' . $query_string,$newArgs,$url); + + return str_replace('?' . $query_string, $newArgs, $url); } -} -?> + /** + * Grabs any variables from the query string + */ + private function getArguments() + { + if (isset($_SERVER['PATH_INFO'])) { + if ($_SERVER['PATH_INFO'] == '') { + if (isset($_ENV['ORIG_PATH_INFO'])) { + $this->arguments = explode('/', $_ENV['ORIG_PATH_INFO']); + } else { + $this->arguments = array(); + } + } else { + $this->arguments = explode('/', $_SERVER['PATH_INFO']); + } + array_shift($this->arguments); + } elseif (isset($_ENV['ORIG_PATH_INFO'])) { + $this->arguments = explode('/', substr($_ENV['ORIG_PATH_INFO'], 1)); + } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { + $this->arguments = explode('/', substr($_SERVER['ORIG_PATH_INFO'], 1)); + + // Added for IIS 7 to work in FastCGI mode + array_shift($this->arguments); + if ($this->arguments[0] == substr($_SERVER['SCRIPT_NAME'], 1)) { + array_shift($this->arguments); + } + // end of add + } else { + $this->arguments = array(); + } + } +} From 14d5cbbe96ebd8cc168be8a31049b2fbe75ae85a Mon Sep 17 00:00:00 2001 From: Kenji ITO Date: Wed, 20 Jan 2016 15:14:00 +0900 Subject: [PATCH 04/81] Added upgrading code --- language/english.php | 2 + language/english_utf-8.php | 2 + language/japanese_utf-8.php | 4 +- public_html/admin/install/config-install.php | 1 + public_html/admin/install/lib-upgrade.php | 9 +++++ sql/mssql_tableanddata.php | 21 ++++++++++ sql/mysql_tableanddata.php | 14 +++++++ sql/pgsql_tableanddata.php | 14 +++++++ sql/updates/mssql_2.1.0_to_2.1.2.php | 41 ++++++++++++++++++++ sql/updates/mysql_2.1.0_to_2.1.2.php | 34 ++++++++++++++++ sql/updates/pgsql_2.1.0_to_2.1.2.php | 34 ++++++++++++++++ 11 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 sql/updates/mssql_2.1.0_to_2.1.2.php create mode 100644 sql/updates/mysql_2.1.0_to_2.1.2.php create mode 100644 sql/updates/pgsql_2.1.0_to_2.1.2.php diff --git a/language/english.php b/language/english.php index 80ee576dd..5e7dffd63 100644 --- a/language/english.php +++ b/language/english.php @@ -2143,6 +2143,7 @@ 'censorlist' => "Censor List", 'ip_lookup' => "IP Lookup", 'url_rewrite' => "Enable URL Rewrite", + 'url_routing' => 'Enable URL routing', 'cdn_hosted' => "Use CDN-hosted copy of jQuery", 'meta_tags' => "Meta Tags", 'meta_description' => "Default Meta Description", @@ -2364,6 +2365,7 @@ 33 => array('Disabled' => 0, 'Enabled' => 1, 'Enabled (No Links)' => 2, 'Enabled (No Outbound Links)' => 3), 34 => array('grid' => 'grid', 'list' => 'list'), 35 => array('default' => 'default', 'Name (asc)' => 'NAME_ASC', 'Name (desc)' => 'NAME_DESC', 'Type (asc)' => 'TYPE_ASC', 'Type (desc)' => 'TYPE_DESC', 'Modified (asc)' => 'MODIFIED_ASC', 'Modified (desc)' => 'MODIFIED_DESC'), + 36 => array('Disabled' => 0, 'Enabled (with index.php)' => 1, 'Enabled (without index.php)' => 2), ); ################################################################################ diff --git a/language/english_utf-8.php b/language/english_utf-8.php index 27c414c06..f49480442 100644 --- a/language/english_utf-8.php +++ b/language/english_utf-8.php @@ -2143,6 +2143,7 @@ 'censorlist' => "Censor List", 'ip_lookup' => "IP Lookup", 'url_rewrite' => "Enable URL Rewrite", + 'url_routing' => 'Enable URL routing', 'cdn_hosted' => "Use CDN-hosted copy of jQuery", 'meta_tags' => "Meta Tags", 'meta_description' => "Default Meta Description", @@ -2364,6 +2365,7 @@ 33 => array('Disabled' => 0, 'Enabled' => 1, 'Enabled (No Links)' => 2, 'Enabled (No Outbound Links)' => 3), 34 => array('grid' => 'grid', 'list' => 'list'), 35 => array('default' => 'default', 'Name (asc)' => 'NAME_ASC', 'Name (desc)' => 'NAME_DESC', 'Type (asc)' => 'TYPE_ASC', 'Type (desc)' => 'TYPE_DESC', 'Modified (asc)' => 'MODIFIED_ASC', 'Modified (desc)' => 'MODIFIED_DESC'), + 36 => array('Disabled' => 0, 'Enabled (with index.php)' => 1, 'Enabled (without index.php)' => 2), ); ################################################################################ diff --git a/language/japanese_utf-8.php b/language/japanese_utf-8.php index 94a146111..a308caed2 100644 --- a/language/japanese_utf-8.php +++ b/language/japanese_utf-8.php @@ -2144,6 +2144,7 @@ 'censorlist' => 'バッドワード', 'ip_lookup' => 'IPアドレス検索', 'url_rewrite' => 'URLリライト', + 'url_routing' => 'URLルーティング', 'cdn_hosted' => 'CDNのjQueryを使用する', 'meta_tags' => 'メタタグ', 'meta_description' => 'サイトの説明文のメタタグ', @@ -2357,7 +2358,8 @@ 32 => array('表示しない' => 0, '記事のページにのみ表示する' => 1, '記事と話題の両方で表示する' => 2), 33 => array('無効にする' => 0, '有効にする' => 1, '有効にする(リンクなし)' => 2, '有効にする(外部リンクなし)' => 3), 34 => array('グリッド' => 'grid', 'リスト' => 'list'), - 35 => array('デフォルト' => 'default', '名前(昇順)' => 'NAME_ASC', '名前(降順)' => 'NAME_DESC', '種類(昇順)' => 'TYPE_ASC', '種類(降順)' => 'TYPE_DESC', '変更日時(昇順)' => 'MODIFIED_ASC', '変更日時(降順)' => 'MODIFIED_DESC') + 35 => array('デフォルト' => 'default', '名前(昇順)' => 'NAME_ASC', '名前(降順)' => 'NAME_DESC', '種類(昇順)' => 'TYPE_ASC', '種類(降順)' => 'TYPE_DESC', '変更日時(昇順)' => 'MODIFIED_ASC', '変更日時(降順)' => 'MODIFIED_DESC'), + 36 => array('無効にする' => 0, '有効にする(index.phpあり)' => 1, '有効にする(index.phpなし)' => 2), ); ############################################################################### diff --git a/public_html/admin/install/config-install.php b/public_html/admin/install/config-install.php index 4711168b1..82d4eda8e 100644 --- a/public_html/admin/install/config-install.php +++ b/public_html/admin/install/config-install.php @@ -57,6 +57,7 @@ function install_config() $c->add('owner_name','','text',0,0,NULL,1000,TRUE, $me, 0); $c->add('copyrightyear',date('Y'),'text',0,0,NULL,1440,FALSE, $me, 0); $c->add('url_rewrite',FALSE,'select',0,0,1,1800,TRUE, $me, 0); + $c->add('url_routing',FALSE,'select',0,0,36,1850,TRUE, $me, 0); $c->add('cdn_hosted',FALSE,'select',0,0,1,1900,TRUE, $me, 0); $c->add('meta_tags',0,'select',0,0,23,2000,TRUE, $me, 0); $c->add('meta_description','Geeklog - The secure Content Management System.','textarea',0,0,NULL,2010,TRUE, $me, 0); diff --git a/public_html/admin/install/lib-upgrade.php b/public_html/admin/install/lib-upgrade.php index 69b32e093..6accf9209 100644 --- a/public_html/admin/install/lib-upgrade.php +++ b/public_html/admin/install/lib-upgrade.php @@ -508,6 +508,15 @@ function INST_doDatabaseUpgrades($current_gl_version) $_SQL = ''; break; + case '2.1.0': + require_once $_CONF['path'] . 'sql/updates/' . $_DB_dbms . '_2.1.0_to_2.1.2.php'; + INST_updateDB($_SQL); + + update_ConfValuesFor212(); + $current_gl_version = '2.1.2'; + $_SQL = ''; + break; + default: $done = true; } diff --git a/sql/mssql_tableanddata.php b/sql/mssql_tableanddata.php index 18451b15c..de0dc6056 100644 --- a/sql/mssql_tableanddata.php +++ b/sql/mssql_tableanddata.php @@ -253,6 +253,15 @@ ) ON [PRIMARY] "; +$_SQL[] = "CREATE TABLE [dbo].[{$_TABLES['routes']}] ( + [rid] [int] NOT NULL IDENTITY (1, 1), + [method] [int] NOT NULL DEFAULT 1, + [rule] [varchar] (255) NOT NULL DEFAULT '', + [route] [varchar] (255) NOT NULL DEFAULT '', + [priority] [int] NOT NULL DEFAULT 100 +) ON [PRIMARY] +"; + $_SQL[] = " CREATE TABLE [dbo].[{$_TABLES['sessions']}] ( [sess_id] [numeric](10, 0) NOT NULL , @@ -654,6 +663,13 @@ ) ON [PRIMARY] "; +$_SQL[] = "ALTER TABLE [dbo].[{$_TABLES['routes']}] ADD + CONSTRAINT [PK_{$_TABLES['routes']}] PRIMARY KEY CLUSTERED + ( + [rid] + ) ON [PRIMARY] +"; + $_SQL[] = "ALTER TABLE [dbo].[{$_TABLES['sessions']}] ADD CONSTRAINT [PK_{$_TABLES['sessions']}] PRIMARY KEY CLUSTERED ( @@ -1521,6 +1537,11 @@ $_SQL[] = "INSERT INTO {$_TABLES['postmodes']} (code, name) VALUES ('plaintext','Plain Old Text')"; $_SQL[] = "INSERT INTO {$_TABLES['postmodes']} (code, name) VALUES ('html','HTML Formatted')"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/stats', '/stats.php')"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/topic/@topic', '/index.php?topic=@topic')"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/page/@page', '/staticpages/index.php?page=@page')"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/article/@sid', '/article.php?story=@sid')"; + $_SQL[] = "INSERT INTO {$_TABLES['sortcodes']} (code, name) VALUES ('ASC','Oldest First')"; $_SQL[] = "INSERT INTO {$_TABLES['sortcodes']} (code, name) VALUES ('DESC','Newest First')"; diff --git a/sql/mysql_tableanddata.php b/sql/mysql_tableanddata.php index ab289da99..b2c56caf8 100644 --- a/sql/mysql_tableanddata.php +++ b/sql/mysql_tableanddata.php @@ -250,6 +250,15 @@ ) ENGINE=MyISAM "; +$_SQL[] = "CREATE TABLE {$_TABLES['routes']} ( + `rid` int(11) NOT NULL AUTO_INCREMENT, + `method` int(11) NOT NULL DEFAULT '1', + `rule` varchar(255) NOT NULL DEFAULT '', + `route` varchar(255) NOT NULL DEFAULT '', + `priority` int(11) NOT NULL DEFAULT '100', + PRIMARY KEY (`rid`) +)"; + $_SQL[] = " CREATE TABLE {$_TABLES['sessions']} ( sess_id int(10) unsigned NOT NULL default '0', @@ -799,6 +808,11 @@ $_DATA[] = "INSERT INTO {$_TABLES['postmodes']} (code, name) VALUES ('plaintext','Plain Old Text') "; $_DATA[] = "INSERT INTO {$_TABLES['postmodes']} (code, name) VALUES ('html','HTML Formatted') "; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/stats', '/stats.php')"; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/topic/@topic', '/index.php?topic=@topic')"; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/page/@page', '/staticpages/index.php?page=@page')"; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/article/@sid', '/article.php?story=@sid')"; + $_DATA[] = "INSERT INTO {$_TABLES['sortcodes']} (code, name) VALUES ('ASC','Oldest First') "; $_DATA[] = "INSERT INTO {$_TABLES['sortcodes']} (code, name) VALUES ('DESC','Newest First') "; diff --git a/sql/pgsql_tableanddata.php b/sql/pgsql_tableanddata.php index a1c323429..2845ca5ab 100644 --- a/sql/pgsql_tableanddata.php +++ b/sql/pgsql_tableanddata.php @@ -249,6 +249,15 @@ ) "; +$_SQL[] = "CREATE TABLE {$_TABLES['routes']} ( + rid SERIAL, + method int NOT NULL DEFAULT 1, + rule varchar(255) NOT NULL DEFAULT '', + route varchar(255) NOT NULL DEFAULT '', + priority int NOT NULL DEFAULT 100, + PRIMARY KEY (rid) +)"; + $_SQL[] = " CREATE TABLE {$_TABLES['sessions']} ( sess_id int NOT NULL default '0', @@ -817,6 +826,11 @@ $_DATA[] = "INSERT INTO {$_TABLES['postmodes']} (code, name) VALUES ('plaintext','Plain Old Text') "; $_DATA[] = "INSERT INTO {$_TABLES['postmodes']} (code, name) VALUES ('html','HTML Formatted') "; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/stats', '/stats.php')"; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/topic/@topic', '/index.php?topic=@topic')"; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/page/@page', '/staticpages/index.php?page=@page')"; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/article/@sid', '/article.php?story=@sid')"; + $_DATA[] = "INSERT INTO {$_TABLES['sortcodes']} (code, name) VALUES ('ASC','Oldest First') "; $_DATA[] = "INSERT INTO {$_TABLES['sortcodes']} (code, name) VALUES ('DESC','Newest First') "; diff --git a/sql/updates/mssql_2.1.0_to_2.1.2.php b/sql/updates/mssql_2.1.0_to_2.1.2.php new file mode 100644 index 000000000..3dc2901d4 --- /dev/null +++ b/sql/updates/mssql_2.1.0_to_2.1.2.php @@ -0,0 +1,41 @@ +add('url_routing',FALSE,'select',0,0,36,1850,TRUE, $me, 0); + + return true; +} diff --git a/sql/updates/mysql_2.1.0_to_2.1.2.php b/sql/updates/mysql_2.1.0_to_2.1.2.php new file mode 100644 index 000000000..eb08b3ee3 --- /dev/null +++ b/sql/updates/mysql_2.1.0_to_2.1.2.php @@ -0,0 +1,34 @@ +add('url_routing',FALSE,'select',0,0,36,1850,TRUE, $me, 0); + + return true; +} diff --git a/sql/updates/pgsql_2.1.0_to_2.1.2.php b/sql/updates/pgsql_2.1.0_to_2.1.2.php new file mode 100644 index 000000000..06402dbbb --- /dev/null +++ b/sql/updates/pgsql_2.1.0_to_2.1.2.php @@ -0,0 +1,34 @@ +add('url_routing',FALSE,'select',0,0,36,1850,TRUE, $me, 0); + + return true; +} From d2cdb0267addbb4ed5cc4834c3a1e2c6a296d564 Mon Sep 17 00:00:00 2001 From: Kenji ITO Date: Wed, 20 Jan 2016 20:47:48 +0900 Subject: [PATCH 05/81] Added admin/router.php --- public_html/admin/router.php | 54 ++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 public_html/admin/router.php diff --git a/public_html/admin/router.php b/public_html/admin/router.php new file mode 100644 index 000000000..44f82fd2a --- /dev/null +++ b/public_html/admin/router.php @@ -0,0 +1,54 @@ + $MESSAGE[30]) + ); + COM_accessLog("User {$_USER['username']} tried to illegally access the URL routing administration screen"); + COM_output($display); + exit; +} + From 1d01e545e08c3912a4065546e0c03e666d65c09d Mon Sep 17 00:00:00 2001 From: Kenji ITO Date: Fri, 22 Jan 2016 15:01:55 +0900 Subject: [PATCH 06/81] Almost finished without documents --- language/english.php | 28 + language/english_utf-8.php | 28 + language/japanese_utf-8.php | 28 + public_html/admin/router.php | 510 +++++++++++++++++- .../modern_curve/admin/router/index.html | 0 .../admin/router/routereditor.thtml | 48 ++ .../modern_curve/images/icons/router.png | Bin 0 -> 2165 bytes public_html/lib-common.php | 19 +- system/classes/router.class.php | 121 +++-- 9 files changed, 722 insertions(+), 60 deletions(-) create mode 100644 public_html/layout/modern_curve/admin/router/index.html create mode 100644 public_html/layout/modern_curve/admin/router/routereditor.thtml create mode 100644 public_html/layout/modern_curve/images/icons/router.png diff --git a/language/english.php b/language/english.php index 5e7dffd63..0d7f06ef0 100644 --- a/language/english.php +++ b/language/english.php @@ -1336,6 +1336,31 @@ 55 => 'Articles' ); +############################################################################### +# admin/router.php + +$LANG_ROUTER = array( + 1 => 'URL routing', + 2 => 'Routing Manager', + 3 => 'ID', + 4 => 'method', + 5 => 'rule', + 6 => 'route', + 7 => 'priority', + 8 => 'Increase priority', + 9 => 'Decrease priority', + 10 => 'Edit routing', + 11 => 'To modify or delete a route, click on the route\'s edit icon below. To create a new route, click on "Create New" above. When you use placeholders (@), you must define the same placeholders in a rule and its route.', + 12 => 'Bad request method', + 13 => 'Rule is a mandatory item.', + 14 => 'Route is a mandatory item.', + 15 => 'Placeholders (@) in a rule and those in a route must be the same.', + 16 => 'Route must not start with "index.php".', + 17 => 'Database error occurred.', + 18 => 'To enable URL routing, you have to enable URL rewrite.', + 19 => '
  • Placeholders (@) must be the same both in a rule and its route.
  • A placeholder starts with "@", followed by an alphabet, optionally followed by any length of alphabet or digit.
  • Placeholders are case-sensitive.
', +); + ############################################################################### # confirmation and error messages @@ -1452,6 +1477,9 @@ 118 => 'Click to select a date', 119 => 'More..', 120 => 'Send this?', + 121 => 'Saved a URL routing item.', + 122 => 'Could not save a URL routing item.', + 123 => 'Deleted a URL routing item.', 400 => 'Not all required fields have been passed validation', // Error codes in the 400 range reserved for CUSTOM membership 401 => 'Please enter Fullname', 500 => 'The Template Cache has been successfully cleared.' diff --git a/language/english_utf-8.php b/language/english_utf-8.php index f49480442..d5e70af6d 100644 --- a/language/english_utf-8.php +++ b/language/english_utf-8.php @@ -1336,6 +1336,31 @@ 55 => 'Articles' ); +############################################################################### +# admin/router.php + +$LANG_ROUTER = array( + 1 => 'URL routing', + 2 => 'Routing Manager', + 3 => 'ID', + 4 => 'method', + 5 => 'rule', + 6 => 'route', + 7 => 'priority', + 8 => 'Increase priority', + 9 => 'Decrease priority', + 10 => 'Edit routing', + 11 => 'To modify or delete a route, click on the route\'s edit icon below. To create a new route, click on "Create New" above. When you use placeholders (@), you must define the same placeholders in a rule and its route.', + 12 => 'Bad request method', + 13 => 'Rule is a mandatory item.', + 14 => 'Route is a mandatory item.', + 15 => 'Placeholders (@) in a rule and those in a route must be the same.', + 16 => 'Route must not start with "index.php".', + 17 => 'Database error occurred.', + 18 => 'To enable URL routing, you have to enable URL rewrite.', + 19 => '
  • Placeholders (@) must be the same both in a rule and its route.
  • A placeholder starts with "@", followed by an alphabet, optionally followed by any length of alphabet or digit.
  • Placeholders are case-sensitive.
', +); + ############################################################################### # confirmation and error messages @@ -1452,6 +1477,9 @@ 118 => 'Click to select a date', 119 => 'More..', 120 => 'Send this?', + 121 => 'Saved a URL routing item.', + 122 => 'Could not save a URL routing item.', + 123 => 'Deleted a URL routing item.', 400 => 'Not all required fields have been passed validation', // Error codes in the 400 range reserved for CUSTOM membership 401 => 'Please enter Fullname', 500 => 'The Template Cache has been successfully cleared.' diff --git a/language/japanese_utf-8.php b/language/japanese_utf-8.php index a308caed2..285c3a132 100644 --- a/language/japanese_utf-8.php +++ b/language/japanese_utf-8.php @@ -1335,6 +1335,31 @@ 55 => '記事' ); +############################################################################### +# admin/router.php + +$LANG_ROUTER = array( + 1 => 'URLルーティング', + 2 => 'ルーティング管理', + 3 => 'ID', + 4 => 'メソッド', + 5 => 'ルール', + 6 => 'ルート', + 7 => '優先順位', + 8 => '優先順位を上げる', + 9 => '優先順位を下げる', + 10 => 'ルーティングの編集', + 11 => '

ルーティングの編集・削除は編集アイコンを、作成は上の「新規作成」をクリックしてください。優先順位の変更は、[↑][↓]をクリックしてください。

', + 12 => 'リクエストメソッドが不正です。', + 13 => 'ルールは必須項目です。', + 14 => 'ルートは必須項目です。', + 15 => 'ルールとルートで同じプレースホルダー(@)を定義しなければなりません。', + 16 => 'ルートの先頭にindex.phpを含むことはできません。', + 17 => 'データベース操作でエラーが発生しました。', + 18 => 'URLルーティングを有効にするには、URLリライトを有効にしてください。', + 19 => '
  • プレースホルダー(@)はルールとルートで同じものを定義してください。
  • プレースホルダーは@で始まり、1文字目は英字、2文字目以降は英数字を使えます。
  • プレースホルダーは大文字・小文字を区別します。
', +); + ############################################################################### # confirmation and error messages @@ -1451,6 +1476,9 @@ 118 => 'クリックして日付を選択してください', 119 => 'More..', 120 => 'この項目を送信しますか?', + 121 => 'URLルーティングのデータを保存しました。', + 122 => 'URLルーティングのデータを保存できませんでした。', + 123 => 'URLルーティングのデータを削除しました。', 400 => '検証に通っていない必須のフィールドがあります。', 401 => '氏名を入力してください。', 500 => 'テンプレートのキャッシュを削除しました。' diff --git a/public_html/admin/router.php b/public_html/admin/router.php index 44f82fd2a..b8786e9c1 100644 --- a/public_html/admin/router.php +++ b/public_html/admin/router.php @@ -33,16 +33,13 @@ * URL routing administration page: Create, edit, delete routing rules * for your Geeklog site. */ - // Geeklog common function library require_once '../lib-common.php'; // Security check to ensure user even belongs on this page require_once './auth.inc.php'; -$display = ''; - -if (!SEC_hasRights('url_routing.edit')) { +if (!SEC_inGroup('Root')) { $display = COM_createHTMLDocument( COM_showMessageText($MESSAGE[29], $MESSAGE[30]), array('pagetitle' => $MESSAGE[30]) @@ -52,3 +49,508 @@ exit; } +/** + * Shows the URL routing editor + * This will show a URL routing edit form. + * + * @param int $rid ID of URL routing rule to edit + * @return string HTML for URL routing editor + */ +function getRouteEditor($rid = 0) +{ + global $_CONF, $_TABLES, $LANG01, $LANG21, $LANG_ACCESS, $LANG_ROUTER, + $LANG_ADMIN, $MESSAGE, $_FINPUT, $securityToken; + + $retval = ''; + + $A = array( + 'rid' => $rid, + 'method' => Router::HTTP_REQUEST_GET, + 'rule' => '', + 'route' => '', + 'priority' => Router::DEFAULT_PRIORITY, + ); + $rid = intval($rid, 10); + + if ($rid > 0) { + if (DB_count($_TABLES['routes'], 'rid', $rid) == 1) { + $sql = "SELECT * FROM {$_TABLES['routes']} WHERE rid =" . DB_escapeString($rid); + $result = DB_query($sql); + $A = DB_fetchArray($result); + } else { + // Non-existent route + $rid = 0; + $A['rid'] = $rid; + } + } + + $T = COM_newTemplate($_CONF['path_layout'] . 'admin/router'); + $T->set_file('editor', 'routereditor.thtml'); + $routerStart = COM_startBlock($LANG_ROUTER[10], '', COM_getBlockTemplate('_admin_block', 'header')) + . LB . SEC_getTokenExpiryNotice($securityToken); + $T->set_var('start_router_editor', $routerStart); + + if ($rid > 0) { + $deleteButton = ''; + $jsConfirm = ' onclick="return confirm(\'' . $MESSAGE[76] . '\');"'; + $T->set_var(array( + 'delete_option' => sprintf($deleteButton, $jsConfirm), + 'delete_option_no_confirmation' => sprintf($deleteButton, ''), + )); + } + + $T->set_var(array( + 'rid' => $A['rid'], + 'method' => $A['method'], + 'rule' => $A['rule'], + 'route' => $A['route'], + 'priority' => $A['priority'], + 'gltoken_name' => CSRF_TOKEN, + 'gltoken' => $securityToken, + )); + $T->set_var(array( + 'lang_router_rid' => $LANG_ROUTER[3], + 'lang_router_method' => $LANG_ROUTER[4], + 'lang_router_rule' => $LANG_ROUTER[5], + 'lang_router_route' => $LANG_ROUTER[6], + 'lang_router_priority' => $LANG_ROUTER[7], + 'lang_router_notice' => $LANG_ROUTER[19], + 'lang_save' => $LANG_ADMIN['save'], + 'lang_cancel' => $LANG_ADMIN['cancel'], + )); + + $T->set_var( + 'end_block', + COM_endBlock(COM_getBlockTemplate('_admin_block', 'footer')) + ); + $T->parse('output', 'editor'); + $retval .= $T->finish($T->get_var('output')); + + return $retval; +} + +/** + * Field function + * + * @param string $fieldName + * @param string $fieldValue + * @param array $A + * @param array $iconArray + * @param string $extra + * @return string + * @throws InvalidArgumentException + */ +function ADMIN_getListFieldRoutes($fieldName, $fieldValue, $A, $iconArray, $extra = '') +{ + global $_CONF, $LANG_ROUTER, $_IMAGE_TYPE, $securityToken; + + switch ($fieldName) { + case 'rid': + $fieldValue = '' + . $iconArray['edit'] . ''; + break; + + case 'method': + switch (intval($fieldValue, 10)) { + case Router::HTTP_REQUEST_GET: + $fieldValue = 'GET'; + break; + + case Router::HTTP_REQUEST_POST: + $fieldValue = 'POST'; + break; + + case Router::HTTP_REQUEST_PUT: + $fieldValue = 'PUT'; + break; + + case Router::HTTP_REQUEST_DELETE: + $fieldValue = 'DELETE'; + break; + + case Router::HTTP_REQUEST_HEAD: + $fieldValue = 'HEAD'; + break; + + default: + throw new InvalidArgumentException(__FUNCTION__ . ': unknown method "' . $fieldValue . '" was given'); + } + + break; + + case 'rule': + break; + + case 'route': + break; + + case 'priority': + $rid = $A['rid']; + $baseUrl = $_CONF['site_admin_url'] . '/router.php?mode=move&rid=' . $rid . '&' + . CSRF_TOKEN . '=' . $securityToken; + $fieldValue = '' + . '' . $LANG_ROUTER[8] . '' + . $fieldValue + . '' + . '' . $LANG_ROUTER[9] . ''; + break; + + default: + throw new InvalidArgumentException(__FUNCTION__ . ': unknown field name "' . $fieldName . '" was given'); + } + + return $fieldValue; +} + +/** + * Display a list of routes + * + * @return string HTML for the list + */ +function listRoutes() +{ + global $_CONF, $_TABLES, $LANG_ADMIN, $LANG21, $LANG_ROUTER, $_IMAGE_TYPE, $securityToken; + + require_once $_CONF['path_system'] . 'lib-admin.php'; + + // Writing the menu on top + $menu_arr = array( + array( + 'url' => $_CONF['site_admin_url'] . '/router.php?mode=edit&rid=0', + 'text' => $LANG_ADMIN['create_new'], + ), + array( + 'url' => $_CONF['site_admin_url'], + 'text' => $LANG_ADMIN['admin_home'], + ), + ); + + $notice = $LANG_ROUTER[11]; + + if (!isset($_CONF['url_rewrite']) || empty($_CONF['url_rewrite'])) { + $notice .= ' ' . $LANG_ROUTER[18]; + } + + $retval = COM_startBlock($LANG_ROUTER[2], '', COM_getBlockTemplate('_admin_block', 'header')) + . ADMIN_createMenu( + $menu_arr, + $notice, + $_CONF['layout_url'] . '/images/icons/router.' . $_IMAGE_TYPE + ); + + $headerArray = array( # display 'text' and use table field 'field' + array( + 'text' => $LANG_ADMIN['edit'], + 'field' => 'rid', + 'sort' => false, + ), + array( + 'text' => $LANG_ROUTER[4], + 'field' => 'method', + 'sort' => true, + ), + array( + 'text' => $LANG_ROUTER[5], + 'field' => 'rule', + 'sort' => true, + ), + array( + 'text' => $LANG_ROUTER[6], + 'field' => 'route', + 'sort' => true, + ), + array( + 'text' => $LANG_ROUTER[7], + 'field' => 'priority', + 'sort' => true, + ), + ); + + $defaultSortArray = array( + 'field' => 'priority', + 'direction' => 'asc', + ); + + $textArray = array( + 'has_extras' => false, + 'title' => $LANG_ROUTER[1], + 'form_url' => $_CONF['site_admin_url'] . '/router.php', + ); + + $queryArray = array( + 'table' => 'routes', + 'sql' => "SELECT * FROM {$_TABLES['routes']} WHERE (1 = 1) ", + 'query_fields' => array('rule', 'route', 'priority'), + 'default_filter' => COM_getPermSql('AND'), + ); + + $retval .= ADMIN_list( + 'routes', 'ADMIN_getListFieldRoutes', $headerArray, $textArray, + $queryArray, $defaultSortArray, '', $securityToken, '' + ); + + $retval .= COM_endBlock(COM_getBlockTemplate('_admin_block', 'footer')); + + return $retval; +} + +/** + * Save a route into database + * + * @param int $rid + * @param int $method + * @param string $rule + * @param string $route + * @param int $priority + * @return string + */ +function saveRoute($rid, $method, $rule, $route, $priority) +{ + global $_CONF, $_TABLES, $MESSAGE, $LANG_ROUTER; + + $messageText = ''; + + $rid = intval($rid, 10); + $method = intval($method, 10); + $rule = trim($rule); + $route = trim($route); + $priority = intval($priority, 10); + + if (($method < Router::HTTP_REQUEST_GET) || ($method > Router::HTTP_REQUEST_HEAD)) { + $messageText = $LANG_ROUTER[12]; + } elseif ($rule === '') { + $messageText = $LANG_ROUTER[13]; + } elseif ($route === '') { + $messageText = $LANG_ROUTER[14]; + } elseif (substr_count($rule, '@') !== substr_count($route, '@')) { + $messageText = $LANG_ROUTER[15]; + } elseif (stripos($route, '/index.php') === 0) { + $messageText = $LANG_ROUTER[16]; + } + + // If rule doesn't begin with a slash, then fix it silently + if (strpos($rule, '/') !== 0) { + $rule = '/' . $rule; + } + + // If route doesn't begin with a slash, then fix it silently + if (strpos($route, '/') !== 0) { + $route = '/' . $route; + } + + // Replace & with & silently + $rule = str_ireplace('&', '&', $rule); + $route = str_ireplace('&', '&', $route); + + // Check if placeholders are the same + $numPlaceHoldersInRule = preg_match_all(Router::PATTERN_PLACEHOLDER, $rule, $matchesRule, PREG_SET_ORDER); + $numPlaceHoldersInRoute = preg_match_all(Router::PATTERN_PLACEHOLDER, $route, $matchesRoute, PREG_SET_ORDER); + + if ($numPlaceHoldersInRule === $numPlaceHoldersInRoute) { + if ($numPlaceHoldersInRule > 0) { + array_shift($matchesRule); + array_shift($matchesRoute); + + foreach ($matchesRule as $r) { + if (!in_array($r, $matchesRoute)) { + $messageText = $LANG_ROUTER[15]; + break; + } + } + } + } else { + $messageText = $LANG_ROUTER[15]; + } + + // If priority is out of range, then fix it silently + if (($priority < 1) || ($priority > 65535)) { + $priority = Router::DEFAULT_PRIORITY; + } + + if ($messageText !== '') { + $content = COM_showMessageText($messageText, $MESSAGE[122]) . getRouteEditor($rid); + $retval = COM_createHTMLDocument( + $content, + array( + 'pagetitle' => $MESSAGE[122], + ) + ); + + return $retval; + } + + // Save data into database + $rid = DB_escapeString($rid); + $method = DB_escapeString($method); + $rule = DB_escapeString($rule); + $route = DB_escapeString($route); + $priority = DB_escapeString($priority); + + $count = intval(DB_count($_TABLES['routes'], 'rid', $rid), 10); + + if ($count === 0) { + $sql = "INSERT INTO {$_TABLES['routes']} (rid, method, rule, route, priority) " + . "VALUES (NULL, {$method}, '{$rule}', '{$route}', {$priority})"; + } else { + $sql = "UPDATE {$_TABLES['routes']} " + . "SET method = {$method}, rule = '{$rule}', route = '{$route}', priority = {$priority} " + . "WHERE rid = {$rid} "; + } + + for ($i = 0; $i < 5; $i++) { + DB_query($sql); + + if (!DB_error()) { + reorderRoutes(); + + return COM_refresh($_CONF['site_admin_url'] . '/router.php?msg=121'); + } + + // Retry + } + + $content = COM_showMessageText($LANG_ROUTER[17], DB_error()) . getRouteEditor($rid); + $retval = COM_createHTMLDocument( + $content, + array( + 'pagetitle' => $MESSAGE[122], + ) + ); + + return $retval; +} + +/** + * Re-orders all routes in increments of 10 + */ +function reorderRoutes() +{ + global $_TABLES; + + $sql = "SELECT rid FROM {$_TABLES['routes']} ORDER BY priority"; + $result = DB_query($sql); + $rids = array(); + + while (($A = DB_fetchArray($result, false)) !== false) { + $rids[] = intval($A['rid'], 10); + } + + $priority = 100; + $step = 10; + + foreach ($rids as $rid) { + $sql = "UPDATE {$_TABLES['routes']} SET priority = " . DB_escapeString($priority) + . " WHERE rid = " . DB_escapeString($rid); + DB_query($sql); + $priority += $step; + } +} + +/** + * Move a route UP or Down + * + * @param int $rid + */ +function moveRoute($rid) +{ + global $_CONF, $_TABLES, $LANG_ROUTER, $_FINPUT; + + $rid = intval($rid, 10); + $direction = $_FINPUT->get('dir', ''); + + // if the router id exists + if (DB_count($_TABLES['routes'], 'rid', $rid)) { + $rid = DB_escapeString($rid); + + if ($direction === 'up') { + $sql = "UPDATE {$_TABLES['routes']} SET priority = priority - 11 WHERE rid = " . $rid; + DB_query($sql); + reorderRoutes(); + } elseif ($direction === 'down') { + $sql = "UPDATE {$_TABLES['routes']} SET priority = priority + 11 WHERE rid = " . $rid; + DB_query($sql); + reorderRoutes(); + } + } else { + COM_errorLog("block admin error: Attempt to move an non-existing route id: {$rid}"); + } +} + +/** + * Delete a route + * + * @param int $rid id of block to delete + * @return string HTML redirect or error message + */ +function deleteRoute($rid) +{ + global $_CONF, $_TABLES; + + $rid = intval($rid, 10); + DB_delete($_TABLES['routes'], 'rid', $rid); + reorderRoutes(); + + return COM_refresh($_CONF['site_admin_url'] . '/router.php?msg=123'); +} + +// MAIN +$display = ''; + +$mode = $_FINPUT->get('mode', $_FINPUT->post('mode', '')); +$rid = $_FINPUT->get('rid', $_FINPUT->post('rid', 0)); +$rid = intval($rid, 10); +$securityToken = SEC_createToken(); + +switch ($mode) { + case $LANG_ADMIN['delete']: + if ($rid === 0) { + COM_errorLog('Attempted to delete route, rid empty or null, value =' . $rid); + $display = COM_refresh($_CONF['site_admin_url'] . '/router.php'); + } elseif (SEC_checkToken()) { + $display = deleteRoute($rid); + } else { + COM_accessLog("User {$_USER['username']} tried to illegally delete route {$rid} and failed CSRF checks."); + $display = COM_refresh($_CONF['site_admin_url'] . '/index.php'); + } + + echo $display; + die(); + break; + + case $LANG_ADMIN['save']: + if (!SEC_checkToken()) { + COM_accessLog("User {$_USER['username']} tried to illegally save route {$rid} and failed CSRF checks."); + echo COM_refresh($_CONF['site_admin_url'] . '/index.php'); + die(); + } + + $method = $_FINPUT->post('method', ''); + $rule = $_INPUT->post('rule', ''); + $route = $_INPUT->post('route', ''); + $priority = $_FINPUT->post('priority', Router::DEFAULT_PRIORITY); + $display = saveRoute($rid, $method, $rule, $route, $priority); + break; + + case 'edit': + $content = getRouteEditor($rid); + $display = COM_createHTMLDocument($content, array('pagetitle' => $LANG_ROUTER[2])); + break; + + case 'move': + if (SEC_checkToken()) { + moveRoute($rid); + } + + $content = listRoutes(); + $display = COM_createHTMLDocument($content, array('pagetitle' => $LANG_ROUTER[2])); + break; + + default: // 'cancel' or no mode at all + $content = COM_showMessageFromParameter() . listRoutes(); + $display = COM_createHTMLDocument($content, array('pagetitle' => $LANG_ROUTER[2])); +} + +COM_output($display); diff --git a/public_html/layout/modern_curve/admin/router/index.html b/public_html/layout/modern_curve/admin/router/index.html new file mode 100644 index 000000000..e69de29bb diff --git a/public_html/layout/modern_curve/admin/router/routereditor.thtml b/public_html/layout/modern_curve/admin/router/routereditor.thtml new file mode 100644 index 000000000..59f0fe6b9 --- /dev/null +++ b/public_html/layout/modern_curve/admin/router/routereditor.thtml @@ -0,0 +1,48 @@ +{# begin {templatelocation} #} + +{start_router_editor} +
+
+
+
+
{rid}
+ +
+
+ +
+ +
+
+ +
+
+ +
+
+
+
+ +
+ {lang_router_notice} +
+ +
    +
  • +
  • +
  • + + + {delete_option} +
  • +
+
+{end_block} + +{# end {templatelocation} #} diff --git a/public_html/layout/modern_curve/images/icons/router.png b/public_html/layout/modern_curve/images/icons/router.png new file mode 100644 index 0000000000000000000000000000000000000000..d2647eee5127f7aebb368dbb1768897852b47dc9 GIT binary patch literal 2165 zcmV-*2#WWKP)Yx(&D1tptWIK#fHO%W zf(Bn0jR7t4SQeJ&t}M&GzS}=`B_;t{r}Z~;=YHQe=bZby=iKl94v)u=2>@gOJmVSh zkDJ88MNj`cdSXm;OLL0_fD?c;nj!a?iSRO+VrsZP?C5hVa!WNDO$>Y)`VIvDta|8q zG$|iJMx>w2MvbjxP-Thg8ef;`lXbg`v#Lkw>#Rvk40H9PU*9p9UtgN}> z(o2k(MW8y6Pf1M`SN&wQEG#T6#nRomQsBi}yWLJppIuU#oSal#R9G}|>5`>FNJy}t zRLDuLP(Z0vLa9`sufG?PB*Eo!!EPT!)|`35^y$+l{_EbIb-|$_4K2+r%|b~@iT>)< z%aH(qh(H{N2Rk8uW~{aWbXhvlBnU`MoF=~c#{P)>{Jf9TW z--ZZ?GsMGWYR7~C6?EDly!+m}?v{Hkt@#^X{(E?M_(uRfBgWX24^Mwsbg`~BVXVVy zwb^u?oo3_4jfFy(K8#MCItiQYJ_-w7fyL4Z#(8hKXn=mhTDQ539#O)X9qKzO>Agrh;80dMK{T zu*#~lo^`)m+tbj{@H%7cS;m-rtc{Ng##n;cY})g}4_CN078WqO-Ok$DTAAHG$POJT zcOO1-=mW;sYQ|XLLp~Y1%XZbiuyO_4^SiP-hr_XzF*a?q_~Tt--vANaVT?5hqBt-o zD=Tz(co-Zv9acq^K&MnHgVh=hyj}@B&!e}u2a-oJ00)LuYJYI0zDdfH0>Lh#MjLJ(xK zsbTt%qw^MIm1;DK82AMCJ9yxvf={^QqYMRrz=VW^_*v;Q-`iZYIYK6iBuNZ{D59d` zI0_4j_@<`Dr<6(sb#+;qJRT1RKm?$OiH@4FfB%7uq!~$xt!<`df*@Y*>FK!-pV*C* zivXxHGt;v*TJ5OnOGJu5wR)05A)lL{_p-uZh~xVEd!f?>;q*tR(bZ!{R^}WSo=8AN zMTPVI_y1;hx!q?Sj-eI+P{`%K*thSGYNOGJ-rioeX;YE2wWXz`rl#%)fFAf;3j&~i zX33&0PcO(W%FfP)BuPHaflm_zo`+hkfmW-9)9HZSZiiB-g3V^bp1ozPu&|K&t$pBm z4&nMJsFW&nn$57=hekx)U@)N7)C`p}0Q%5y9C+)1v#PSH?$*sO^GqgFGXNXy*s&v_ zrL}o`K|$f_sK`h-ox|Wb9>fti;s639uLQT-4I&^QAP_d2jcqU9HrQ?HZdQ{J z8;?p6fcPhj2-bx_CW=0PNRSvCiTCvwITl#9Y*}1!aj|yfcdD%V7&!}n0EtN;0yy%O zMv@q;R;%}mFK&42>T0EF(-JT2E&Kg(jYe}60Nb@|x8>ZqsvA3Z?#%H@o~WFhMTm}y z=GAKTgI)r$F|oMUeisu|0SFI`#KE@@dcJDB-CX|m;bT!zQD=zg7BR-YS6N+IaOKKn z<7kIfCdxcooz9vN7jMbP$h^*R+ywyFA6!t3F#<3NK)T6fN~o@`j_c~`)L5<7005}1 zyO6nW-ydL1Frufo2ivx7b zX#jvo02=sy-`LX9-9KIa-12Ob$t0DRm-lTfDEv!GO3FC^XNhQ}85jIJ`*OSm{#y{y zAb`e)h>_JcFk!=nJR~QlTs(Z}&?%*I!Usfj>)XDMDteC!YkXY1Ic-+@JGr^Jp8z;X rM7GfkX|#|3FZjzo)&F{PJmLE{mE3gB8B_p?00000NkvXXu0mjfiPZ`3 literal 0 HcmV?d00001 diff --git a/public_html/lib-common.php b/public_html/lib-common.php index ce5f6613e..5c7fe2138 100755 --- a/public_html/lib-common.php +++ b/public_html/lib-common.php @@ -3371,7 +3371,7 @@ function COM_userMenu( $help='', $title='', $position='' ) function COM_commandControl($adminMenu = false, $help = '', $title = '', $position = '') { global $_CONF, $_CONF_FT, $_TABLES, $LANG01, $LANG29, $LANG_LOGVIEW, - $LANG_ENVCHECK, $LANG_ADMIN, $_IMAGE_TYPE, $_DB_dbms, $config;; + $LANG_ENVCHECK, $LANG_ADMIN, $_IMAGE_TYPE, $LANG_ROUTER, $_DB_dbms, $config;; $retval = ''; @@ -3645,6 +3645,18 @@ function COM_commandControl($adminMenu = false, $help = '', $title = '', $positi } } + $routeCount = '0'; + if ($adminMenu && SEC_inGroup('Root')) { + // Find num of URL routes + $sql = "SELECT COUNT(rid) AS cnt FROM {$_TABLES['routes']}"; + $result = DB_query($sql); + + if (!DB_error()) { + $temp = DB_fetchArray($result, false); + $routeCount = COM_numberFormat($temp['cnt']); + } + } + $cc_arr = array( array('condition' => SEC_hasRights($_CONF_FT, 'OR'), 'url' => $_CONF['site_admin_url'] . '/configuration.php', @@ -3690,6 +3702,11 @@ function COM_commandControl($adminMenu = false, $help = '', $title = '', $positi 'num' => '', 'image' => $_CONF['layout_url'] . '/images/icons/filemanager.' . $_IMAGE_TYPE, 'target' => '_blank'), + array('condition' => SEC_inGroup('Root'), + 'url' => $_CONF['site_admin_url'] . '/router.php', + 'lang' => $LANG_ROUTER[1], + 'num' => $routeCount, + 'image' => $_CONF['layout_url'] . '/images/icons/router.' . $_IMAGE_TYPE), array('condition' => true, 'url' =>$_CONF['site_url'] . '/users.php?mode=logout', 'lang' => $LANG01[35], diff --git a/system/classes/router.class.php b/system/classes/router.class.php index 7d29d0ab8..67c06a47e 100644 --- a/system/classes/router.class.php +++ b/system/classes/router.class.php @@ -18,12 +18,15 @@ class Router const ROUTING_WITHOUT_INDEX_PHP = 2; // Placeholder pattern - const PATTERN_PLACEHOLDER = '|(@[a-zA-Z][0-9a-zA-Z_-]*)|'; + const PATTERN_PLACEHOLDER = '|(@[a-zA-Z][0-9a-zA-Z_]*)|'; + + // Default priority + const DEFAULT_PRIORITY = 100; /** * @var bool */ - private static $debug = false; + private static $debug = true; /** * Set debug mode @@ -42,7 +45,7 @@ public static function setDebug($switch) */ public static function dispatch() { - global $_CONF, $_TABLES; + global $_CONF, $_TABLES, $LANG_ROUTER; // URL rewrite is disabled if (!$_CONF['url_rewrite']) { @@ -59,8 +62,6 @@ public static function dispatch() // URL routing is disabled if ($routingType === self::ROUTING_DISABLED) { return false; - } elseif (self::$debug) { - COM_errorLog(__METHOD__ . ': routing type = ' . $routingType); } // $_SERVER['PATH_INFO'] is unavailable @@ -116,58 +117,60 @@ public static function dispatch() $rule = $A['rule']; $route = $A['route']; - if (self::$debug) { - COM_errorLog(__METHOD__ . ': rule = ' . $rule); - COM_errorLog(__METHOD__ . ': route = ' . $route); - } - // Try simple comparison without placeholders if (strcasecmp($rule, $pathInfo) === 0) { - if ($routingType === self::ROUTING_WITH_INDEX_PHP) { - $route = $_CONF['site_url'] . $route; - } + $route = $_CONF['site_url'] . $route; if (self::$debug) { - COM_errorLog(__METHOD__ . ': matched with simple comparison rule "' . $A['rule'] . '"'); + COM_errorLog(__METHOD__ . ': "' . $pathInfo . '"matched with simple comparison rule "' . $A['rule'] . '", converted into "' . $route . '"'); } header('Location: ' . $route); + + COM_errorLog(__METHOD__ . ': somehow could not redirect'); + return false; } // Try comparison with placeholders if (preg_match_all(self::PATTERN_PLACEHOLDER, $rule, $matches, PREG_SET_ORDER)) { + // Escape a period and a question mark so that they can safely be used in a regular expression + $rule = str_replace(array('.', '?'), array('\.', '\?'), $rule); $placeHolders = array(); // Replace placeholders in a rule with ones for regular expressions foreach ($matches as $match) { $placeHolders[] = $match[1]; - $rule = str_ireplace($match[1], '([^/]+)', $rule); + $rule = str_replace($match[1], '([^/&=?#]+)', $rule); } - $rule = '|' . $rule . '|i'; + $rule = '|\A' . $rule . '\z|i'; - if (!preg_match($rule, $pathInfo, $matches)) { + if (!preg_match($rule, $pathInfo, $values)) { continue; } - array_shift($matches); - - if (count($placeHolders) !== count($matches)) { - continue; - } + array_shift($values); - foreach ($matches as $match) { - $match = urlencode($match); + foreach ($values as $value) { + $value = urlencode($value); $placeHolder = array_shift($placeHolders); - $route = str_ireplace($placeHolder, $match, $route); + $route = str_replace($placeHolder, $value, $route); } - if ($routingType === self::ROUTING_WITH_INDEX_PHP) { - $route = $_CONF['site_url'] . $route; + if ((strpos($route, '@') !== false) && self::$debug) { + COM_errorLog( + sprintf( + '%s: %s. Rule (rid = %d) = %s, Route = %s', + __METHOD__, @$LANG_ROUTER[15], $A['rid'], $A['rule'], $A['route'] + ) + ); + continue; } + $route = $_CONF['site_url'] . $route; + if (self::$debug) { - COM_errorLog(__METHOD__ . ': matched with regular expression rule "' . $A['rule'] . '"'); + COM_errorLog(__METHOD__ . ': "' . $pathInfo . '" matched with regular expression rule "' . $A['rule'] . '", converted into "' . $route . '"'); } header('Location: ' . $route); @@ -179,6 +182,7 @@ public static function dispatch() /** * Convert a URL + * * e.g. [SITE_URL]/article.php?story=welcome -> [SITE_URL]/index.php/article/welcome or [SITE_URL]/article/welcome * * @param string $url @@ -187,29 +191,29 @@ public static function dispatch() */ public static function convertUrl($url, $requestMethod = self::HTTP_REQUEST_GET) { - global $_CONF, $_TABLES; + global $_CONF, $_TABLES, $LANG_ROUTER; + + $originalUrl = $url; // URL rewrite is disabled if (!$_CONF['url_rewrite']) { - return $url; + return $originalUrl; } // URL routing is not supported if (!isset($_CONF['url_routing'])) { - return $url; + return $originalUrl; } $routingType = intval($_CONF['url_routing'], 10); // URL routing is disabled if ($routingType === self::ROUTING_DISABLED) { - return $url; - } elseif (self::$debug) { - COM_errorLog(__METHOD__ . ': routing type = ' . $routingType); + return $originalUrl; } // Strip $url of $_CONF['site_url'] - $url = str_ireplace($_CONF['site_url'], '', $url); + $url = str_replace($_CONF['site_url'], '', $url); // Check for $requestMethod $requestMethod = intval($requestMethod, 10); @@ -217,7 +221,7 @@ public static function convertUrl($url, $requestMethod = self::HTTP_REQUEST_GET) if (($requestMethod < self::HTTP_REQUEST_GET) || ($requestMethod > self::HTTP_REQUEST_HEAD)) { COM_errorLog(__METHOD__ . ': unknown request method "' . $requestMethod . '" was given'); - return $url; + return $originalUrl; } // Get routing rules and routes from database @@ -227,21 +231,21 @@ public static function convertUrl($url, $requestMethod = self::HTTP_REQUEST_GET) if (DB_error()) { COM_errorLog(__METHOD__ . ': ' . DB_error()); - return $url; + return $originalUrl; } + $url = str_replace('&', '&', $url); + $path = $url; + while (($A = DB_fetchArray($result, false)) !== false) { + $url = $path; $rule = $A['rule']; $route = $A['route']; - if (self::$debug) { - COM_errorLog(__METHOD__ . ': rule = ' . $rule); - COM_errorLog(__METHOD__ . ': route = ' . $route); - } - // Try simple comparison without placeholders if (strcasecmp($route, $url) === 0) { $retval = $rule; + $retval = str_replace('&', '&', $retval); if ($routingType === self::ROUTING_WITH_INDEX_PHP) { $retval = '/index.php' . $retval; @@ -250,7 +254,7 @@ public static function convertUrl($url, $requestMethod = self::HTTP_REQUEST_GET) $retval = $_CONF['site_url'] . $retval; if (self::$debug) { - COM_errorLog(__METHOD__ . ': matched with simple comparison route "' . $A['route'] . '"'); + COM_errorLog(__METHOD__ . ': "' . $originalUrl . '" matched with simple comparison route "' . $A['route'] . '"'); } return $retval; @@ -263,30 +267,37 @@ public static function convertUrl($url, $requestMethod = self::HTTP_REQUEST_GET) // Replace placeholders in a route with ones for regular expressions foreach ($matches as $match) { $placeHolders[] = $match[1]; - $route = str_ireplace($match[1], '([^/]+)', $route); + $route = str_replace($match[1], '([^/&=?#]+)', $route); } // Escape a period and a question mark so that they can safely be used in a regular expression $route = str_replace(array('.', '?'), array('\.', '\?'), $route); - $route = '|' . $route . '|'; + $route = '|\A' . $route . '\z|i'; - if (!preg_match($route, $url, $matches)) { + if (!preg_match($route, $url, $values)) { continue; } - array_shift($matches); + array_shift($values); - if (count($placeHolders) !== count($matches)) { - continue; + foreach ($values as $value) { + $value = urlencode($value); + $placeHolder = array_shift($placeHolders); + $rule = str_replace($placeHolder, $value, $rule); } - foreach ($matches as $match) { - $match = urlencode($match); - $placeHolder = array_shift($placeHolders); - $rule = str_ireplace($placeHolder, $match, $rule); + if ((strpos($rule, '@') !== false) && self::$debug) { + COM_errorLog( + sprintf( + '%s: %s. Rule (rid = %d) = %s, Route = %s', + __METHOD__, @$LANG_ROUTER[15], $A['rid'], $A['rule'], $A['route'] + ) + ); + continue; } $retval = $rule; + $retval = str_replace('&', '&', $retval); if ($routingType === self::ROUTING_WITH_INDEX_PHP) { $retval = '/index.php' . $retval; @@ -295,13 +306,13 @@ public static function convertUrl($url, $requestMethod = self::HTTP_REQUEST_GET) $retval = $_CONF['site_url'] . $retval; if (self::$debug) { - COM_errorLog(__METHOD__ . ': matched with regular expression rule "' . $A['route'] . '"'); + COM_errorLog(__METHOD__ . ': "' . $originalUrl . '" matched with regular expression rule "' . $A['route'] . '", converted into "' . $retval . '"'); } return $retval; } } - return $url; + return $originalUrl; } } From eda050eec30f2673a1422d817e6906d2534d7b9f Mon Sep 17 00:00:00 2001 From: Kenji ITO Date: Fri, 22 Jan 2016 18:31:17 +0900 Subject: [PATCH 07/81] Modified initial database entries --- sql/mssql_tableanddata.php | 3 +-- sql/mysql_tableanddata.php | 3 +-- sql/pgsql_tableanddata.php | 3 +-- sql/updates/mssql_2.1.0_to_2.1.2.php | 3 +-- sql/updates/mysql_2.1.0_to_2.1.2.php | 3 +-- sql/updates/pgsql_2.1.0_to_2.1.2.php | 3 +-- system/classes/router.class.php | 2 +- 7 files changed, 7 insertions(+), 13 deletions(-) diff --git a/sql/mssql_tableanddata.php b/sql/mssql_tableanddata.php index de0dc6056..55784377e 100644 --- a/sql/mssql_tableanddata.php +++ b/sql/mssql_tableanddata.php @@ -1537,10 +1537,9 @@ $_SQL[] = "INSERT INTO {$_TABLES['postmodes']} (code, name) VALUES ('plaintext','Plain Old Text')"; $_SQL[] = "INSERT INTO {$_TABLES['postmodes']} (code, name) VALUES ('html','HTML Formatted')"; -$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/stats', '/stats.php')"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/article/@sid', '/article.php?story=@sid')"; $_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/topic/@topic', '/index.php?topic=@topic')"; $_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/page/@page', '/staticpages/index.php?page=@page')"; -$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/article/@sid', '/article.php?story=@sid')"; $_SQL[] = "INSERT INTO {$_TABLES['sortcodes']} (code, name) VALUES ('ASC','Oldest First')"; $_SQL[] = "INSERT INTO {$_TABLES['sortcodes']} (code, name) VALUES ('DESC','Newest First')"; diff --git a/sql/mysql_tableanddata.php b/sql/mysql_tableanddata.php index b2c56caf8..649ec00ae 100644 --- a/sql/mysql_tableanddata.php +++ b/sql/mysql_tableanddata.php @@ -808,10 +808,9 @@ $_DATA[] = "INSERT INTO {$_TABLES['postmodes']} (code, name) VALUES ('plaintext','Plain Old Text') "; $_DATA[] = "INSERT INTO {$_TABLES['postmodes']} (code, name) VALUES ('html','HTML Formatted') "; -$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/stats', '/stats.php')"; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/article/@sid', '/article.php?story=@sid')"; $_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/topic/@topic', '/index.php?topic=@topic')"; $_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/page/@page', '/staticpages/index.php?page=@page')"; -$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/article/@sid', '/article.php?story=@sid')"; $_DATA[] = "INSERT INTO {$_TABLES['sortcodes']} (code, name) VALUES ('ASC','Oldest First') "; $_DATA[] = "INSERT INTO {$_TABLES['sortcodes']} (code, name) VALUES ('DESC','Newest First') "; diff --git a/sql/pgsql_tableanddata.php b/sql/pgsql_tableanddata.php index 2845ca5ab..0f7901e3e 100644 --- a/sql/pgsql_tableanddata.php +++ b/sql/pgsql_tableanddata.php @@ -826,10 +826,9 @@ $_DATA[] = "INSERT INTO {$_TABLES['postmodes']} (code, name) VALUES ('plaintext','Plain Old Text') "; $_DATA[] = "INSERT INTO {$_TABLES['postmodes']} (code, name) VALUES ('html','HTML Formatted') "; -$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/stats', '/stats.php')"; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/article/@sid', '/article.php?story=@sid')"; $_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/topic/@topic', '/index.php?topic=@topic')"; $_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/page/@page', '/staticpages/index.php?page=@page')"; -$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/article/@sid', '/article.php?story=@sid')"; $_DATA[] = "INSERT INTO {$_TABLES['sortcodes']} (code, name) VALUES ('ASC','Oldest First') "; $_DATA[] = "INSERT INTO {$_TABLES['sortcodes']} (code, name) VALUES ('DESC','Newest First') "; diff --git a/sql/updates/mssql_2.1.0_to_2.1.2.php b/sql/updates/mssql_2.1.0_to_2.1.2.php index 3dc2901d4..e67149593 100644 --- a/sql/updates/mssql_2.1.0_to_2.1.2.php +++ b/sql/updates/mssql_2.1.0_to_2.1.2.php @@ -18,10 +18,9 @@ "; // Add sample routes to the table -$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/stats', '/stats.php')"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/article/@sid', '/article.php?story=@sid')"; $_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/topic/@topic', '/index.php?topic=@topic')"; $_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/page/@page', '/staticpages/index.php?page=@page')"; -$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/article/@sid', '/article.php?story=@sid')"; /** * Add new config options diff --git a/sql/updates/mysql_2.1.0_to_2.1.2.php b/sql/updates/mysql_2.1.0_to_2.1.2.php index eb08b3ee3..ac18d1125 100644 --- a/sql/updates/mysql_2.1.0_to_2.1.2.php +++ b/sql/updates/mysql_2.1.0_to_2.1.2.php @@ -11,10 +11,9 @@ )"; // Add sample routes to the table -$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/stats', '/stats.php')"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/article/@sid', '/article.php?story=@sid')"; $_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/topic/@topic', '/index.php?topic=@topic')"; $_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/page/@page', '/staticpages/index.php?page=@page')"; -$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/article/@sid', '/article.php?story=@sid')"; /** * Add new config options diff --git a/sql/updates/pgsql_2.1.0_to_2.1.2.php b/sql/updates/pgsql_2.1.0_to_2.1.2.php index 06402dbbb..436b4d4c4 100644 --- a/sql/updates/pgsql_2.1.0_to_2.1.2.php +++ b/sql/updates/pgsql_2.1.0_to_2.1.2.php @@ -11,10 +11,9 @@ )"; // Add sample routes to the table -$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/stats', '/stats.php')"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/article/@sid', '/article.php?story=@sid')"; $_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/topic/@topic', '/index.php?topic=@topic')"; $_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/page/@page', '/staticpages/index.php?page=@page')"; -$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/article/@sid', '/article.php?story=@sid')"; /** * Add new config options diff --git a/system/classes/router.class.php b/system/classes/router.class.php index 67c06a47e..5016a5707 100644 --- a/system/classes/router.class.php +++ b/system/classes/router.class.php @@ -26,7 +26,7 @@ class Router /** * @var bool */ - private static $debug = true; + private static $debug = false; /** * Set debug mode From 10e19a355cd9dfaf2c9ec1bc04327a0a3da70ffd Mon Sep 17 00:00:00 2001 From: Kenji ITO Date: Sat, 23 Jan 2016 13:26:57 +0900 Subject: [PATCH 08/81] Added documents --- public_html/admin/router.php | 26 +++--- public_html/docs/english/config.html | 110 ++++++++++++++++++++++++++ public_html/docs/japanese/config.html | 109 +++++++++++++++++++++++++ sql/mssql_tableanddata.php | 10 ++- sql/mysql_tableanddata.php | 10 ++- sql/pgsql_tableanddata.php | 10 ++- sql/updates/mssql_2.1.0_to_2.1.2.php | 10 ++- sql/updates/mysql_2.1.0_to_2.1.2.php | 10 ++- sql/updates/pgsql_2.1.0_to_2.1.2.php | 10 ++- 9 files changed, 278 insertions(+), 27 deletions(-) diff --git a/public_html/admin/router.php b/public_html/admin/router.php index b8786e9c1..349ce1541 100644 --- a/public_html/admin/router.php +++ b/public_html/admin/router.php @@ -58,8 +58,7 @@ */ function getRouteEditor($rid = 0) { - global $_CONF, $_TABLES, $LANG01, $LANG21, $LANG_ACCESS, $LANG_ROUTER, - $LANG_ADMIN, $MESSAGE, $_FINPUT, $securityToken; + global $_CONF, $_TABLES, $LANG_ROUTER, $LANG_ADMIN, $MESSAGE, $securityToken; $retval = ''; @@ -212,7 +211,7 @@ function ADMIN_getListFieldRoutes($fieldName, $fieldValue, $A, $iconArray, $extr */ function listRoutes() { - global $_CONF, $_TABLES, $LANG_ADMIN, $LANG21, $LANG_ROUTER, $_IMAGE_TYPE, $securityToken; + global $_CONF, $_TABLES, $LANG_ADMIN, $LANG_ROUTER, $_IMAGE_TYPE, $securityToken; require_once $_CONF['path_system'] . 'lib-admin.php'; @@ -327,21 +326,30 @@ function saveRoute($rid, $method, $rule, $route, $priority) $messageText = $LANG_ROUTER[14]; } elseif (substr_count($rule, '@') !== substr_count($route, '@')) { $messageText = $LANG_ROUTER[15]; - } elseif (stripos($route, '/index.php') === 0) { - $messageText = $LANG_ROUTER[16]; } - // If rule doesn't begin with a slash, then fix it silently + // If a rule doesn't begin with a slash, then add one silently if (strpos($rule, '/') !== 0) { $rule = '/' . $rule; } - // If route doesn't begin with a slash, then fix it silently + // If a rule starts with "/index.php", then remove it silently + if (stripos($rule, '/index.php') === 0) { + $rule = preg_replace('|^/index\.php|i', '', $rule); + } + + // If a route doesn't begin with a slash, then add one silently if (strpos($route, '/') !== 0) { $route = '/' . $route; } - // Replace & with & silently + // If a route starts with "/index.php", then make it an error to prevent the script + // from going an infinite loop + if (stripos($route, '/index.php') === 0) { + $messageText = $LANG_ROUTER[16]; + } + + // Replace & with & $rule = str_ireplace('&', '&', $rule); $route = str_ireplace('&', '&', $route); @@ -456,7 +464,7 @@ function reorderRoutes() */ function moveRoute($rid) { - global $_CONF, $_TABLES, $LANG_ROUTER, $_FINPUT; + global $_TABLES, $_FINPUT; $rid = intval($rid, 10); $direction = $_FINPUT->get('dir', ''); diff --git a/public_html/docs/english/config.html b/public_html/docs/english/config.html index 402d401f5..6bbf2c67d 100644 --- a/public_html/docs/english/config.html +++ b/public_html/docs/english/config.html @@ -95,6 +95,11 @@

Site: Site

false Enable (true) or disable (false) URL rewriting.
Also see the section on URL Rewriting below. + + url_routing + disabled(0) + Enable (true) or disable (false) URL routing. Caution: This feature takes effect only when URL Rewriting is enabled.
+ Also see the section on URL Routing below. cdn_hosted false @@ -2088,6 +2093,111 @@

URL Rewriting

with your site.

+

URL Routing

+ +

This feature enhances the above URL Rewriting feature and makes + public_html/index.php work like a front controller. So, in order to use URL Routing feature, + you have to enable URL Rewriting beforehand. Currently, the following rules are predefined.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RuleRoutePriority
http://yourdomain/article/[sid]/printhttp://yourdomain/article.php?story=[sid]&mode=print100
http://yourdomain/article/[sid]http://yourdomain/article.php?story=[sid]110
http://yourdomain/archives/@topic/@year/@monthhttp://yourdomain/directory.php?topic=@topic&year=@year&month=@month120
http://yourdomain/page/@pagehttp://yourdomain/staticpages/index.php?page=@page130
http://yourdomain/links/portal/@itemhttp://yourdomain/links/portal.php?what=link&item=@item140
http://yourdomain/links/category/@cathttp://yourdomain/links/index.php?category=@cat150
http://yourdomain/topic/@topichttp://yourdomain/index.php?topic=@topic160
+ +

Look at the first row in the table. This means if you access "http://yourdomain/index.php/article/[sid]/print", + then you will be redirected to "http://yourdomain/article.php?story=[sid]&mode=print". + [sid] is a story ID.

+

Then, look at the third row. You will see an at mark (@), followed by alphabetical + letters. This is a placeholder. For instance, when you access + "http://yourdomain/index.php/archives/all/2016/1", @topic matches "all", @year matches "2016", + and @month matches "1" in the rule. Then, all placeholders in the route will be replaced with + the matched values. And you will be redirected to "http://yourdomain/directory.php?topic=all&year=2016&month=1".

+

You don't have to include "/index.php" in a rule.

+

You must not start a route with "/index.php", since this will cause an infinite loop.

+

A placeholder starts with @ and an alphabetical letter or letters follow it. + Placeholders are case-sensitive.

+

URL Routing works in the reverse way as well. When you output a URL in your plugin or custom + function, you can URL-route it by using COM_buildURL function and adding your own rule and + route in the URL Routing screen (admin/router.php).

+

Note: This feature may not work with all web servers. It uses PHP's + $_SERVER['PATH_INFO'] variable. Please try it out before you go public with your site.

+

Advanced feature: If you use Apache as a Web server and want to remove "index.php" + from URLs you access, you have to do the following things. Be warned! + Unless you are familiar with Web server configurations, you might as well not use this feature.

+
    +
  1. Enable the rewrite module of your web server. In "httpd.conf", look for
    + # LoadModule rewrite_module modules/mod_rewrite.so
    + and uncomment the line by removing # at the top of the line. Don't forget to + reboot your Web server.
  2. +
  3. Create a file named ".htaccess" in the top of the public directory of your + Geeklog installation (where lib-common.php is located) and add the following lines.
    +
    +        <IfModule mod_rewrite.c>
    +            RewriteEngine On
    +            # RewriteBase /
    +            # Directs all Geeklog requests through the site index file
    +            RewriteCond %{REQUEST_FILENAME} !-f
    +            RewriteCond %{REQUEST_FILENAME} !-d
    +            RewriteRule ^(.*)$ /index.php/$1 [L]
    +        </IfModule>
    +    

    + If you have installed Geeklog in a subdirectory of the document root, you will + have to change the line
    +
    +            RewriteRule ^(.*)$ /index.php/$1 [L]
    +    
    into
    +
    +            RewriteRule ^(.*)$ [sub_directory]/index.php/$1 [L]
    +        

    + or
    +
    +            RewriteRule ^(.*)$ /[sub_directory]/index.php/$1 [L]
    +        
    +
  4. +
  5. + Finally, log in as admin and go to the Configuration screen, and set URL Routing + to "Enabled (without index.php)". +
  6. +
+ +

Localization

Localizing Geeklog is fairly easy. All strings are contained in a diff --git a/public_html/docs/japanese/config.html b/public_html/docs/japanese/config.html index 108d5d6bb..d844a29c2 100644 --- a/public_html/docs/japanese/config.html +++ b/public_html/docs/japanese/config.html @@ -84,6 +84,11 @@

サイト: サイト

いいえ 「はい」でURLリライトを行い、「いいえ」で行わないようになります。
下記のURLリライトのセクションを参照してください。 + + URLルーティング(url_routing) + いいえ + 「はい」でURLルーティングを行い、「いいえ」で行わないようになります。注意: この機能を使うにはURLリライトを「はい」にしてください。
+ 夏期のURLルーティングのセクションを参照してください。 CDNのjQueryを使う(cdn_hosted) いいえ @@ -1785,6 +1790,110 @@

URLリライト

注意:この機能はすべてのWebサーバーで機能するとは限りません。Apache(全バージョン)では機能しますが、IIS(の少なくとも一部のバージョン)では動作しない ことが知られています。サイトを公開する前に検証してください。

+

URLルーティング

+ +

この機能は上述のURLリライトを拡張し、public_html/index.phpを + 疑似フロントコントローラーして使用します。したがって、URLルーティング機能を使うには、 + URLリライトを有効にする必要があります。現時点では以下のルールが定義されています。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ルールルート優先順位
http://yourdomain/article/[sid]/printhttp://yourdomain/article.php?story=[sid]&mode=print100
http://yourdomain/article/[sid]http://yourdomain/article.php?story=[sid]110
http://yourdomain/archives/@topic/@year/@monthhttp://yourdomain/directory.php?topic=@topic&year=@year&month=@month120
http://yourdomain/page/@pagehttp://yourdomain/staticpages/index.php?page=@page130
http://yourdomain/links/portal/@itemhttp://yourdomain/links/portal.php?what=link&item=@item140
http://yourdomain/links/category/@cathttp://yourdomain/links/index.php?category=@cat150
http://yourdomain/topic/@topichttp://yourdomain/index.php?topic=@topic160
+ +

表の1行目をご覧ください。"http://yourdomain/index.php/article/[sid]/print"にアクセスすると、 + "http://yourdomain/article.php?story=[sid]&mode=print"にリダイレクトされることを意味しています。 + [sid]は記事のIDです。

+

次に3行目をご覧ください。@の後にアルファベットが続くものがプレースホルダーです。たとえば、 + "http://yourdomain/index.php/archives/all/2016/1" にアクセすると、ルールの中の + @topicが"all"に、@year が "2016" に、そして @month が "1" にマッチします。その後、ルートの中の + プレースホルダーがマッチした値に置き換えられ、"http://yourdomain/directory.php?topic=all&year=2016&month=1" + にリダイレクトされます。

+

ルールの中に"/index.php"を含める必要はありません。

+

無限ループになるので、ルートを "/index.php" で始めないようにしてください。

+

プレースホルダーは@で始まり、アルファベットが続きます。プレースホルダーは大文字・小文字を区別します。

+

URLルーティングは逆向きにも働きます。自作のプラグインやカスタム関数の中でURLを出力する時に、 + COM_buildURL 関数を使い、URLルーティング(admin/router.php)で独自のルールとルートを設定する + ことでURLルーティングを行うことができます。

+

注意: この機能は全てのWebサーバーで使えるとは限りません。PHPの + $_SERVER['PATH_INFO'] 変数を使用します。サイトを公開する前に検証してください。

+

高度な機能: WebサーバーとしてApacheを使用し、ユーザーがアクセするURLから + "index.php" を取り除きたいなら、以下のことを行います。警告! Webサーバーの + 設定を熟知していない場合は、この機能を使わない方が良いでしょう。

+
    +
  1. Webサーバーの rewrite モジュールを有効にします。"httpd.conf" の中で次の行を探します。
    + # LoadModule rewrite_module modules/mod_rewrite.so
    + そして行頭の#を取り除いてコメントを外します。Webサーバーを再起動するのを忘れないでください。
  2. +
  3. Geeklogをインストールしている公開領域のトップのディレクトリ(lib-common.phpがある場所)に + ".htaccess" という名前のファイルを設置し、次の行を追加します。 +
    +        <IfModule mod_rewrite.c>
    +            RewriteEngine On
    +            # RewriteBase /
    +            # Directs all Geeklog requests through the site index file
    +            RewriteCond %{REQUEST_FILENAME} !-f
    +            RewriteCond %{REQUEST_FILENAME} !-d
    +            RewriteRule ^(.*)$ /index.php/$1 [L]
    +        </IfModule>
    +    
    + Geeklogをドキュメントルートのサブディレクトリにインストールしている場合は、 +
    +            RewriteRule ^(.*)$ /index.php/$1 [L]
    +    
    を、 +
    +            RewriteRule ^(.*)$ [sub_directory]/index.php/$1 [L]
    +        
    + や、 +
    +            RewriteRule ^(.*)$ /[sub_directory]/index.php/$1 [L]
    +        
    + に変える必要があるでしょう。 +
  4. +
  5. + 最後に、管理者としてログインし、コンフィギュレーションでURLルーティングの設定を + 「有効(index.phpあり)」に変えます。 +
  6. +
+ +

ローカリゼーション(Localization)

Geeklogをローカライズするのは簡単です。文字列はすべて言語ファイルに格納されています。Geeklogに最初から同梱されているのはenglish.phpです。他の言語への翻訳に関心のある方はGeeklog翻訳(geeklog-translations)メーリングリストへの加入をお勧めします。Geeklogの翻訳に関する重要な情報はみなこのメーリングリストに投稿されます。

diff --git a/sql/mssql_tableanddata.php b/sql/mssql_tableanddata.php index 55784377e..4ee75c32a 100644 --- a/sql/mssql_tableanddata.php +++ b/sql/mssql_tableanddata.php @@ -1537,9 +1537,13 @@ $_SQL[] = "INSERT INTO {$_TABLES['postmodes']} (code, name) VALUES ('plaintext','Plain Old Text')"; $_SQL[] = "INSERT INTO {$_TABLES['postmodes']} (code, name) VALUES ('html','HTML Formatted')"; -$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/article/@sid', '/article.php?story=@sid')"; -$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/topic/@topic', '/index.php?topic=@topic')"; -$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/page/@page', '/staticpages/index.php?page=@page')"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/article/@sid', '/article.php?story=@sid&mode=print', 100)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/article/@sid', '/article.php?story=@sid', 110)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/archives/@topic/@year/@month', '/directory.php?topic=@topic&year=@year&month=@month', 120)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/page/@page', '/staticpages/index.php?page=@page', 130)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/links/portal/@item', '/links/portal.php?what=link&item=@item', 140)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/links/category/@cat', '/links/index.php?category=@cat', 150)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/topic/@topic', '/index.php?topic=@topic', 160)"; $_SQL[] = "INSERT INTO {$_TABLES['sortcodes']} (code, name) VALUES ('ASC','Oldest First')"; $_SQL[] = "INSERT INTO {$_TABLES['sortcodes']} (code, name) VALUES ('DESC','Newest First')"; diff --git a/sql/mysql_tableanddata.php b/sql/mysql_tableanddata.php index 649ec00ae..c5f57ecd4 100644 --- a/sql/mysql_tableanddata.php +++ b/sql/mysql_tableanddata.php @@ -808,9 +808,13 @@ $_DATA[] = "INSERT INTO {$_TABLES['postmodes']} (code, name) VALUES ('plaintext','Plain Old Text') "; $_DATA[] = "INSERT INTO {$_TABLES['postmodes']} (code, name) VALUES ('html','HTML Formatted') "; -$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/article/@sid', '/article.php?story=@sid')"; -$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/topic/@topic', '/index.php?topic=@topic')"; -$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/page/@page', '/staticpages/index.php?page=@page')"; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/article/@sid', '/article.php?story=@sid&mode=print', 100)"; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/article/@sid', '/article.php?story=@sid', 110)"; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/archives/@topic/@year/@month', '/directory.php?topic=@topic&year=@year&month=@month', 120)"; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/page/@page', '/staticpages/index.php?page=@page', 130)"; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/links/portal/@item', '/links/portal.php?what=link&item=@item', 140)"; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/links/category/@cat', '/links/index.php?category=@cat', 150)"; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/topic/@topic', '/index.php?topic=@topic', 160)"; $_DATA[] = "INSERT INTO {$_TABLES['sortcodes']} (code, name) VALUES ('ASC','Oldest First') "; $_DATA[] = "INSERT INTO {$_TABLES['sortcodes']} (code, name) VALUES ('DESC','Newest First') "; diff --git a/sql/pgsql_tableanddata.php b/sql/pgsql_tableanddata.php index 0f7901e3e..8e9744f28 100644 --- a/sql/pgsql_tableanddata.php +++ b/sql/pgsql_tableanddata.php @@ -826,9 +826,13 @@ $_DATA[] = "INSERT INTO {$_TABLES['postmodes']} (code, name) VALUES ('plaintext','Plain Old Text') "; $_DATA[] = "INSERT INTO {$_TABLES['postmodes']} (code, name) VALUES ('html','HTML Formatted') "; -$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/article/@sid', '/article.php?story=@sid')"; -$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/topic/@topic', '/index.php?topic=@topic')"; -$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/page/@page', '/staticpages/index.php?page=@page')"; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/article/@sid', '/article.php?story=@sid&mode=print', 100)"; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/article/@sid', '/article.php?story=@sid', 110)"; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/archives/@topic/@year/@month', '/directory.php?topic=@topic&year=@year&month=@month', 120)"; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/page/@page', '/staticpages/index.php?page=@page', 130)"; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/links/portal/@item', '/links/portal.php?what=link&item=@item', 140)"; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/links/category/@cat', '/links/index.php?category=@cat', 150)"; +$_DATA[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/topic/@topic', '/index.php?topic=@topic', 160)"; $_DATA[] = "INSERT INTO {$_TABLES['sortcodes']} (code, name) VALUES ('ASC','Oldest First') "; $_DATA[] = "INSERT INTO {$_TABLES['sortcodes']} (code, name) VALUES ('DESC','Newest First') "; diff --git a/sql/updates/mssql_2.1.0_to_2.1.2.php b/sql/updates/mssql_2.1.0_to_2.1.2.php index e67149593..0bc38b0e7 100644 --- a/sql/updates/mssql_2.1.0_to_2.1.2.php +++ b/sql/updates/mssql_2.1.0_to_2.1.2.php @@ -18,9 +18,13 @@ "; // Add sample routes to the table -$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/article/@sid', '/article.php?story=@sid')"; -$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/topic/@topic', '/index.php?topic=@topic')"; -$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/page/@page', '/staticpages/index.php?page=@page')"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/article/@sid', '/article.php?story=@sid&mode=print', 100)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/article/@sid', '/article.php?story=@sid', 110)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/archives/@topic/@year/@month', '/directory.php?topic=@topic&year=@year&month=@month', 120)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/page/@page', '/staticpages/index.php?page=@page', 130)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/links/portal/@item', '/links/portal.php?what=link&item=@item', 140)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/links/category/@cat', '/links/index.php?category=@cat', 150)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/topic/@topic', '/index.php?topic=@topic', 160)"; /** * Add new config options diff --git a/sql/updates/mysql_2.1.0_to_2.1.2.php b/sql/updates/mysql_2.1.0_to_2.1.2.php index ac18d1125..f93066271 100644 --- a/sql/updates/mysql_2.1.0_to_2.1.2.php +++ b/sql/updates/mysql_2.1.0_to_2.1.2.php @@ -11,9 +11,13 @@ )"; // Add sample routes to the table -$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/article/@sid', '/article.php?story=@sid')"; -$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/topic/@topic', '/index.php?topic=@topic')"; -$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/page/@page', '/staticpages/index.php?page=@page')"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/article/@sid', '/article.php?story=@sid&mode=print', 100)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/article/@sid', '/article.php?story=@sid', 110)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/archives/@topic/@year/@month', '/directory.php?topic=@topic&year=@year&month=@month', 120)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/page/@page', '/staticpages/index.php?page=@page', 130)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/links/portal/@item', '/links/portal.php?what=link&item=@item', 140)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/links/category/@cat', '/links/index.php?category=@cat', 150)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/topic/@topic', '/index.php?topic=@topic', 160)"; /** * Add new config options diff --git a/sql/updates/pgsql_2.1.0_to_2.1.2.php b/sql/updates/pgsql_2.1.0_to_2.1.2.php index 436b4d4c4..30eee310e 100644 --- a/sql/updates/pgsql_2.1.0_to_2.1.2.php +++ b/sql/updates/pgsql_2.1.0_to_2.1.2.php @@ -11,9 +11,13 @@ )"; // Add sample routes to the table -$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/article/@sid', '/article.php?story=@sid')"; -$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/topic/@topic', '/index.php?topic=@topic')"; -$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route) VALUES (1, '/page/@page', '/staticpages/index.php?page=@page')"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/article/@sid', '/article.php?story=@sid&mode=print', 100)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/article/@sid', '/article.php?story=@sid', 110)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/archives/@topic/@year/@month', '/directory.php?topic=@topic&year=@year&month=@month', 120)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/page/@page', '/staticpages/index.php?page=@page', 130)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/links/portal/@item', '/links/portal.php?what=link&item=@item', 140)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/links/category/@cat', '/links/index.php?category=@cat', 150)"; +$_SQL[] = "INSERT INTO {$_TABLES['routes']} (method, rule, route, priority) VALUES (1, '/topic/@topic', '/index.php?topic=@topic', 160)"; /** * Add new config options From 0407a81ed5075fc528582fd5f1f7646ee8596df6 Mon Sep 17 00:00:00 2001 From: Kenji ITO Date: Sun, 24 Jan 2016 00:37:02 +0900 Subject: [PATCH 09/81] Fixed a typo --- public_html/docs/japanese/config.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public_html/docs/japanese/config.html b/public_html/docs/japanese/config.html index 461e100b3..aa25d7e26 100644 --- a/public_html/docs/japanese/config.html +++ b/public_html/docs/japanese/config.html @@ -88,7 +88,7 @@

サイト: サイト

URLルーティング(url_routing) いいえ 「はい」でURLルーティングを行い、「いいえ」で行わないようになります。注意: この機能を使うにはURLリライトを「はい」にしてください。
- 夏期のURLルーティングのセクションを参照してください。 + 下記のURLルーティングのセクションを参照してください。 CDNのjQueryを使う(cdn_hosted) いいえ From 73ec58cc3bdbc07417ea3674ecdea1ee3ba25948 Mon Sep 17 00:00:00 2001 From: Kenji ITO Date: Sun, 24 Jan 2016 06:20:08 +0900 Subject: [PATCH 10/81] Modified to allow routes to start with "/index.php?" (not with "/index.php/") --- language/english.php | 2 +- language/english_utf-8.php | 2 +- language/japanese_utf-8.php | 2 +- public_html/admin/router.php | 4 ++-- public_html/docs/english/config.html | 3 ++- public_html/docs/japanese/config.html | 2 +- 6 files changed, 8 insertions(+), 7 deletions(-) diff --git a/language/english.php b/language/english.php index 0d7f06ef0..b5b40126b 100644 --- a/language/english.php +++ b/language/english.php @@ -1355,7 +1355,7 @@ 13 => 'Rule is a mandatory item.', 14 => 'Route is a mandatory item.', 15 => 'Placeholders (@) in a rule and those in a route must be the same.', - 16 => 'Route must not start with "index.php".', + 16 => 'Route must not start with "/index.php/".', 17 => 'Database error occurred.', 18 => 'To enable URL routing, you have to enable URL rewrite.', 19 => '
  • Placeholders (@) must be the same both in a rule and its route.
  • A placeholder starts with "@", followed by an alphabet, optionally followed by any length of alphabet or digit.
  • Placeholders are case-sensitive.
', diff --git a/language/english_utf-8.php b/language/english_utf-8.php index d5e70af6d..f0b7f8e92 100644 --- a/language/english_utf-8.php +++ b/language/english_utf-8.php @@ -1355,7 +1355,7 @@ 13 => 'Rule is a mandatory item.', 14 => 'Route is a mandatory item.', 15 => 'Placeholders (@) in a rule and those in a route must be the same.', - 16 => 'Route must not start with "index.php".', + 16 => 'Route must not start with "/index.php/".', 17 => 'Database error occurred.', 18 => 'To enable URL routing, you have to enable URL rewrite.', 19 => '
  • Placeholders (@) must be the same both in a rule and its route.
  • A placeholder starts with "@", followed by an alphabet, optionally followed by any length of alphabet or digit.
  • Placeholders are case-sensitive.
', diff --git a/language/japanese_utf-8.php b/language/japanese_utf-8.php index 285c3a132..e07c64dde 100644 --- a/language/japanese_utf-8.php +++ b/language/japanese_utf-8.php @@ -1354,7 +1354,7 @@ 13 => 'ルールは必須項目です。', 14 => 'ルートは必須項目です。', 15 => 'ルールとルートで同じプレースホルダー(@)を定義しなければなりません。', - 16 => 'ルートの先頭にindex.phpを含むことはできません。', + 16 => 'ルートの先頭を "/index.php/" とすることはできません。', 17 => 'データベース操作でエラーが発生しました。', 18 => 'URLルーティングを有効にするには、URLリライトを有効にしてください。', 19 => '
  • プレースホルダー(@)はルールとルートで同じものを定義してください。
  • プレースホルダーは@で始まり、1文字目は英字、2文字目以降は英数字を使えます。
  • プレースホルダーは大文字・小文字を区別します。
', diff --git a/public_html/admin/router.php b/public_html/admin/router.php index 349ce1541..5f37c9d2e 100644 --- a/public_html/admin/router.php +++ b/public_html/admin/router.php @@ -343,9 +343,9 @@ function saveRoute($rid, $method, $rule, $route, $priority) $route = '/' . $route; } - // If a route starts with "/index.php", then make it an error to prevent the script + // If a route starts with "/index.php/", then make it an error to prevent the script // from going an infinite loop - if (stripos($route, '/index.php') === 0) { + if (stripos($route, '/index.php/') === 0) { $messageText = $LANG_ROUTER[16]; } diff --git a/public_html/docs/english/config.html b/public_html/docs/english/config.html index 81c8d22b0..5a8044c24 100644 --- a/public_html/docs/english/config.html +++ b/public_html/docs/english/config.html @@ -2154,7 +2154,8 @@

URL Routing

and @month matches "1" in the rule. Then, all placeholders in the route will be replaced with the matched values. And you will be redirected to "http://yourdomain/directory.php?topic=all&year=2016&month=1".

You don't have to include "/index.php" in a rule.

-

You must not start a route with "/index.php", since this will cause an infinite loop.

+

You must not start a route with "/index.php/", since this will cause an infinite loop. However, you can start + with a route with "/index.php?".

A placeholder starts with @ and an alphabetical letter or letters follow it. Placeholders are case-sensitive.

URL Routing works in the reverse way as well. When you output a URL in your plugin or custom diff --git a/public_html/docs/japanese/config.html b/public_html/docs/japanese/config.html index aa25d7e26..2378621d5 100644 --- a/public_html/docs/japanese/config.html +++ b/public_html/docs/japanese/config.html @@ -1852,7 +1852,7 @@

URLルーティング

プレースホルダーがマッチした値に置き換えられ、"http://yourdomain/directory.php?topic=all&year=2016&month=1" にリダイレクトされます。

ルールの中に"/index.php"を含める必要はありません。

-

無限ループになるので、ルートを "/index.php" で始めないようにしてください。

+

無限ループになるので、ルートの先頭は "/index.php/" にしないでください("/index.php?" は大丈夫です)。

プレースホルダーは@で始まり、アルファベットが続きます。プレースホルダーは大文字・小文字を区別します。

URLルーティングは逆向きにも働きます。自作のプラグインやカスタム関数の中でURLを出力する時に、 COM_buildURL 関数を使い、URLルーティング(admin/router.php)で独自のルールとルートを設定する From 5e9ff986ca844ab996b23c1c0e0a5360655a63aa Mon Sep 17 00:00:00 2001 From: Kenji ITO Date: Sun, 24 Jan 2016 07:15:32 +0900 Subject: [PATCH 11/81] Refactored to use class constants --- public_html/admin/router.php | 4 ++-- system/classes/router.class.php | 39 ++++++++++++++++++++++----------- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/public_html/admin/router.php b/public_html/admin/router.php index 5f37c9d2e..fdc6d5ab7 100644 --- a/public_html/admin/router.php +++ b/public_html/admin/router.php @@ -354,8 +354,8 @@ function saveRoute($rid, $method, $rule, $route, $priority) $route = str_ireplace('&', '&', $route); // Check if placeholders are the same - $numPlaceHoldersInRule = preg_match_all(Router::PATTERN_PLACEHOLDER, $rule, $matchesRule, PREG_SET_ORDER); - $numPlaceHoldersInRoute = preg_match_all(Router::PATTERN_PLACEHOLDER, $route, $matchesRoute, PREG_SET_ORDER); + $numPlaceHoldersInRule = preg_match_all(Router::PLACEHOLDER_MATCH, $rule, $matchesRule, PREG_SET_ORDER); + $numPlaceHoldersInRoute = preg_match_all(Router::PLACEHOLDER_MATCH, $route, $matchesRoute, PREG_SET_ORDER); if ($numPlaceHoldersInRule === $numPlaceHoldersInRoute) { if ($numPlaceHoldersInRule > 0) { diff --git a/system/classes/router.class.php b/system/classes/router.class.php index 5016a5707..d242b9a00 100644 --- a/system/classes/router.class.php +++ b/system/classes/router.class.php @@ -18,7 +18,11 @@ class Router const ROUTING_WITHOUT_INDEX_PHP = 2; // Placeholder pattern - const PATTERN_PLACEHOLDER = '|(@[a-zA-Z][0-9a-zA-Z_]*)|'; + const PLACEHOLDER_MATCH = '|(@[a-zA-Z][0-9a-zA-Z_]*)|'; + const PLACEHOLDER_REPLACE = '([^/&=?#]+)'; + + // Values to escape pattern + const VALUE_MATCH = '|[^0-9a-zA-Z_.-]|'; // Default priority const DEFAULT_PRIORITY = 100; @@ -39,7 +43,7 @@ public static function setDebug($switch) } /** - * Dispatch the client + * Dispatch the client based on $_SERVER['PATH_INFO'] * * @return bool when not dispatched */ @@ -128,11 +132,12 @@ public static function dispatch() header('Location: ' . $route); COM_errorLog(__METHOD__ . ': somehow could not redirect'); + return false; } // Try comparison with placeholders - if (preg_match_all(self::PATTERN_PLACEHOLDER, $rule, $matches, PREG_SET_ORDER)) { + if (preg_match_all(self::PLACEHOLDER_MATCH, $rule, $matches, PREG_SET_ORDER)) { // Escape a period and a question mark so that they can safely be used in a regular expression $rule = str_replace(array('.', '?'), array('\.', '\?'), $rule); $placeHolders = array(); @@ -140,7 +145,7 @@ public static function dispatch() // Replace placeholders in a rule with ones for regular expressions foreach ($matches as $match) { $placeHolders[] = $match[1]; - $rule = str_replace($match[1], '([^/&=?#]+)', $rule); + $rule = str_replace($match[1], self::PLACEHOLDER_REPLACE, $rule); } $rule = '|\A' . $rule . '\z|i'; @@ -152,7 +157,10 @@ public static function dispatch() array_shift($values); foreach ($values as $value) { - $value = urlencode($value); + if (preg_match(self::VALUE_MATCH, $value)) { + $value = urlencode($value); + } + $placeHolder = array_shift($placeHolders); $route = str_replace($placeHolder, $value, $route); } @@ -183,7 +191,8 @@ public static function dispatch() /** * Convert a URL * - * e.g. [SITE_URL]/article.php?story=welcome -> [SITE_URL]/index.php/article/welcome or [SITE_URL]/article/welcome + * e.g. [SITE_URL]/article.php?story=welcome + * -> [SITE_URL]/index.php/article/welcome or [SITE_URL]/article/welcome * * @param string $url * @param int $requestMethod @@ -195,7 +204,7 @@ public static function convertUrl($url, $requestMethod = self::HTTP_REQUEST_GET) $originalUrl = $url; - // URL rewrite is disabled + // URL rewriting is disabled if (!$_CONF['url_rewrite']) { return $originalUrl; } @@ -213,7 +222,7 @@ public static function convertUrl($url, $requestMethod = self::HTTP_REQUEST_GET) } // Strip $url of $_CONF['site_url'] - $url = str_replace($_CONF['site_url'], '', $url); + $url = str_ireplace($_CONF['site_url'], '', $url); // Check for $requestMethod $requestMethod = intval($requestMethod, 10); @@ -261,17 +270,18 @@ public static function convertUrl($url, $requestMethod = self::HTTP_REQUEST_GET) } // Try comparison with placeholders - if (preg_match_all(self::PATTERN_PLACEHOLDER, $route, $matches, PREG_SET_ORDER)) { + if (preg_match_all(self::PLACEHOLDER_MATCH, $route, $matches, PREG_SET_ORDER)) { $placeHolders = array(); + // Escape '.', '?' and '+' in the route so that they can safely be used in a regular expression + $route = str_replace(array('.', '?', '+'), array('\.', '\?', '\+'), $route); + // Replace placeholders in a route with ones for regular expressions foreach ($matches as $match) { $placeHolders[] = $match[1]; - $route = str_replace($match[1], '([^/&=?#]+)', $route); + $route = str_replace($match[1], self::PLACEHOLDER_REPLACE, $route); } - // Escape a period and a question mark so that they can safely be used in a regular expression - $route = str_replace(array('.', '?'), array('\.', '\?'), $route); $route = '|\A' . $route . '\z|i'; if (!preg_match($route, $url, $values)) { @@ -281,7 +291,10 @@ public static function convertUrl($url, $requestMethod = self::HTTP_REQUEST_GET) array_shift($values); foreach ($values as $value) { - $value = urlencode($value); + if (preg_match(self::VALUE_MATCH, $value)) { + $value = urlencode($value); + } + $placeHolder = array_shift($placeHolders); $rule = str_replace($placeHolder, $value, $rule); } From 96b5d1320c8d33944e927cc959b8114dd3d27a73 Mon Sep 17 00:00:00 2001 From: Kenji ITO Date: Sun, 31 Jan 2016 11:48:03 +0900 Subject: [PATCH 12/81] Added language entries --- language/english.php | 6 +- language/japanese_utf-8.php | 8 +- public_html/admin/comment.php | 310 ++++++++++++++++++++++++++++++++++ 3 files changed, 320 insertions(+), 4 deletions(-) create mode 100644 public_html/admin/comment.php diff --git a/language/english.php b/language/english.php index 033569ab7..b118357a9 100644 --- a/language/english.php +++ b/language/english.php @@ -242,7 +242,7 @@ 42 => 'Unsubscribe', 43 => 'Unsubscribe from reply notifications', 44 => 'Your Name', - 45 => "Thank you for your comment. You must wait {$_CONF['commentspeedlimit']} seconds to post a new comment." + 45 => "Thank you for your comment. You must wait {$_CONF['commentspeedlimit']} seconds to post a new comment.", ); ############################################################################### @@ -1454,6 +1454,7 @@ 118 => 'Click to select a date', 119 => 'More..', 120 => 'Send this?', + 130 => 'Deleted comment(s).', 400 => 'Not all required fields have been passed validation', // Error codes in the 400 range reserved for CUSTOM membership 401 => 'Please enter Fullname', 500 => 'The Template Cache has been successfully cleared.' @@ -1824,7 +1825,8 @@ 'token_expiry' => 'You have until %s to make changes. After that time, the security token embedded into this page will expire and you will lose your changes.', 'token_expired' => 'The security token for this operation has expired. Please authenticate again to continue.', 'reauth_msg' => 'The security token for this operation has expired. If you want to continue with this operation, then please authenticate again below. This will ensure that the changes you just made will not be lost.', - 'authenticate' => 'Authenticate' + 'authenticate' => 'Authenticate', + 'approve' => 'Approve', ); # Localisation of the texts for the various drop-down menus that are actually diff --git a/language/japanese_utf-8.php b/language/japanese_utf-8.php index 0f87721fe..044328bb4 100644 --- a/language/japanese_utf-8.php +++ b/language/japanese_utf-8.php @@ -250,7 +250,9 @@ 42 => '通知を解除', 43 => '返信の通知を解除', 44 => 'あなたの名前', - 45 => "コメントをありがとうございます。 新たなコメント投稿のために {$_CONF['commentspeedlimit']} 秒以上お待ちください。" + 45 => "コメントをありがとうございます。 新たなコメント投稿のために {$_CONF['commentspeedlimit']} 秒以上お待ちください。", + 100 => 'コメント管理', + 101 => 'コメントを削除できませんでした。', ); ############################################################################### @@ -1453,6 +1455,7 @@ 118 => 'クリックして日付を選択してください', 119 => 'More..', 120 => 'この項目を送信しますか?', + 130 => 'コメントを削除しました。', 400 => '検証に通っていない必須のフィールドがあります。', 401 => '氏名を入力してください。', 500 => 'テンプレートのキャッシュを削除しました。' @@ -1826,7 +1829,8 @@ 'token_expiry' => '%s までに編集してください。その時刻を過ぎると、このページに埋め込まれたセキュリティトークンは期限切れとなって、編集内容を失うことになります。', 'token_expired' => 'この操作のセキュリティトークンは期限切れになりました。続けるには再度認証してください。', 'reauth_msg' => 'この操作のセキュリティトークンは期限切れになりました。続けるには下の認証を行ってください。そうすれば今回の編集作業を失うことはありません。', - 'authenticate' => '認証する' + 'authenticate' => '認証する', + 'approve' => '承認する', ); # Localisation of the texts for the various drop-down menus that are actually diff --git a/public_html/admin/comment.php b/public_html/admin/comment.php new file mode 100644 index 000000000..df6ef7bec --- /dev/null +++ b/public_html/admin/comment.php @@ -0,0 +1,310 @@ + $MESSAGE[30])); + COM_accessLog("User {$_USER['username']} tried to illegally access the block administration screen"); + COM_output($display); + exit; +} + +// Comment library +require_once $_CONF['path_system'] . 'lib-comment.php'; + +/** + * Field function + * + * @param string $fieldName + * @param string $fieldValue + * @param array $A + * @param array $iconArray + * @param string $extra + * @return string + */ +function ADMIN_getListField_comments($fieldName, $fieldValue, $A, $iconArray, $extra = '') +{ + global $_CONF, $_TABLES, $LANG_ADMIN, $LANG03, $_IMAGE_TYPE, $securityToken; + + switch ($fieldName) { + case 'edit': + $cid = $A['cid']; + $fieldValue = ''; + break; + + default: + break; + } + + return $fieldValue; +} + +/** + * Return a selector to filter item type + * + * @param string $itemType + * @return string + */ +function getTypeSelector($itemType) +{ + global $_PLUGINS, $_FINPUT, $LANG_ADMIN, $LANG09, $LANG_STATIC, $LANG_POLLS; + + $retval = $LANG_ADMIN['type'] + . ': ' . LB; + + return $retval; +} + +/** + * Display two lists of comments, ordinary comments and submissions + * + * @return string HTML for the two lists + */ +function listComments() +{ + global $_CONF, $_PLUGINS, $_SCRIPTS, $_TABLES, $_FINPUT, $LANG_ADMIN, $LANG03, $_IMAGE_TYPE, $securityToken; + + require_once $_CONF['path_system'] . 'lib-admin.php'; + + $securityToken = SEC_createToken(); + + // Writing the menu on top + $menu_arr = array( + array( + 'url' => $_CONF['site_admin_url'], + 'text' => $LANG_ADMIN['admin_home'], + ), + ); + + $retval = COM_startBlock($LANG03[100], '', COM_getBlockTemplate('_admin_block', 'header')) + . ADMIN_createMenu( + $menu_arr, + $LANG03[100], + $_CONF['layout_url'] . '/images/icons/trackback.' . $_IMAGE_TYPE + ); + + // Regular Comments + $headerArray = array( # display 'text' and use table field 'field' + array('text' => '', 'field' => 'edit', 'sort' => false), + array('text' => 'Type', 'field' => 'type', 'sort' => true), + array('text' => 'Sid', 'field' => 'sid', 'sort' => true), + array('text' => 'Date', 'field' => 'date', 'sort' => true), + array('text' => $LANG_ADMIN['title'], 'field' => 'title', 'sort' => true), + array('text' => 'Comment', 'field' => 'comment', 'sort' => true), + array('text' => 'Name', 'field' => 'name', 'sort' => true), + array('text' => 'Uid', 'field' => 'uid', 'sort' => true), + array('text' => 'IP Address', 'field' => 'ipaddress', 'sort' => true), + ); + + $defaultSortArray = array('field' => 'date', 'direction' => 'desc'); + + $textArray = array( + 'has_extras' => true, + 'title' => $LANG03[100], + 'form_url' => $_CONF['site_admin_url'] . '/comment.php', + ); + + $itemType = $_FINPUT->post('item_type', ''); + + switch ($itemType) { + case 'article': + case 'all': + break; + + case 'staticpages': + if (!in_array('staticpages', $_PLUGINS)) { + $itemType = ''; + } + break; + + case 'polls': + if (!in_array('polls', $_PLUGINS)) { + $itemType = ''; + } + break; + + default: + $itemType = ''; + break; + } + + if (($itemType === '') || ($itemType === 'all')) { + $sqlForType = ''; + } else { + $sqlForType = " AND (type = '" . DB_escapeString($itemType) . "') "; + } + + $queryArray = array( + 'table' => 'comments', + 'sql' => "SELECT * FROM {$_TABLES['comments']} WHERE (1 = 1) ", + 'query_fields' => array('type', 'sid', 'date', 'title', 'comment', 'name', 'uid', 'ipaddress'), + 'default_filter' => $sqlForType . COM_getPermSql('AND'), + ); + + $filter = getTypeSelector($itemType); + $options = array(); + $actionSelector = '' . LB + . ''; + + $formArray = array( + 'top' => '', + 'bottom' => $actionSelector, + ); + + $retval .= ADMIN_list( + 'comments', 'ADMIN_getListField_comments', $headerArray, $textArray, + $queryArray, $defaultSortArray, $filter, $securityToken, $options, $formArray + ); + + $retval .= COM_endBlock(COM_getBlockTemplate('_admin_block', 'footer')); + $_SCRIPTS->setJavaScript('jQuery("#select_all").on("change", function (e) { + jQuery("input[name=cids\[\]]").checked = e.target.checked; + }); + '); + return $retval; +} + +/** + * Delete a comment + * + * @return string HTML redirect or error message + */ +function deleteComment() +{ + global $_CONF, $_TABLES, $_USER, $_FINPUT, $LANG03; + + $cid = $_FINPUT->get('cid', $_FINPUT->post('cid', 0)); + $cid = intval($cid, 10); + $sid = $_FINPUT->post('sid', ''); + $type = $_FINPUT->post('type', ''); + + $retval = ''; + + if (($cid <= 0) || ($sid === '') || ($type === '')) { + COM_errorLog("Attempted to delete a nonexistent comment (cid = {$cid})"); + $retval = COM_refresh($_CONF['site_admin_url'] . '/comment.php'); + } elseif (!SEC_checkToken()) { + COM_accessLog("User {$_USER['username']} tried to delete comment (cid = {$cid}) and failed CSRF checks."); + $retval = COM_refresh($_CONF['site_admin_url'] . '/index.php'); + } else { + if (DB_count($_TABLES['comments'], array('cid', 'sid', 'type'), array($cid, $sid, $type)) == 1) { + $result = CMT_deleteComment($cid, $sid, $type); + + if ($result == 0) { + $retval = COM_refresh($_CONF['site_admin_url'] . '/comment.php?msg=130'); + } + } else { + // Failed to delete a comment + $retval = $LANG03[101]; + } + } + + return $retval; +} + +/** + * Approve a comment + * + * @param int $cid id of comment to delete + * @return string HTML redirect or error message + */ +function approveComment($cid = 0) +{ + $cid = intval($cid, 10); + CMT_approveModeration($cid); +} + +// MAIN +$mode = $_FINPUT->get('mode', $_INPUT->post('mode', '')); +$cid = $_FINPUT->get('cid' . $_FINPUT->post('cid', 0)); +$cid = intval($cid, 10); + +switch ($mode) { + case $LANG_ADMIN['delete']: + deleteComment(); + break; + + case $LANG_ADMIN['approve']: + $content = ''; + + if (SEC_checkToken()) { + $content = approveComment($cid); + } + + $content .= listComments(); + $display = COM_createHTMLDocument($content, array('pagetitle' => $LANG21[19])); + break; + + default:// 'cancel' or no mode at all + $content = COM_showMessageFromParameter() + . listComments(); + $display = COM_createHTMLDocument($content, array('pagetitle' => $LANG21[19])); + break; +} + +COM_output($display); From 97f7eec93245b832360788420e8226552834c78c Mon Sep 17 00:00:00 2001 From: Kenji ITO Date: Wed, 3 Feb 2016 09:38:33 +0900 Subject: [PATCH 13/81] Added bulk actions --- language/english.php | 6 + language/japanese_utf-8.php | 4 + public_html/admin/comment.php | 294 +++++++++++++++--- public_html/javascript/comment.js | 34 ++ .../layout/denim/images/icons/comment.png | Bin 0 -> 2925 bytes .../modern_curve/images/icons/comment.png | Bin 0 -> 2925 bytes .../professional/images/icons/comment.png | Bin 0 -> 2925 bytes .../professional_css/images/icons/comment.png | Bin 0 -> 2925 bytes public_html/lib-common.php | 8 + 9 files changed, 299 insertions(+), 47 deletions(-) create mode 100644 public_html/javascript/comment.js create mode 100644 public_html/layout/denim/images/icons/comment.png create mode 100644 public_html/layout/modern_curve/images/icons/comment.png create mode 100644 public_html/layout/professional/images/icons/comment.png create mode 100644 public_html/layout/professional_css/images/icons/comment.png diff --git a/language/english.php b/language/english.php index b118357a9..12e72f49d 100644 --- a/language/english.php +++ b/language/english.php @@ -243,6 +243,12 @@ 43 => 'Unsubscribe from reply notifications', 44 => 'Your Name', 45 => "Thank you for your comment. You must wait {$_CONF['commentspeedlimit']} seconds to post a new comment.", + 100 => 'Comment Manager', + 101 => 'Could not delete a comment,', + 102 => 'Bulk Action', + 103 => 'Ban this user', + 104 => 'Ban this IP address with the Spamx plugin', + 105 => 'IP Address', ); ############################################################################### diff --git a/language/japanese_utf-8.php b/language/japanese_utf-8.php index 044328bb4..5dc022716 100644 --- a/language/japanese_utf-8.php +++ b/language/japanese_utf-8.php @@ -253,6 +253,10 @@ 45 => "コメントをありがとうございます。 新たなコメント投稿のために {$_CONF['commentspeedlimit']} 秒以上お待ちください。", 100 => 'コメント管理', 101 => 'コメントを削除できませんでした。', + 102 => '一括操作', + 103 => 'このユーザーを禁止する', + 104 => 'このIPアドレスをSpamxプラグインで禁止する', + 105 => 'IPアドレス', ); ############################################################################### diff --git a/public_html/admin/comment.php b/public_html/admin/comment.php index df6ef7bec..388e972b0 100644 --- a/public_html/admin/comment.php +++ b/public_html/admin/comment.php @@ -56,6 +56,23 @@ // Comment library require_once $_CONF['path_system'] . 'lib-comment.php'; +/** + * Return comment IDs being selected in the list + * + * @return array of int + */ +function getCids() { + global $_FINPUT; + + $cids = $_FINPUT->post('cids', array()); + + if (count($cids) > 0) { + $cids = array_map('intval', $cids); + } + + return $cids; +} + /** * Field function * @@ -65,10 +82,20 @@ * @param array $iconArray * @param string $extra * @return string + * @throws Exception */ function ADMIN_getListField_comments($fieldName, $fieldValue, $A, $iconArray, $extra = '') { - global $_CONF, $_TABLES, $LANG_ADMIN, $LANG03, $_IMAGE_TYPE, $securityToken; + global $_CONF, $_TABLES, $LANG_ADMIN, $LANG01, $LANG03, $LANG_STATIC, $LANG_POLLS, $_IMAGE_TYPE, $securityToken; + static $encoding = null; + + if ($encoding === null) { + $encoding = COM_getEncodingt(); + } + + if (!in_array($A['type'], array('article', 'staticpages', 'polls'))) { + throw new Exception(__FUNCTION__ . ': unknown type "' . $A['type'] . '" was given'); + } switch ($fieldName) { case 'edit': @@ -76,6 +103,68 @@ function ADMIN_getListField_comments($fieldName, $fieldValue, $A, $iconArray, $e $fieldValue = ''; break; + case 'type': + switch ($fieldValue) { + case 'article': + $fieldValue = $LANG01[11]; + break; + + case 'staticpages': + $fieldValue = $LANG_STATIC['staticpages']; + break; + + case 'polls': + $fieldValue = $LANG_POLLS['poll']; + break; + } + break; + + case 'sid': + $what = 'title,url'; + + switch ($A['type']) { + case 'article': + list($title, $url) = plugin_getiteminfo_story($fieldValue, $what); + break; + + case 'staticpages': + list($title, $url) = plugin_getiteminfo_staticpages($fieldValue, $what); + break; + + case 'polls': + list($title, $url) = plugin_getiteminfo_polls($fieldValue, $what); + break; + } + + $fieldValue = '' . htmlspecialchars($title, ENT_QUOTES, $encoding) . ''; + break; + + case 'title': + $fieldValue = '' + . htmlspecialchars($fieldValue, ENT_QUOTES, $encoding) . ''; + break; + + case 'comment': + $fieldValue = htmlspecialchars($fieldValue, ENT_QUOTES, $encoding); + break; + + case 'uid': + $uid = intval($fieldValue, 10); + $userName = trim($A['name']); + $fieldValue = COM_getDisplayName($uid, $userName); + $fieldValue = htmlspecialchars($fieldValue, ENT_QUOTES, $encoding); + + if ($uid > 1) { + $fieldValue = '' . $fieldValue . ''; + } + + break; + + case 'ipaddress': + $fieldValue = htmlspecialchars($fieldValue, ENT_QUOTES, $encoding); + break; + default: break; } @@ -124,9 +213,10 @@ function getTypeSelector($itemType) */ function listComments() { - global $_CONF, $_PLUGINS, $_SCRIPTS, $_TABLES, $_FINPUT, $LANG_ADMIN, $LANG03, $_IMAGE_TYPE, $securityToken; + global $_CONF, $_PLUGINS, $_SCRIPTS, $_TABLES, $_FINPUT, $LANG_ADMIN, $LANG01, $LANG03, $LANG28, $LANG29, $_IMAGE_TYPE, $securityToken; require_once $_CONF['path_system'] . 'lib-admin.php'; + require_once $_CONF['path_system'] . 'lib-story.php'; $securityToken = SEC_createToken(); @@ -142,20 +232,51 @@ function listComments() . ADMIN_createMenu( $menu_arr, $LANG03[100], - $_CONF['layout_url'] . '/images/icons/trackback.' . $_IMAGE_TYPE + $_CONF['layout_url'] . '/images/icons/comment.' . $_IMAGE_TYPE ); // Regular Comments $headerArray = array( # display 'text' and use table field 'field' - array('text' => '', 'field' => 'edit', 'sort' => false), - array('text' => 'Type', 'field' => 'type', 'sort' => true), - array('text' => 'Sid', 'field' => 'sid', 'sort' => true), - array('text' => 'Date', 'field' => 'date', 'sort' => true), - array('text' => $LANG_ADMIN['title'], 'field' => 'title', 'sort' => true), - array('text' => 'Comment', 'field' => 'comment', 'sort' => true), - array('text' => 'Name', 'field' => 'name', 'sort' => true), - array('text' => 'Uid', 'field' => 'uid', 'sort' => true), - array('text' => 'IP Address', 'field' => 'ipaddress', 'sort' => true), + array( + 'text' => '', + 'field' => 'edit', + 'sort' => false, + ), + array( + 'text' => $LANG_ADMIN['type'], + 'field' => 'type', + 'sort' => true, + ), + array( + 'text' => $LANG29[36], + 'field' => 'sid', + 'sort' => true, + ), + array( + 'text' => $LANG29[14], + 'field' => 'date', + 'sort' => true, + ), + array( + 'text' => $LANG_ADMIN['title'], + 'field' => 'title', + 'sort' => true, + ), + array( + 'text' => $LANG03[9], + 'field' => 'comment', + 'sort' => true, + ), + array( + 'text' => $LANG28[3], + 'field' => 'uid', + 'sort' => true, + ), + array( + 'text' => $LANG03[105], + 'field' => 'ipaddress', + 'sort' => true, + ), ); $defaultSortArray = array('field' => 'date', 'direction' => 'desc'); @@ -199,21 +320,30 @@ function listComments() $queryArray = array( 'table' => 'comments', 'sql' => "SELECT * FROM {$_TABLES['comments']} WHERE (1 = 1) ", - 'query_fields' => array('type', 'sid', 'date', 'title', 'comment', 'name', 'uid', 'ipaddress'), + 'query_fields' => array('type', 'sid', 'date', 'title', 'comment', 'uid', 'ipaddress'), 'default_filter' => $sqlForType . COM_getPermSql('AND'), ); $filter = getTypeSelector($itemType); $options = array(); - $actionSelector = '' . LB - . ''; + $actionSelector = '' . LB + . '' . LB; + $securityTokenTag = '' . LB; $formArray = array( 'top' => '', - 'bottom' => $actionSelector, + 'bottom' => $actionSelector . $securityTokenTag, ); $retval .= ADMIN_list( @@ -222,10 +352,8 @@ function listComments() ); $retval .= COM_endBlock(COM_getBlockTemplate('_admin_block', 'footer')); - $_SCRIPTS->setJavaScript('jQuery("#select_all").on("change", function (e) { - jQuery("input[name=cids\[\]]").checked = e.target.checked; - }); - '); + $_SCRIPTS->setJavaScriptFile('comment', '/javascript/comment.js', true); + return $retval; } @@ -234,7 +362,7 @@ function listComments() * * @return string HTML redirect or error message */ -function deleteComment() +function deleteComments() { global $_CONF, $_TABLES, $_USER, $_FINPUT, $LANG03; @@ -269,42 +397,114 @@ function deleteComment() /** * Approve a comment - * - * @param int $cid id of comment to delete - * @return string HTML redirect or error message */ -function approveComment($cid = 0) +function approveComments() { - $cid = intval($cid, 10); - CMT_approveModeration($cid); + $cids = getCids(); + + if (count($cids) > 0) { + foreach ($cids as $cid) { + CMT_approveModeration($cid); + } + } +} + +/** + * Ban users + */ +function banUsers() { + global $_TABLES, $_USER; + + $cids = getCids(); + + if (count($cids) > 0) { + $currentUid = $_USER['uid']; + $sql = "SELECT uid FROM {$_TABLES['comments']} " + . "WHERE (uid <> 1) AND (uid <> {$currentUid}) AND " + . " (cid IN (" . implode(',', $cids) . "))"; + $result = DB_query($sql); + $uids = array(); + + while (($A = DB_fetchArray($result, false)) !== false) { + $uids[] = $A['uid']; + } + + if (count($uids) > 0) { + $sql = "UPDATE {$_TABLES['users']} SET status = " . USER_ACCOUNT_DISABLED + . " WHERE (uid IN (" . implode(',', $uids) . "))"; + DB_query($sql); + } + } +} + +/** + * Ban IP Addresses being selected with the Spamx plugin + * + * @return bool true = success, false = otherwise + */ +function banIpAddresses() { + global $_PLUGINS, $_TABLES; + + $retval = false; + + if (!in_array('spamx', $_PLUGINS)) { + COM_errorLog(__FUNCTION__ . ': Spmax plugin is not installed or disabled.'); + return $retval; + } + + $cids = getCids(); + + if (count($cids) > 0) { + $sql = "SELECT DISTINCT ipaddress FROM {$_TABLES['comments']} " + . "WHERE (ipaddress NOT LIKE '192.168.%') AND (ipaddress <> '::1') AND " + . " (cid IN (" . implode(',', $cids) . "))"; + $result = DB_query($sql); + + if (!DB_error()) { + $ipAddresses = array(); + + while (($A = DB_fetchArray($result, false)) !== false) { + $ipAddresses[] = $A['ipaddress']; + } + + foreach ($ipAddresses as $ipAddress) { + $sql = "INSERT INTO {$_TABLES['spamx']} (name, value) " + . "VALUES ('IP', '" . DB_escapeString($ipAddress) . "')"; + DB_query($sql); + } + + $retval = true; + } + } + + return $retval; } // MAIN -$mode = $_FINPUT->get('mode', $_INPUT->post('mode', '')); -$cid = $_FINPUT->get('cid' . $_FINPUT->post('cid', 0)); -$cid = intval($cid, 10); +$action = $_FINPUT->post('bulk_action', ''); -switch ($mode) { - case $LANG_ADMIN['delete']: - deleteComment(); +switch ($action) { + case 'bulk_approve': + approveComments(); break; - case $LANG_ADMIN['approve']: - $content = ''; + case 'bulk_delete': + deleteComments(); + break; - if (SEC_checkToken()) { - $content = approveComment($cid); - } + case 'bulk_ban_user': + banUsers(); + break; - $content .= listComments(); - $display = COM_createHTMLDocument($content, array('pagetitle' => $LANG21[19])); + case 'bulk_ban_ip_address': + banIpAddresses(); break; - default:// 'cancel' or no mode at all - $content = COM_showMessageFromParameter() - . listComments(); - $display = COM_createHTMLDocument($content, array('pagetitle' => $LANG21[19])); + default: + // Do nothing here break; } +$content = COM_showMessageFromParameter() . listComments(); +$display = COM_createHTMLDocument($content, array('pagetitle' => $LANG21[19])); COM_output($display); diff --git a/public_html/javascript/comment.js b/public_html/javascript/comment.js new file mode 100644 index 000000000..85c97eec8 --- /dev/null +++ b/public_html/javascript/comment.js @@ -0,0 +1,34 @@ +(function ($) { + var selectAll = $("#select_all"), + bulkActionSelector = $("#bulk_action")[0], + bulkActionSubmit = $("#bulk_action_submit")[0], + checkBoxes = $("input[name='cids\[\]']"); + + bulkActionSelector.disabled = true; + bulkActionSubmit.disabled = true; + + selectAll.on("click", function (e) { + var selectorState = e.target.checked; + + checkBoxes.each(function () { + this.checked = selectorState; + }); + checkBoxes.triggerHandler("change"); + }); + + checkBoxes.on("change", function () { + var i, len, elm, checked = false; + + for (i = 0, len = checkBoxes.length; i < len; i++) { + checked = checked || checkBoxes[i].checked; + + if (checked === true) { + break; + } + } + + bulkActionSelector.disabled = !checked; + bulkActionSubmit.disabled = !checked; + selectAll[0].checked = checked; + }); +})(jQuery); diff --git a/public_html/layout/denim/images/icons/comment.png b/public_html/layout/denim/images/icons/comment.png new file mode 100644 index 0000000000000000000000000000000000000000..df5801e170686bc7270cf0ce36aae2aed3223fef GIT binary patch literal 2925 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 $LANG01[38], 'num' => $syndicationcount, 'image' => $_CONF['layout_url'] . '/images/icons/syndication.' . $_IMAGE_TYPE), + array('condition' => SEC_hasRights('comment.moderate'), + 'url' => $_CONF['site_admin_url'] . '/comment.php', + 'lang' => $LANG01[83], + 'num' => $commentCount, + 'image' => $_CONF['layout_url'] . '/images/icons/comment.' . $_IMAGE_TYPE), array('condition' => $showTrackbackIcon, 'url' => $_CONF['site_admin_url'] . '/trackback.php', 'lang' => $LANG01[116], From a8efab95122cf292971b7370ffcc40f8d4aca4b9 Mon Sep 17 00:00:00 2001 From: Kenji ITO Date: Wed, 3 Feb 2016 14:35:01 +0900 Subject: [PATCH 14/81] Added a list of comment submissions --- language/english.php | 2 +- language/japanese_utf-8.php | 2 +- public_html/admin/comment.php | 63 +++++++++++++++++++++++++++++++---- 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/language/english.php b/language/english.php index 12e72f49d..62d24378d 100644 --- a/language/english.php +++ b/language/english.php @@ -244,7 +244,7 @@ 44 => 'Your Name', 45 => "Thank you for your comment. You must wait {$_CONF['commentspeedlimit']} seconds to post a new comment.", 100 => 'Comment Manager', - 101 => 'Could not delete a comment,', + 101 => 'Existing Comments', 102 => 'Bulk Action', 103 => 'Ban this user', 104 => 'Ban this IP address with the Spamx plugin', diff --git a/language/japanese_utf-8.php b/language/japanese_utf-8.php index 5dc022716..18f47c4b5 100644 --- a/language/japanese_utf-8.php +++ b/language/japanese_utf-8.php @@ -252,7 +252,7 @@ 44 => 'あなたの名前', 45 => "コメントをありがとうございます。 新たなコメント投稿のために {$_CONF['commentspeedlimit']} 秒以上お待ちください。", 100 => 'コメント管理', - 101 => 'コメントを削除できませんでした。', + 101 => '既存のコメント', 102 => '一括操作', 103 => 'このユーザーを禁止する', 104 => 'このIPアドレスをSpamxプラグインで禁止する', diff --git a/public_html/admin/comment.php b/public_html/admin/comment.php index 388e972b0..acafed8c3 100644 --- a/public_html/admin/comment.php +++ b/public_html/admin/comment.php @@ -86,7 +86,7 @@ function getCids() { */ function ADMIN_getListField_comments($fieldName, $fieldValue, $A, $iconArray, $extra = '') { - global $_CONF, $_TABLES, $LANG_ADMIN, $LANG01, $LANG03, $LANG_STATIC, $LANG_POLLS, $_IMAGE_TYPE, $securityToken; + global $_CONF, $LANG01, $LANG_STATIC, $LANG_POLLS; static $encoding = null; if ($encoding === null) { @@ -98,11 +98,19 @@ function ADMIN_getListField_comments($fieldName, $fieldValue, $A, $iconArray, $e } switch ($fieldName) { - case 'edit': + case 'selector': $cid = $A['cid']; $fieldValue = ''; break; + case 'edit': + $cid = $A['cid']; + $fieldValue = '' + . $iconArray['edit'] . ''; + break; + case 'type': switch ($fieldValue) { case 'article': @@ -145,7 +153,7 @@ function ADMIN_getListField_comments($fieldName, $fieldValue, $A, $iconArray, $e break; case 'comment': - $fieldValue = htmlspecialchars($fieldValue, ENT_QUOTES, $encoding); +// $fieldValue = htmlspecialchars($fieldValue, ENT_QUOTES, $encoding); break; case 'uid': @@ -172,6 +180,22 @@ function ADMIN_getListField_comments($fieldName, $fieldValue, $A, $iconArray, $e return $fieldValue; } +/** + * Field function + * + * @param string $fieldName + * @param string $fieldValue + * @param array $A + * @param array $iconArray + * @param string $extra + * @return string + * @throws Exception + */ +function ADMIN_getListField_commentSubmissions($fieldName, $fieldValue, $A, $iconArray, $extra = '') +{ + return ADMIN_getListField_comments($fieldName, $fieldValue, $A, $iconArray, $extra); +} + /** * Return a selector to filter item type * @@ -239,6 +263,11 @@ function listComments() $headerArray = array( # display 'text' and use table field 'field' array( 'text' => '', + 'field' => 'selector', + 'sort' => false, + ), + array( + 'text' => $LANG01[4], 'field' => 'edit', 'sort' => false, ), @@ -283,7 +312,7 @@ function listComments() $textArray = array( 'has_extras' => true, - 'title' => $LANG03[100], + 'title' => $LANG03[101], 'form_url' => $_CONF['site_admin_url'] . '/comment.php', ); @@ -346,12 +375,32 @@ function listComments() 'bottom' => $actionSelector . $securityTokenTag, ); - $retval .= ADMIN_list( + $commentList = ADMIN_list( 'comments', 'ADMIN_getListField_comments', $headerArray, $textArray, $queryArray, $defaultSortArray, $filter, $securityToken, $options, $formArray ); - $retval .= COM_endBlock(COM_getBlockTemplate('_admin_block', 'footer')); + // Comment submissions + $textArray = array( + 'has_extras' => true, + 'title' => $LANG29[41], + 'form_url' => $_CONF['site_admin_url'] . '/comment.php', + ); + + $queryArray = array( + 'table' => 'commentsubmissions', + 'sql' => "SELECT * FROM {$_TABLES['commentsubmissions']} WHERE (1 = 1) ", + 'query_fields' => array('type', 'sid', 'date', 'title', 'comment', 'uid', 'ipaddress'), + 'default_filter' => $sqlForType . COM_getPermSql('AND'), + ); + + $submissionList = ADMIN_list( + 'comments', 'ADMIN_getListField_commentSubmissions', $headerArray, $textArray, + $queryArray, $defaultSortArray, $filter, $securityToken, $options, $formArray + ); + + $retval .= $submissionList . $commentList + . COM_endBlock(COM_getBlockTemplate('_admin_block', 'footer')); $_SCRIPTS->setJavaScriptFile('comment', '/javascript/comment.js', true); return $retval; @@ -506,5 +555,5 @@ function banIpAddresses() { } $content = COM_showMessageFromParameter() . listComments(); -$display = COM_createHTMLDocument($content, array('pagetitle' => $LANG21[19])); +$display = COM_createHTMLDocument($content, array('pagetitle' => $LANG03[100])); COM_output($display); From 82c0f604fb4357b81258c38a1f29578c88c65014 Mon Sep 17 00:00:00 2001 From: Kenji ITO Date: Sat, 6 Feb 2016 17:10:00 +0900 Subject: [PATCH 15/81] Added languate data --- language/english.php | 4 + language/japanese_utf-8.php | 4 + public_html/admin/comment.php | 398 +++++++++++++++++------------- public_html/javascript/comment.js | 18 +- 4 files changed, 242 insertions(+), 182 deletions(-) diff --git a/language/english.php b/language/english.php index 62d24378d..0bde69c99 100644 --- a/language/english.php +++ b/language/english.php @@ -1461,6 +1461,10 @@ 119 => 'More..', 120 => 'Send this?', 130 => 'Deleted comment(s).', + 131 => 'Failed to delete a comment.', + 132 => 'Approved comment(s).', + 133 => 'Banned user(s).', + 134 => 'Banned IP addresses with the Spamx plugin.', 400 => 'Not all required fields have been passed validation', // Error codes in the 400 range reserved for CUSTOM membership 401 => 'Please enter Fullname', 500 => 'The Template Cache has been successfully cleared.' diff --git a/language/japanese_utf-8.php b/language/japanese_utf-8.php index 18f47c4b5..e2361a2e0 100644 --- a/language/japanese_utf-8.php +++ b/language/japanese_utf-8.php @@ -1460,6 +1460,10 @@ 119 => 'More..', 120 => 'この項目を送信しますか?', 130 => 'コメントを削除しました。', + 131 => 'コメントを削除できませんでした。', + 132 => 'コメントを承認しました。', + 133 => 'ユーザーを禁止しました。', + 134 => 'IPアドレスをSpamxプラグインで禁止しました。', 400 => '検証に通っていない必須のフィールドがあります。', 401 => '氏名を入力してください。', 500 => 'テンプレートのキャッシュを削除しました。' diff --git a/public_html/admin/comment.php b/public_html/admin/comment.php index acafed8c3..d94d6101e 100644 --- a/public_html/admin/comment.php +++ b/public_html/admin/comment.php @@ -39,6 +39,9 @@ * Comment administration page: Moderate, edit, delete, comments for your Geeklog site. */ +define('SUFFIX_COMMENTS', '_comments'); +define('SUFFIX_COMMENT_SUBMISSIONS', '_submissions'); + // Geeklog common function library require_once '../lib-common.php'; @@ -53,24 +56,28 @@ exit; } -// Comment library +// Include system libraries +require_once $_CONF['path_system'] . 'lib-admin.php'; +require_once $_CONF['path_system'] . 'lib-story.php'; require_once $_CONF['path_system'] . 'lib-comment.php'; /** * Return comment IDs being selected in the list * + * @param string $suffix * @return array of int */ -function getCids() { +function getCommentIds($suffix) +{ global $_FINPUT; - $cids = $_FINPUT->post('cids', array()); + $commentIds = $_FINPUT->post('cids' . $suffix, array()); - if (count($cids) > 0) { - $cids = array_map('intval', $cids); + if (count($commentIds) > 0) { + $commentIds = array_map('intval', $commentIds); } - return $cids; + return $commentIds; } /** @@ -80,11 +87,11 @@ function getCids() { * @param string $fieldValue * @param array $A * @param array $iconArray - * @param string $extra + * @param string $suffix * @return string * @throws Exception */ -function ADMIN_getListField_comments($fieldName, $fieldValue, $A, $iconArray, $extra = '') +function ADMIN_getListField_comments($fieldName, $fieldValue, $A, $iconArray, $suffix) { global $_CONF, $LANG01, $LANG_STATIC, $LANG_POLLS; static $encoding = null; @@ -97,17 +104,25 @@ function ADMIN_getListField_comments($fieldName, $fieldValue, $A, $iconArray, $e throw new Exception(__FUNCTION__ . ': unknown type "' . $A['type'] . '" was given'); } + $commentId = $A['cid']; + switch ($fieldName) { case 'selector': - $cid = $A['cid']; - $fieldValue = ''; + $fieldValue = ''; break; case 'edit': - $cid = $A['cid']; - $fieldValue = '' + if ($suffix === SUFFIX_COMMENTS) { + $link = $_CONF['site_url'] . '/comment.php?mode=edit&cid=' + . htmlspecialchars($commentId, ENT_QUOTES, $encoding) + . '&sid=' . $A['sid'] . '&type=' . $A['type']; + } else { + $link = $_CONF['site_url'] . '/comment.php?mode=editsubmission&cid=' + . htmlspecialchars($commentId, ENT_QUOTES, $encoding); + } + + $fieldValue = '' . $iconArray['edit'] . ''; break; @@ -144,27 +159,28 @@ function ADMIN_getListField_comments($fieldName, $fieldValue, $A, $iconArray, $e break; } - $fieldValue = '' . htmlspecialchars($title, ENT_QUOTES, $encoding) . ''; + $fieldValue = '' + . htmlspecialchars($title, ENT_QUOTES, $encoding) . ''; break; case 'title': - $fieldValue = '' + $fieldValue = '' . htmlspecialchars($fieldValue, ENT_QUOTES, $encoding) . ''; break; case 'comment': -// $fieldValue = htmlspecialchars($fieldValue, ENT_QUOTES, $encoding); break; case 'uid': - $uid = intval($fieldValue, 10); + $userId = intval($fieldValue, 10); $userName = trim($A['name']); - $fieldValue = COM_getDisplayName($uid, $userName); + $fieldValue = COM_getDisplayName($userId, $userName); $fieldValue = htmlspecialchars($fieldValue, ENT_QUOTES, $encoding); - if ($uid > 1) { + if ($userId > 1) { $fieldValue = '' . $fieldValue . ''; + . '/users.php?mode=profile&uid=' . $userId . '">' . $fieldValue . ''; } break; @@ -180,22 +196,6 @@ function ADMIN_getListField_comments($fieldName, $fieldValue, $A, $iconArray, $e return $fieldValue; } -/** - * Field function - * - * @param string $fieldName - * @param string $fieldValue - * @param array $A - * @param array $iconArray - * @param string $extra - * @return string - * @throws Exception - */ -function ADMIN_getListField_commentSubmissions($fieldName, $fieldValue, $A, $iconArray, $extra = '') -{ - return ADMIN_getListField_comments($fieldName, $fieldValue, $A, $iconArray, $extra); -} - /** * Return a selector to filter item type * @@ -204,7 +204,7 @@ function ADMIN_getListField_commentSubmissions($fieldName, $fieldValue, $A, $ico */ function getTypeSelector($itemType) { - global $_PLUGINS, $_FINPUT, $LANG_ADMIN, $LANG09, $LANG_STATIC, $LANG_POLLS; + global $_PLUGINS, $LANG_ADMIN, $LANG09, $LANG_STATIC, $LANG_POLLS; $retval = $LANG_ADMIN['type'] . ': ', + 'text' => '', 'field' => 'selector', 'sort' => false, ), @@ -309,10 +291,9 @@ function listComments() ); $defaultSortArray = array('field' => 'date', 'direction' => 'desc'); - $textArray = array( 'has_extras' => true, - 'title' => $LANG03[101], + 'title' => ($suffix === SUFFIX_COMMENTS ? $LANG03[101] : $LANG29[41]), 'form_url' => $_CONF['site_admin_url'] . '/comment.php', ); @@ -347,18 +328,22 @@ function listComments() } $queryArray = array( - 'table' => 'comments', - 'sql' => "SELECT * FROM {$_TABLES['comments']} WHERE (1 = 1) ", + 'table' => $tableName, + 'sql' => "SELECT * FROM " . $_TABLES[$tableName] . " WHERE (1 = 1) ", 'query_fields' => array('type', 'sid', 'date', 'title', 'comment', 'uid', 'ipaddress'), 'default_filter' => $sqlForType . COM_getPermSql('AND'), ); $filter = getTypeSelector($itemType); $options = array(); - $actionSelector = '' . LB + . '' . LB; + + if ($suffix === SUFFIX_COMMENT_SUBMISSIONS) { + $actionSelector .= '' . LB; + } + + $actionSelector .= '' . LB . '' . LB; if (in_array('spamx', $_PLUGINS)) { @@ -366,8 +351,9 @@ function listComments() } $actionSelector .= '' . LB - . '' . LB; + . '' . LB + . '' . LB; $securityTokenTag = '' . LB; $formArray = array( @@ -377,29 +363,39 @@ function listComments() $commentList = ADMIN_list( 'comments', 'ADMIN_getListField_comments', $headerArray, $textArray, - $queryArray, $defaultSortArray, $filter, $securityToken, $options, $formArray + $queryArray, $defaultSortArray, $filter, $suffix, $options, $formArray ); - // Comment submissions - $textArray = array( - 'has_extras' => true, - 'title' => $LANG29[41], - 'form_url' => $_CONF['site_admin_url'] . '/comment.php', - ); + return $commentList; +} - $queryArray = array( - 'table' => 'commentsubmissions', - 'sql' => "SELECT * FROM {$_TABLES['commentsubmissions']} WHERE (1 = 1) ", - 'query_fields' => array('type', 'sid', 'date', 'title', 'comment', 'uid', 'ipaddress'), - 'default_filter' => $sqlForType . COM_getPermSql('AND'), - ); +/** + * Display two lists of comments, ordinary comments and submissions + * + * @return string HTML for the two lists + */ +function listComments() +{ + global $_CONF, $_SCRIPTS, $LANG_ADMIN, $LANG03, $_IMAGE_TYPE; - $submissionList = ADMIN_list( - 'comments', 'ADMIN_getListField_commentSubmissions', $headerArray, $textArray, - $queryArray, $defaultSortArray, $filter, $securityToken, $options, $formArray - ); + // Create a security token to be used in both lists + $securityToken = SEC_createToken(); - $retval .= $submissionList . $commentList + // Writing the menu on top + $menu_arr = array( + array( + 'url' => $_CONF['site_admin_url'], + 'text' => $LANG_ADMIN['admin_home'], + ), + ); + $retval = COM_startBlock($LANG03[100], '', COM_getBlockTemplate('_admin_block', 'header')) + . ADMIN_createMenu( + $menu_arr, + $LANG03[100], + $_CONF['layout_url'] . '/images/icons/comment.' . $_IMAGE_TYPE + ) + . ADMIN_buildCommentList(SUFFIX_COMMENT_SUBMISSIONS, 'commentsubmissions', $securityToken) + . ADMIN_buildCommentList(SUFFIX_COMMENTS, 'comments', $securityToken) . COM_endBlock(COM_getBlockTemplate('_admin_block', 'footer')); $_SCRIPTS->setJavaScriptFile('comment', '/javascript/comment.js', true); @@ -409,144 +405,196 @@ function listComments() /** * Delete a comment * - * @return string HTML redirect or error message + * @param string $suffix */ -function deleteComments() +function deleteComments($suffix) { - global $_CONF, $_TABLES, $_USER, $_FINPUT, $LANG03; - - $cid = $_FINPUT->get('cid', $_FINPUT->post('cid', 0)); - $cid = intval($cid, 10); - $sid = $_FINPUT->post('sid', ''); - $type = $_FINPUT->post('type', ''); - - $retval = ''; - - if (($cid <= 0) || ($sid === '') || ($type === '')) { - COM_errorLog("Attempted to delete a nonexistent comment (cid = {$cid})"); - $retval = COM_refresh($_CONF['site_admin_url'] . '/comment.php'); - } elseif (!SEC_checkToken()) { - COM_accessLog("User {$_USER['username']} tried to delete comment (cid = {$cid}) and failed CSRF checks."); - $retval = COM_refresh($_CONF['site_admin_url'] . '/index.php'); - } else { - if (DB_count($_TABLES['comments'], array('cid', 'sid', 'type'), array($cid, $sid, $type)) == 1) { - $result = CMT_deleteComment($cid, $sid, $type); - - if ($result == 0) { - $retval = COM_refresh($_CONF['site_admin_url'] . '/comment.php?msg=130'); + global $_CONF, $_TABLES, $_USER; + + $commentIds = getCommentIds($suffix); + + if (SEC_checkToken()) { + if (count($commentIds) > 0) { + foreach ($commentIds as $commentId) { + if ($commentId <= 0) { + COM_errorLog("Attempted to delete a nonexistent comment (cid = {$commentId})"); + } else { + if ($suffix === SUFFIX_COMMENTS) { + $sql = "SELECT sid, type FROM {$_TABLES['comments']} WHERE cid = " . DB_escapeString($commentId); + $result = DB_query($sql); + + if (!DB_error()) { + $A = DB_fetchArray($result, false); + + if (is_array($A) && (count($A) > 0)) { + $sid = $A['sid']; + $type = $A['type']; + + if (CMT_deleteComment($commentId, $sid, $type) > 0) { + COM_errorLog("Attempted to delete a nonexistent comment (cid = {$commentId})"); + } + } + } + } elseif ($suffix === SUFFIX_COMMENT_SUBMISSIONS) { + $sql = "DELETE FROM {$_TABLES['commentsubmissions']} " + . "WHERE cid = " . DB_escapeString($commentId); + DB_query($sql); + } + } } - } else { - // Failed to delete a comment - $retval = $LANG03[101]; + + echo COM_refresh($_CONF['site_admin_url'] . '/comment.php?msg=130'); + exit; } + } else { + COM_accessLog("User {$_USER['username']} tried to delete comments (cid = " . implode(', ', $commentIds) . ") and failed CSRF checks."); + echo COM_refresh($_CONF['site_admin_url'] . '/index.php'); + exit; } - - return $retval; } /** * Approve a comment + * + * @param string $suffix */ -function approveComments() +function approveComments($suffix) { - $cids = getCids(); + global $_CONF, $_USER; - if (count($cids) > 0) { - foreach ($cids as $cid) { - CMT_approveModeration($cid); + $commentIds = getCommentIds($suffix); + + if (SEC_checkToken()) { + if (count($commentIds) > 0) { + foreach ($commentIds as $commentId) { + CMT_approveModeration($commentId); + } } + + echo COM_refresh($_CONF['site_admin_url'] . '/comment.php?msg=132'); + exit; + } else { + COM_accessLog("User {$_USER['username']} tried to approve comments (cid = " . implode(', ', $commentIds) . ") and failed CSRF checks."); + echo COM_refresh($_CONF['site_admin_url'] . '/index.php'); + exit; } } /** * Ban users + * + * @param string $suffix */ -function banUsers() { - global $_TABLES, $_USER; +function banUsers($suffix) +{ + global $_CONF, $_TABLES, $_USER; - $cids = getCids(); + $getCommentIds = getCommentIds($suffix); - if (count($cids) > 0) { - $currentUid = $_USER['uid']; - $sql = "SELECT uid FROM {$_TABLES['comments']} " - . "WHERE (uid <> 1) AND (uid <> {$currentUid}) AND " - . " (cid IN (" . implode(',', $cids) . "))"; - $result = DB_query($sql); - $uids = array(); + if (SEC_checkToken()) { + if (count($getCommentIds) > 0) { + $currentUserId = $_USER['uid']; + $sql = "SELECT DISTINCT uid FROM {$_TABLES['comments']} " + . "WHERE (uid <> 1) AND (uid <> {$currentUserId}) AND " + . " (cid IN (" . implode(',', $getCommentIds) . "))"; + $result = DB_query($sql); + $userIds = array(); - while (($A = DB_fetchArray($result, false)) !== false) { - $uids[] = $A['uid']; - } + while (($A = DB_fetchArray($result, false)) !== false) { + $userIds[] = $A['uid']; + } - if (count($uids) > 0) { - $sql = "UPDATE {$_TABLES['users']} SET status = " . USER_ACCOUNT_DISABLED - . " WHERE (uid IN (" . implode(',', $uids) . "))"; - DB_query($sql); + if (count($userIds) > 0) { + $sql = "UPDATE {$_TABLES['users']} SET status = " . USER_ACCOUNT_DISABLED + . " WHERE (uid IN (" . implode(',', $userIds) . "))"; + DB_query($sql); + echo COM_refresh($_CONF['site_admin_url'] . '/comment.php?msg=133'); + exit; + } } + } else { + COM_accessLog("User {$_USER['username']} tried to ban users and failed CSRF checks."); + echo COM_refresh($_CONF['site_admin_url'] . '/index.php'); + exit; } } /** * Ban IP Addresses being selected with the Spamx plugin * - * @return bool true = success, false = otherwise + * @param string $suffix */ -function banIpAddresses() { - global $_PLUGINS, $_TABLES; +function banIpAddresses($suffix) +{ + global $_CONF, $_PLUGINS, $_TABLES, $_USER; - $retval = false; + if (SEC_checkToken()) { + if (!in_array('spamx', $_PLUGINS)) { + COM_errorLog(__FUNCTION__ . ': Spmax plugin is not installed or disabled.'); + echo COM_refresh($_CONF['site_admin_url'] . '/index.php'); + exit; + } - if (!in_array('spamx', $_PLUGINS)) { - COM_errorLog(__FUNCTION__ . ': Spmax plugin is not installed or disabled.'); - return $retval; - } + $getCommentIds = getCommentIds($suffix); - $cids = getCids(); + if (count($getCommentIds) > 0) { + $sql = "SELECT DISTINCT ipaddress FROM {$_TABLES['comments']} " + . "WHERE (ipaddress NOT LIKE '192.168.%') AND (ipaddress <> '::1') AND " + . " (cid IN (" . implode(',', $getCommentIds) . "))"; + $result = DB_query($sql); - if (count($cids) > 0) { - $sql = "SELECT DISTINCT ipaddress FROM {$_TABLES['comments']} " - . "WHERE (ipaddress NOT LIKE '192.168.%') AND (ipaddress <> '::1') AND " - . " (cid IN (" . implode(',', $cids) . "))"; - $result = DB_query($sql); + if (!DB_error()) { + $ipAddresses = array(); - if (!DB_error()) { - $ipAddresses = array(); + while (($A = DB_fetchArray($result, false)) !== false) { + $ipAddresses[] = $A['ipaddress']; + } - while (($A = DB_fetchArray($result, false)) !== false) { - $ipAddresses[] = $A['ipaddress']; + foreach ($ipAddresses as $ipAddress) { + $sql = "INSERT INTO {$_TABLES['spamx']} (name, value) " + . "VALUES ('IP', '" . DB_escapeString($ipAddress) . "')"; + DB_query($sql); + } } - foreach ($ipAddresses as $ipAddress) { - $sql = "INSERT INTO {$_TABLES['spamx']} (name, value) " - . "VALUES ('IP', '" . DB_escapeString($ipAddress) . "')"; - DB_query($sql); - } - - $retval = true; + echo COM_refresh($_CONF['site_admin_url'] . '/comment.php?msg=134'); + exit; } + } else { + COM_accessLog("User {$_USER['username']} tried to ban IP addresses and failed CSRF checks."); + echo COM_refresh($_CONF['site_admin_url'] . '/index.php'); + exit; } - - return $retval; } // MAIN -$action = $_FINPUT->post('bulk_action', ''); +$list = $_FINPUT->post('list', ''); + +if ($list === SUFFIX_COMMENTS) { + $suffix = SUFFIX_COMMENTS; +} elseif ($list === SUFFIX_COMMENT_SUBMISSIONS) { + $suffix = SUFFIX_COMMENT_SUBMISSIONS; +} else { + $suffix = ''; +} + +$action = $_FINPUT->post('bulk_action' . $suffix, ''); switch ($action) { case 'bulk_approve': - approveComments(); + approveComments($suffix); break; case 'bulk_delete': - deleteComments(); + deleteComments($suffix); break; case 'bulk_ban_user': - banUsers(); + banUsers($suffix); break; case 'bulk_ban_ip_address': - banIpAddresses(); + banIpAddresses($suffix); break; default: diff --git a/public_html/javascript/comment.js b/public_html/javascript/comment.js index 85c97eec8..f1cf8ccb8 100644 --- a/public_html/javascript/comment.js +++ b/public_html/javascript/comment.js @@ -1,9 +1,10 @@ -(function ($) { - var selectAll = $("#select_all"), - bulkActionSelector = $("#bulk_action")[0], - bulkActionSubmit = $("#bulk_action_submit")[0], - checkBoxes = $("input[name='cids\[\]']"); +var adminCommentList = function (SUFFIX) { + var selectAll = jQuery("#select_all" + SUFFIX), + bulkActionSelector = jQuery("#bulk_action" + SUFFIX)[0], + bulkActionSubmit = jQuery("#bulk_action_submit" + SUFFIX)[0], + checkBoxes = jQuery("input[name='cids" + SUFFIX + "\[\]']"); + // Initialize bulkActionSelector.disabled = true; bulkActionSubmit.disabled = true; @@ -17,7 +18,7 @@ }); checkBoxes.on("change", function () { - var i, len, elm, checked = false; + var i, len, checked = false; for (i = 0, len = checkBoxes.length; i < len; i++) { checked = checked || checkBoxes[i].checked; @@ -31,4 +32,7 @@ bulkActionSubmit.disabled = !checked; selectAll[0].checked = checked; }); -})(jQuery); +}; + +var adminCommentListComments = new adminCommentList("_comments"), + adminCommentListSubmissions = new adminCommentList("_submissions"); From cdcae4bec1163e89a7bba324316be38a43f9c187 Mon Sep 17 00:00:00 2001 From: Kenji ITO Date: Sat, 6 Feb 2016 21:48:36 +0900 Subject: [PATCH 16/81] Updated english_utf-8.php to sync with english.php --- language/english_utf-8.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/language/english_utf-8.php b/language/english_utf-8.php index 239ef5407..e193edc01 100644 --- a/language/english_utf-8.php +++ b/language/english_utf-8.php @@ -242,7 +242,13 @@ 42 => 'Unsubscribe', 43 => 'Unsubscribe from reply notifications', 44 => 'Your Name', - 45 => "Thank you for your comment. You must wait {$_CONF['commentspeedlimit']} seconds to post a new comment." + 45 => "Thank you for your comment. You must wait {$_CONF['commentspeedlimit']} seconds to post a new comment.", + 100 => 'Comment Manager', + 101 => 'Existing Comments', + 102 => 'Bulk Action', + 103 => 'Ban this user', + 104 => 'Ban this IP address with the Spamx plugin', + 105 => 'IP Address', ); ############################################################################### @@ -1454,6 +1460,11 @@ 118 => 'Click to select a date', 119 => 'More..', 120 => 'Send this?', + 130 => 'Deleted comment(s).', + 131 => 'Failed to delete a comment.', + 132 => 'Approved comment(s).', + 133 => 'Banned user(s).', + 134 => 'Banned IP addresses with the Spamx plugin.', 400 => 'Not all required fields have been passed validation', // Error codes in the 400 range reserved for CUSTOM membership 401 => 'Please enter Fullname', 500 => 'The Template Cache has been successfully cleared.' @@ -1824,7 +1835,8 @@ 'token_expiry' => 'You have until %s to make changes. After that time, the security token embedded into this page will expire and you will lose your changes.', 'token_expired' => 'The security token for this operation has expired. Please authenticate again to continue.', 'reauth_msg' => 'The security token for this operation has expired. If you want to continue with this operation, then please authenticate again below. This will ensure that the changes you just made will not be lost.', - 'authenticate' => 'Authenticate' + 'authenticate' => 'Authenticate', + 'approve' => 'Approve', ); # Localisation of the texts for the various drop-down menus that are actually From 81e35adf1d2204fc0f9689b66c29903dfced0fb0 Mon Sep 17 00:00:00 2001 From: Ivy Date: Sun, 14 Feb 2016 23:13:40 +0900 Subject: [PATCH 17/81] OAuth button resize --- public_html/images/facebook-login-icon.png | Bin 570 -> 22512 bytes public_html/images/google-login-icon.png | Bin 637 -> 23314 bytes public_html/images/linkedin-login-icon.png | Bin 404 -> 22623 bytes public_html/images/microsoft-login-icon.png | Bin 267 -> 22690 bytes public_html/images/twitter-login-icon.png | Bin 636 -> 22654 bytes public_html/images/yahoo-login-icon.png | Bin 413 -> 22861 bytes 6 files changed, 0 insertions(+), 0 deletions(-) diff --git a/public_html/images/facebook-login-icon.png b/public_html/images/facebook-login-icon.png index 2c6824059fe9f944b7e47a28d3b98fda63dfc300..10bfc560b842311a150717f04047f69b7c24e35c 100644 GIT binary patch literal 22512 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 diff --git a/public_html/images/google-login-icon.png b/public_html/images/google-login-icon.png index c1c0d12051a28b2fd1aa7e372ed84784f957097c..75f5a527687e56cb2f7a926c108ac6a22c6a9cfd 100644 GIT binary patch literal 23314 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 literal 637 zcmV-@0)qXCP)-uv+nC$Ga}MsMvEfAz{Lb_6Km7k3p7-Tw^={+%|ACoJ zTRGm>cm=cPkK?j9pj_1rhLi;LtyWyP=NiA|#mg zd5s+U%_P8TwerG{1rD`T#BX?ja5#)mD1`YtO?X$fLxgCoTleln_onp#SMv@0JXV#5 z;GSh6%P*edpJH0BsFwxI=sNI2Rhh@q*axGTj;{A>r@koMx)}~l?#Z)VsJg{!MFziybm8w$T3w@feBezQK*+|Bge?0N=!F5VtL>-(&;o(sTAD- z2{B{*v;yPOJq1i2C_v zk4X~JN(1^$M&5o&gDGiA9^UQ_|BvefRdF_W-!R X0$+F{;ypHF00000NkvXXu0mjf(GM)C diff --git a/public_html/images/linkedin-login-icon.png b/public_html/images/linkedin-login-icon.png index 9d998a6b62a25f4761f17ac0afcf02568613cc4e..e2b0c05bdbe165910fb7a169e873c99691cb2213 100644 GIT binary patch literal 22623 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 ZF?_q`T71@0PYdX922WQ%mvv4FO#mDUnE3zz diff --git a/public_html/images/microsoft-login-icon.png b/public_html/images/microsoft-login-icon.png index 66fdbe9492e33d0a2ea98021a19f933261c7a2bd..e6293dd70fcfe4b4c729b7e4a82f286ab192a43a 100644 GIT binary patch literal 22690 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 literal 267 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%0AM>Qx4 zSntXHOx}txc$j4$g>sNxG#AUKq?6+{f!A#!C=Gi*7r^9Q?se%yuUnT~=}eX~>%Du; zT<>1r-Vbf9=8FgMlyyaEiRiV)j6oe^&8C%Rr&DjL1f@BVH~Vtzo{nUv7xSspnPP-a zd?Wx$B6GgN*S_SJ?TDg3&H^QyvDd8{2B*8K2_s9Nbf0(p_J#?OJFMP}nB@A8cr7ZQ zNkM63inMFD>VXTW3Am70Ea8|3`SweK^t=tfZjjCWLt9jCRmHXDD|5@r@xb`*7OE~} z+h*+EP@AIQH2lLRm>OgOu>MtL!-Kq){L1QlWpF=z2zZpfOK(qr%hPrXuhx12EJNWt zAXwX}$D&ySphrN}|7zvh!=c{Re|Edup>(H^N+r34Qm&I~qui1TxmAh^NixhZGrL{1 zZ5oE$85L5wjQib865~Ea3^{i*D_1F8Z z_p{#5^Sqze;|Rq1ZeHCnVp^;Y59PPyiSeoGb*agcyOxi zKB)9i;<`-g3-_CT%;(hoLJFsd=pN)|s@YRPbUAb*5cU^p{`JZI$tM;C-?v469g*;G zE$AIkF~7Nz+)ux^?MARYAMwmGEcksZ{hnt?EX!^=3I%QCtm#*joWggyz{5;>Z3OJ4 z_bdX*_Y+ZrKf<)E)uG%EUM%5;N3IOUi1%%MKBO{U@!VeWv&f<-$aQv(+hG{ylh1!q z44ob(q)sz)?+4>yjiV|R0ho#I^I{>LUjCYo32KUw$-*~$v*fjIVQG}ULcOPT@20NQ z{$g6pa-zLTox2`qhG z9IXA$!xb<7t%o1qER4CD!FbQ%IA^TL9%=Fg%?q9YrbH7+dYpa5y^a#j`=bY`cYe9R z;@y~Q)IcXX#KPw_aSh@F(V=l{2^blX>m>_P2@G;V04VpkkaNq$!f-|sRnT8fK~y=8 z`k`50m zcSNzr8Cyi?KrOgWt?8Ys87#oiW z@5l4TO-Iu#M-hLldj0j1;_rvwFm|Jb2QvJUa_w?d_J!iDFTxa?H(tjfe|G30y;}s`JxBjIw{U5e#ok zk?wx#XpN4n$E}l&Tr(?4!H2be33U)@P?N5D&0E^~2+}_ygziaXc*6l&>`MH!PtfOk z3ee@SA7E&ex>v7Tc943ZkH19A-F-q!Q5-bmMMdbORTKw}?nlNLbk1e|)0PpxD3j^Q zHf7X8XgOG)!Hl_-ebEK5vrG%&$1YQtE z{H)iUB86hW<=vhjukt3P^)1%nh%B5*YgJ+dFg0>0iQ+@BJ}=Q)2Ou~<-ZMegLDZq? zv+1J+Y1~@%AI`KhU39dudMHc_72Bs%zh5Pg$O5u?^27da`!!hbb$~NarI^h3uT>jW z5S+zl?B*`8Ml!s+=wZBfQ%_2vwPAOgCy7BYEGp6~{)-;E1N8;&BY-ZN%|`&iJ^mN@ z^E`L2q>0LT%$XYIT0K^gR>t?>5X`w$DJdC-qFbIJOWbll_R-V1n!3_8jDb$*XtOI0 zM&XOID$1MUcx^N#Ks>=*&x@sPYG`XvhJ)=dNtAhpQ+8Jd?=qN86}5tJmWLh8!wOXZ z)0Ktu+0)hEg1!#oWGHi+4&Q0JXDK8D)bCA&Vbe81zV3R{5dZ9da$yj z=9VOXPYeF9dwfaiHK?UZ2V$Ij^Da!QwAJ6=e_&2JIkahZ3;7u^_K~N1)_Zp#;Ne*QBlL#FDkxWI<6$0p{@UGHkLW8~J<gfVb$w!%& zYC*T6o+Pw&0jG!`|9aF9^r}-H&&R0V#B7HJeinG2n@XW?g9nrp4>xO+POci`jwEqG z3#u|QH}a-#psCcF+Tl|3DdIe3RFHrJc#!r#bhne#qsgzVu` z__JYu%WZiZNYgfM6w~2V3t8qFG1RTCgy)-y0jInm{lEcV6^|P-r%}G>%L7z172sw* zE8@uGY`W<4*z)z6)X&JriQDSD;dF+f76xYK8r1%1 z=d>U|MhM~~>yHeR4@jc#)Fbu4_tHjLj&wljTid~}gjMQ*;cc!TH<0ceT4uB?+4xf- zcBFQH!rIy-40T_iB-~t1W`VL9Z)S~Jx4%pD7Hg@mRyvz}|n=p!>F(8x@q zWzE2dbpRAkI~W2Ak!5m^N=VML!JAClKRI40_|3(}!iq|UlzjHRM!?t>O1 z<}ToGJ|2+IG0c3><1*hMR_i%SMo2#70#?1SPZn+^Z#1uf0o%5>KBL zB4@~76EdFqlDytlR=keb;w_#Fe9mopm7@5Cd~^rZPgD+>qSbTXb`Ol?dCLX9x~JIo z064kVgi!fj3GYg#pldzNcG6&Q@BucB-N*-$jPLY3DxiOrk(RICazVpIg%>!b(mw2d?7vw}XlK_1ZfMKD^Hi0)gJ%WofDi^%>8B?dh`F;e3+ z#^&sbI=c@lxh=(W1wws5II#|4&T(2QnUS())aAcN$vp zXLaz{P@lmW8_yrPB*vIs*W$!h`Xf6xD7`w=5c8w_D7}CSG+-~zvubxYoPLRo*R=eR ze$E}ExtXms$kF6;4{rH0Q*2uG_46*AMm5j+QCH+g=SILdywb=l3Og=%Rsz3m`> zPxKGK5S3-jMt!R!)qdw_)$bd7;N{dPc{HKstbFg+HQvKmf{ST3$N%*hE zFI5Nizv!vvB6Z=3fV%ff!YpTdN?^R(ucE8v! zCxh#6pFaOHGDn}FJ>2XOiOry^H9T4zKgV+@b*F~BMSq|5XjF|%K*TlWm2!I#dpG&6 z>p)tO3MY7tJlurYz(P3ZSyB0~WRtYw9mvj5q$}yA#I`hhnYDcF`OOF;Xw9qN#QWDO z7Z|N}X!HBG1h(wI%vJy6_09=xwW~H1T_jqfmV;FsI;Jq69 zr#I08+`+SGRWcd=Tkq1*3rUAAB=P6~04z;S`8Iq5N&7id_nau~uNTBh+^uyDt z^oa@L5N5eKR6T|2@nJZ)md4@Mg^3eVm*{LbEcJ437@eSunFF;yWBf>T%%B{8-M1HF zZ^=8QC<1DwbE!HvsPe?TQ9?$+Af+UXo49nK`Fe-CNV&k|GWlxeAn1;n5mK9{&*~r{ z$`UW`^Fc{x6#APbRzC_DyG;GSS#bw?@V{iJm)g)@&S%ux!_rLsu5GCBoNFL#g@~8n zl|mAUgdu5Mw#*)&`M$-Kzj7=i_20@7lb0+?#Kpsl-quJ9KEmUMG6ge= z!_?$}**yhYTaS@HoEj-idOnJ07LK)78=~`&*{gWvD*jwH$&##x_7JV>{xl=-VCm3KAWM*9mItIXp(+H&2t(*gr5azO7d zl4MpuCA$}IgHZP*P@l6XMh@a^?<1&AbJ+~tDy`wF)Go!Dxc1rzU%=lNFn6K-iC|Oz z8^@47lO@X3Y;xW1dwi)bVhA9u`pSEtc?MxLPBKiB(io>ZEV|0jD!phbkM5FoJ(7r} z`))buXQ)!?tuaL>8*{_K*;7!z+s3IQDA_i{+&uVajJY2ztzQMTTAh|U^v)}k71(nx z6|gh#Ne#t#rWqu777`yiVk`aCI_q7B_O#yd;BV>~FCL+Hn=WTe#a6#q@@vl&*Ij0F z1a36L5}ihIPkQ;o5-ApKf3qoxo!{*#xQr)!ol05>`UdU^$SclB;8>$DYH7=L)nBZY z0gp=P8_F3y0<1vB4D+AUyWde%i+U=$PDrY)r)kiR7K1!?o-lzM^#YW_G(m)vB|Fd^ z8#d!7JcJkJ#-Ef>nh9V}w~5>>i-y8@EB#!lW#hF4m0qKA3z`vqc{I=5XMXn*XZh%^ z9o(1m(L^kmOpF&d!66eto27U&Xme(d3+ZEJ4Vfe`F|k1$1ErXQ1ZOdSALNcFgdGD# zfrY9_v(8A}W=z-cR(m~JRQD(T+42_F7gjcbfT)TX~xrPM3r4p!9Bea%G8Ox)AFh)i#>M)mj{YA5< zlmWrAHFZw%ZY@gBuseNfd9>18Py<)An_7oNx39{-{~nzGx4C~ED5pE_H~CY2yX&f& z(CXh1=l`o#Vd!h@(@R^|U6L=~CSSf)rF`q3i;(&!Phv+7iypU{EMD?&ClonRcKjP; z{fha18XHO%q#^G!o3o$*ti*%5qDo%Me#;E8$GSJCW@MIJAF9wvm@mGmj7G)O)iT_Y zf(`9UQxLiDrFIl4Qc;k`LgEisgL>33E_;tA^oSjT837`}6|Y=CbQt0>AXHKiTM|YL zq79hQ1sD+~SDY{}8Z=7umeC4RuZkx^*#E7%GHP-|`SEByv*2eotA!tTFPyi!vI@EB z4z^dEm1A-v$@7f6yAQ{9oj2`pGkzMIS-IoD#@P4A&$qm|w=H`6d8@dOk8sr~yMuJ@ z`hGbWfICrHw*L=>gG#9z|F{@?Vp}hvFNaz6&|0cJgGFk#gbURA5S-8j!P0mn_RzDf zrBqIYP-vpzyF)0Ie_fK>CmQJSRyj9 zu0P4!Jv`%y{9&C#VKHdHq2aRJWoMj?Rm1kUo&8ifxkFidwKu1}&|SVpfHo$*p)<^P=Y~030p4eHG7n2-t zcFj>n`m$R?&ZlQDl&aQAI)pas*Eh3z3|%~MwLf~LKL#d`(s%nRsvB>cf5*6>{`Jy#yA^uApUUlm8mns;%{rV?7C%@;2TH_e6e4?u9NX?*jA~=ZIiB!v z1D%iKg7rV`#8xy8w}__J)lDR52JXpFJhBYyC&y7vzEr)E`eJWrP!)Xm*4Lf*eW$`t z)*ZR3(LC;?%!LBl(Z^_l`&|1{H~;bMX7;VS*JRy4@ACD-9qA5J*DNN_-GlFRzBLhz z+wVh)H=Nv?j}h8@+IN7UxdLRK?%2Jck(K}Q>Ba*cT_+B>rlf3tQM{0XWQHH=dHv%o zBh^~-euvYE?baJ#YoC{1f0I|TNek_~{ED;ZPY18H8b9-)>mu`Rz?+7#cJ-Z8jbWK) Spz5Cibm14r^QC8R{PsUqCz-GS delta 624 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 Hu0mjfQ&hSE From d3e2481ad96717b1f7610fd5c80af6393f7537d4 Mon Sep 17 00:00:00 2001 From: Tom Homer Date: Thu, 14 Apr 2016 21:24:37 -0400 Subject: [PATCH 18/81] Page navigation can be turned off for frontpage and/or topics Feature Request #673. Allowed page navigation to be turned off just for frontpage, or frontpage and topics. Updated config option hide_main_page_navigation --- language/english.php | 3 +- language/english_utf-8.php | 1 + language/japanese_utf-8.php | 3 +- .../admin/configuration_validation.php | 4 ++- public_html/admin/install/config-install.php | 2 +- public_html/admin/install/lib-upgrade.php | 10 +++++++ public_html/docs/english/config.html | 6 ++-- public_html/docs/japanese/config.html | 7 +++-- public_html/index.php | 9 ++++-- sql/updates/mssql_2.1.1_to_2.1.2.php | 27 ++++++++++++++++++ sql/updates/mysql_2.1.1_to_2.1.2.php | 28 +++++++++++++++++++ sql/updates/pgsql_2.1.1_to_2.1.2.php | 28 +++++++++++++++++++ 12 files changed, 117 insertions(+), 11 deletions(-) create mode 100644 sql/updates/mssql_2.1.1_to_2.1.2.php create mode 100644 sql/updates/mysql_2.1.1_to_2.1.2.php create mode 100644 sql/updates/pgsql_2.1.1_to_2.1.2.php diff --git a/language/english.php b/language/english.php index 0bde69c99..2ca62207c 100644 --- a/language/english.php +++ b/language/english.php @@ -2377,7 +2377,8 @@ 32 => array('Disabled' => 0, 'Just Article Pages' => 1, 'Articles and Topics' => 2), 33 => array('Disabled' => 0, 'Enabled' => 1, 'Enabled (No Links)' => 2, 'Enabled (No Outbound Links)' => 3), 34 => array('grid' => 'grid', 'list' => 'list'), - 35 => array('default' => 'default', 'Name (asc)' => 'NAME_ASC', 'Name (desc)' => 'NAME_DESC', 'Type (asc)' => 'TYPE_ASC', 'Type (desc)' => 'TYPE_DESC', 'Modified (asc)' => 'MODIFIED_ASC', 'Modified (desc)' => 'MODIFIED_DESC'), + 35 => array('default' => 'default', 'Name (asc)' => 'NAME_ASC', 'Name (desc)' => 'NAME_DESC', 'Type (asc)' => 'TYPE_ASC', 'Type (desc)' => 'TYPE_DESC', 'Modified (asc)' => 'MODIFIED_ASC', 'Modified (desc)' => 'MODIFIED_DESC'), + 36 => array('False' => 'false', 'Frontpage only' => 'frontpage', 'Frontpage and Topics' => 'frontpage_topics') ); ################################################################################ diff --git a/language/english_utf-8.php b/language/english_utf-8.php index e193edc01..493b23551 100644 --- a/language/english_utf-8.php +++ b/language/english_utf-8.php @@ -2378,6 +2378,7 @@ 33 => array('Disabled' => 0, 'Enabled' => 1, 'Enabled (No Links)' => 2, 'Enabled (No Outbound Links)' => 3), 34 => array('grid' => 'grid', 'list' => 'list'), 35 => array('default' => 'default', 'Name (asc)' => 'NAME_ASC', 'Name (desc)' => 'NAME_DESC', 'Type (asc)' => 'TYPE_ASC', 'Type (desc)' => 'TYPE_DESC', 'Modified (asc)' => 'MODIFIED_ASC', 'Modified (desc)' => 'MODIFIED_DESC'), + 36 => array('False' => 'false', 'Frontpage only' => 'frontpage', 'Frontpage and Topics' => 'frontpage_topics') ); ################################################################################ diff --git a/language/japanese_utf-8.php b/language/japanese_utf-8.php index e2361a2e0..8d900f000 100644 --- a/language/japanese_utf-8.php +++ b/language/japanese_utf-8.php @@ -2371,7 +2371,8 @@ 32 => array('表示しない' => 0, '記事のページにのみ表示する' => 1, '記事と話題の両方で表示する' => 2), 33 => array('無効にする' => 0, '有効にする' => 1, '有効にする(リンクなし)' => 2, '有効にする(外部リンクなし)' => 3), 34 => array('グリッド' => 'grid', 'リスト' => 'list'), - 35 => array('デフォルト' => 'default', '名前(昇順)' => 'NAME_ASC', '名前(降順)' => 'NAME_DESC', '種類(昇順)' => 'TYPE_ASC', '種類(降順)' => 'TYPE_DESC', '変更日時(昇順)' => 'MODIFIED_ASC', '変更日時(降順)' => 'MODIFIED_DESC') + 35 => array('デフォルト' => 'default', '名前(昇順)' => 'NAME_ASC', '名前(降順)' => 'NAME_DESC', '種類(昇順)' => 'TYPE_ASC', '種類(降順)' => 'TYPE_DESC', '変更日時(昇順)' => 'MODIFIED_ASC', '変更日時(降順)' => 'MODIFIED_DESC'), + 36 => array('False' => 'false', 'Frontpage only' => 'frontpage', 'Frontpage and Topics' => 'frontpage_topics') ); ############################################################################### diff --git a/public_html/admin/configuration_validation.php b/public_html/admin/configuration_validation.php index 52556ef39..19560c0d4 100644 --- a/public_html/admin/configuration_validation.php +++ b/public_html/admin/configuration_validation.php @@ -214,7 +214,9 @@ $_CONF_VALIDATE['Core']['draft_flag'] = array('rule' => 'boolean'); $_CONF_VALIDATE['Core']['frontpage'] = array('rule' => 'boolean'); $_CONF_VALIDATE['Core']['hide_no_news_msg'] = array('rule' => 'boolean'); -$_CONF_VALIDATE['Core']['hide_main_page_navigation'] = array('rule' => 'boolean'); +$_CONF_VALIDATE['Core']['hide_main_page_navigation'] = array( + 'rule' => array('inList', array('false', 'frontpage', 'frontpage_topics'), true) +); $_CONF_VALIDATE['Core']['onlyrootfeatures'] = array('rule' => 'boolean'); $_CONF_VALIDATE['Core']['aftersave_story'] = array( 'rule' => array('inList', array('admin', 'home', 'list', 'item'), true) diff --git a/public_html/admin/install/config-install.php b/public_html/admin/install/config-install.php index 4711168b1..aaafe65a6 100644 --- a/public_html/admin/install/config-install.php +++ b/public_html/admin/install/config-install.php @@ -141,7 +141,7 @@ function install_config() $c->add('draft_flag',0,'select',1,7,0,1280,TRUE, $me, 7); $c->add('frontpage',1,'select',1,7,0,1290,TRUE, $me, 7); $c->add('hide_no_news_msg',0,'select',1,7,0,1300,TRUE, $me, 7); - $c->add('hide_main_page_navigation',0,'select',1,7,0,1310,TRUE, $me, 7); + $c->add('hide_main_page_navigation','false','select',1,7,36,1310,TRUE, $me, 7); $c->add('onlyrootfeatures',0,'select',1,7,0,1320,TRUE, $me, 7); $c->add('aftersave_story','list','select',1,7,9,1330,TRUE, $me, 7); $c->add('related_topics',1,'select',1,7,32,1340,TRUE, $me, 7); diff --git a/public_html/admin/install/lib-upgrade.php b/public_html/admin/install/lib-upgrade.php index 69b32e093..7aefd726a 100644 --- a/public_html/admin/install/lib-upgrade.php +++ b/public_html/admin/install/lib-upgrade.php @@ -507,6 +507,16 @@ function INST_doDatabaseUpgrades($current_gl_version) $current_gl_version = '2.1.0'; $_SQL = ''; break; + + case '2.1.1': + require_once $_CONF['path'] . 'sql/updates/' . $_DB_dbms . '_2.1.1_to_2.1.2.php'; + INST_updateDB($_SQL); + + update_ConfValuesFor212(); + + $current_gl_version = '2.1.2'; + $_SQL = ''; + break; default: $done = true; diff --git a/public_html/docs/english/config.html b/public_html/docs/english/config.html index e2c696aed..2c4828076 100644 --- a/public_html/docs/english/config.html +++ b/public_html/docs/english/config.html @@ -436,9 +436,9 @@

Stories and Trackback: Story

When set to 1, hide the "No News To Display" message on the index page (e.g. when viewing a topic without any stories in it) hide_main_page_navigation - 0 - When set to 1, this option will hide the "Google paging" - navigation from index.php, i.e. from the site's frontpage and all topic + false + When set to frontpage, this option will hide the "Google paging" + navigation from index.php, i.e. from the site's frontpage. When set to frontpage_topics it will hide the navigation from the frontpage and all topic pages. This may come in handy for more advanced layouts but will of course prevent people from easily reaching older articles. diff --git a/public_html/docs/japanese/config.html b/public_html/docs/japanese/config.html index c513804bf..db1a8fa87 100644 --- a/public_html/docs/japanese/config.html +++ b/public_html/docs/japanese/config.html @@ -391,8 +391,11 @@

記事とトラックバック: 記事

「はい」なら「記事がありません」を表示しません(記事がまだ投稿されていない話題を選んだときなど。)。 ページナビゲーションを表示しない (hide_main_page_navigation) - いいえ - 「はい」にすると、index.php、つまり、サイトのトップページとすべての話題でページナビゲーションが表示されなくなります。この動作はより高度なレイアウトを実現するのには役立ちますが、訪問者が古い記事を読むのが難しくなります。 + false + When set to frontpage, this option will hide the "Google paging" + navigation from index.php, i.e. from the site's frontpage. When set to frontpage_topics it will hide the navigation from the frontpage and all topic + pages. This may come in handy for more advanced layouts but will of course + prevent people from easily reaching older articles. Rootユーザーだけが注目記事を設定する(onlyrootfeatures) いいえ diff --git a/public_html/index.php b/public_html/index.php index c16cec121..dec60dc97 100644 --- a/public_html/index.php +++ b/public_html/index.php @@ -361,14 +361,19 @@ function fixTopic(&$A, $tid_list) // Print Google-like paging navigation if (!isset($_CONF['hide_main_page_navigation']) || - ($_CONF['hide_main_page_navigation'] == 0)) { + ($_CONF['hide_main_page_navigation'] == 'false')) { if (empty($topic)) { $base_url = $_CONF['site_url'] . '/index.php'; } else { $base_url = $_CONF['site_url'] . '/index.php?topic=' . $topic; } $display .= COM_printPageNavigation($base_url, $page, $num_pages); - } + } else { + if ($_CONF['hide_main_page_navigation'] == 'frontpage' AND !empty($topic)) { + $base_url = $_CONF['site_url'] . '/index.php?topic=' . $topic; + $display .= COM_printPageNavigation($base_url, $page, $num_pages); + } + } } else { // no stories to display if ($page == 1) { if (!isset($_CONF['hide_no_news_msg']) || diff --git a/sql/updates/mssql_2.1.1_to_2.1.2.php b/sql/updates/mssql_2.1.1_to_2.1.2.php new file mode 100644 index 000000000..d0c00617c --- /dev/null +++ b/sql/updates/mssql_2.1.1_to_2.1.2.php @@ -0,0 +1,27 @@ +del('hide_main_page_navigation', $me); + $c->add('hide_main_page_navigation','false','select',1,7,36,1310,TRUE, $me, 7); + + return true; +} + +?> diff --git a/sql/updates/mysql_2.1.1_to_2.1.2.php b/sql/updates/mysql_2.1.1_to_2.1.2.php new file mode 100644 index 000000000..341c288c9 --- /dev/null +++ b/sql/updates/mysql_2.1.1_to_2.1.2.php @@ -0,0 +1,28 @@ +del('hide_main_page_navigation', $me); + $c->add('hide_main_page_navigation','false','select',1,7,36,1310,TRUE, $me, 7); + + return true; +} + +?> diff --git a/sql/updates/pgsql_2.1.1_to_2.1.2.php b/sql/updates/pgsql_2.1.1_to_2.1.2.php new file mode 100644 index 000000000..341c288c9 --- /dev/null +++ b/sql/updates/pgsql_2.1.1_to_2.1.2.php @@ -0,0 +1,28 @@ +del('hide_main_page_navigation', $me); + $c->add('hide_main_page_navigation','false','select',1,7,36,1310,TRUE, $me, 7); + + return true; +} + +?> From c3e4d9ae194ea60dc63987ac4bd43c8e8d03ad54 Mon Sep 17 00:00:00 2001 From: The Gitter Badger Date: Mon, 25 Jul 2016 16:17:20 +0000 Subject: [PATCH 19/81] Add Gitter link --- readme | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme b/readme index 96d0fdcce..3ad421254 100644 --- a/readme +++ b/readme @@ -210,3 +210,5 @@ Appendix A - readme history [various] 2001-08-18 - First readme file. + +Join the chat at https://gitter.im/Geeklog-Core/geeklog From c783b553fe4bb15b1d650d92633881bc508c5d3d Mon Sep 17 00:00:00 2001 From: Kenji ITO Date: Sat, 30 Jul 2016 07:12:20 +0900 Subject: [PATCH 20/81] Dropped MSSQL support --- plugins/calendar/functions.inc | 1768 +++++++++--------- plugins/polls/functions.inc | 1382 +++++++------- plugins/spamx/DeleteComment.Action.class.php | 20 +- plugins/staticpages/functions.inc | 1456 +++++++-------- plugins/staticpages/services.inc.php | 263 ++- public_html/admin/block.php | 578 +++--- public_html/admin/install/index.php | 17 +- public_html/admin/install/lib-install.php | 35 +- public_html/admin/install/lib-upgrade.php | 23 +- public_html/admin/plugins.php | 582 +++--- public_html/directory.php | 212 +-- public_html/docs/english/changes.html | 15 + public_html/docs/english/config.html | 2 +- public_html/docs/english/install.html | 4 +- public_html/docs/history | 1 + public_html/docs/japanese/config.html | 1 - public_html/index.php | 89 +- public_html/lib-common.php | 23 - public_html/stats.php | 77 +- readme | 2 +- sql/mssql_tableanddata.php | 1640 ---------------- sql/updates/mssql_1.4.1_to_1.5.0.php | 408 ---- sql/updates/mssql_1.5.0_to_1.5.1.php | 7 - sql/updates/mssql_1.5.2_to_1.6.0.php | 209 --- sql/updates/mssql_1.6.0_to_1.6.1.php | 36 - sql/updates/mssql_1.6.1_to_1.7.0.php | 56 - sql/updates/mssql_1.7.2_to_1.8.0.php | 245 --- sql/updates/mssql_1.8.2_to_2.0.0.php | 136 -- sql/updates/mssql_2.0.0_to_2.1.0.php | 261 --- sql/updates/mssql_2.1.1_to_2.1.2.php | 39 - system/classes/config.class.php | 739 ++++---- system/classes/listfactory.class.php | 540 +++--- system/classes/search.class.php | 514 +++-- system/classes/searchcriteria.class.php | 57 +- system/classes/story.class.php | 1109 ++++++----- system/databases/mssql.class.php | 1199 ------------ system/lib-database.php | 622 +++--- system/lib-security.php | 1300 ++++++------- system/lib-story.php | 1503 ++++++++------- 39 files changed, 6177 insertions(+), 10993 deletions(-) delete mode 100644 sql/mssql_tableanddata.php delete mode 100644 sql/updates/mssql_1.4.1_to_1.5.0.php delete mode 100644 sql/updates/mssql_1.5.0_to_1.5.1.php delete mode 100644 sql/updates/mssql_1.5.2_to_1.6.0.php delete mode 100644 sql/updates/mssql_1.6.0_to_1.6.1.php delete mode 100644 sql/updates/mssql_1.6.1_to_1.7.0.php delete mode 100644 sql/updates/mssql_1.7.2_to_1.8.0.php delete mode 100644 sql/updates/mssql_1.8.2_to_2.0.0.php delete mode 100644 sql/updates/mssql_2.0.0_to_2.1.0.php delete mode 100644 sql/updates/mssql_2.1.1_to_2.1.2.php delete mode 100755 system/databases/mssql.class.php diff --git a/plugins/calendar/functions.inc b/plugins/calendar/functions.inc index 97e2f202c..f7f913a0b 100644 --- a/plugins/calendar/functions.inc +++ b/plugins/calendar/functions.inc @@ -35,12 +35,12 @@ // +---------------------------------------------------------------------------+ /** -* Implementation of the Plugin API for the Calendar plugin -* -* @package Calendar -*/ + * Implementation of the Plugin API for the Calendar plugin + * + * @package Calendar + */ -if (strpos(strtolower($_SERVER['PHP_SELF']), 'functions.inc') !== false) { +if (stripos($_SERVER['PHP_SELF'], 'functions.inc') !== false) { die('This file can not be used on its own.'); } @@ -63,25 +63,23 @@ if (!isset($_CA_CONF['calendarloginrequired'])) { $_CA_CONF = $ca_config->get_config('calendar'); } - // +---------------------------------------------------------------------------+ // | Geeklog Plugin API Implementations | // +---------------------------------------------------------------------------+ /** -* Returns the items for this plugin that should appear on the main menu -* -* NOTE: this MUST return the url/value pairs in the following format -* $[