diff options
-rw-r--r-- | _test/tests/inc/auth_password.test.php | 14 | ||||
-rw-r--r-- | _test/tests/inc/parser/parser_media.test.php | 11 | ||||
-rw-r--r-- | _test/tests/inc/parser/renderer_resolveinterwiki.test.php | 18 | ||||
-rw-r--r-- | conf/dokuwiki.php | 1 | ||||
-rw-r--r-- | feed.php | 3 | ||||
-rw-r--r-- | inc/Cache/Cache.php | 13 | ||||
-rw-r--r-- | inc/Cache/CacheInstructions.php | 2 | ||||
-rw-r--r-- | inc/PassHash.php | 134 | ||||
-rw-r--r-- | inc/Search/Indexer.php | 4 | ||||
-rw-r--r-- | inc/init.php | 4 | ||||
-rw-r--r-- | inc/io.php | 2 | ||||
-rw-r--r-- | inc/lang/el/lang.php | 3 | ||||
-rw-r--r-- | inc/lang/en/lang.php | 1 | ||||
-rw-r--r-- | inc/lang/ja/lang.php | 1 | ||||
-rw-r--r-- | inc/media.php | 4 | ||||
-rw-r--r-- | inc/parser/renderer.php | 16 | ||||
-rw-r--r-- | inc/parser/xhtml.php | 20 | ||||
-rw-r--r-- | install.php | 14 | ||||
-rw-r--r-- | lib/plugins/config/lang/el/lang.php | 1 | ||||
-rw-r--r-- | lib/plugins/config/lang/en/lang.php | 1 | ||||
-rw-r--r-- | lib/plugins/config/lang/ja/lang.php | 3 | ||||
-rw-r--r-- | lib/plugins/config/settings/config.metadata.php | 1 | ||||
-rw-r--r-- | lib/plugins/extension/helper/extension.php | 4 | ||||
-rw-r--r-- | lib/plugins/usermanager/_test/csv_import.test.php | 38 | ||||
-rw-r--r-- | lib/tpl/dokuwiki/script.js | 11 |
25 files changed, 264 insertions, 60 deletions
diff --git a/_test/tests/inc/auth_password.test.php b/_test/tests/inc/auth_password.test.php index 1f4e47a30..beb95c060 100644 --- a/_test/tests/inc/auth_password.test.php +++ b/_test/tests/inc/auth_password.test.php @@ -92,6 +92,20 @@ class auth_password_test extends DokuWikiTest { $this->assertTrue(auth_verifyPassword('test12345', '$H$9IQRaTwmfeRo7ud9Fh4E2PdI0S3r.L0')); } + function test_verifypassword_drupal_sha512() { + $this->assertTrue(auth_verifypassword('drupal_sha512', '$S$D7JxIm0f7QKO3zjwVS1RH4AW8sYvmLjO0.Rn4swH0JVt6OrZ4yzZ')); + } + + function test_verifypassword_drupal_migrated_6to7() { + $this->assertTrue(auth_verifypassword('pouette1234', 'U$S$9c47LGZuhR6TvhRQXzymkJIQ3mXthUCc6KDEGTt4B7eOL/H9Ykuy')); + } + + function test_verifyPassword_seafilepbkdf2() { + $hash='PBKDF2SHA256$10000$99227b6df52aa1394b5ca0aceee2733dd6c2670c85bbe26c751a2c65e79d4db7$d61dd1c4df6873c73813fe97f96d0e917792602a33966f3fab0eef154637cc84'; + $pw='@STR0NGpassW0RD'; + $this->assertTrue(auth_verifyPassword($pw, $hash)); + } + function test_veryPassword_mediawiki() { $this->assertTrue(auth_verifyPassword('password', ':B:838c83e1:e4ab7024509eef084cdabd03d8b2972c')); } diff --git a/_test/tests/inc/parser/parser_media.test.php b/_test/tests/inc/parser/parser_media.test.php index 2b22dabff..fdb9c0fc4 100644 --- a/_test/tests/inc/parser/parser_media.test.php +++ b/_test/tests/inc/parser/parser_media.test.php @@ -133,6 +133,17 @@ class TestOfDoku_Parser_Media extends TestOfDoku_Parser { $this->assertEquals($rest, substr($url, $substr_start)); } + function testVideoInternalTitle() { + $file = 'wiki:kind_zu_katze.ogv'; + $title = 'Single quote: \' Ampersand: &'; + + $Renderer = new Doku_Renderer_xhtml(); + $url = $Renderer->externalmedia($file, $title, null, null, null, 'cache', 'details', true); + + // make sure the title is escaped just once + $this->assertEquals(htmlspecialchars($title), substr($url, 28, 32)); + } + function testSimpleLinkText() { $file = 'wiki:dokuwiki-128.png'; $parser_response = p_get_instructions('{{' . $file . '|This is a simple text.}}'); diff --git a/_test/tests/inc/parser/renderer_resolveinterwiki.test.php b/_test/tests/inc/parser/renderer_resolveinterwiki.test.php index 2cd23dfaa..ae23280cf 100644 --- a/_test/tests/inc/parser/renderer_resolveinterwiki.test.php +++ b/_test/tests/inc/parser/renderer_resolveinterwiki.test.php @@ -43,13 +43,27 @@ class Test_resolveInterwiki extends DokuWikiTest { function testNonexisting() { $Renderer = new Doku_Renderer(); $Renderer->interwiki = getInterwiki(); + unset($Renderer->interwiki['default']); $shortcut = 'nonexisting'; $reference = 'foo @+%/'; $url = $Renderer->_resolveInterWiki($shortcut, $reference); - $expected = 'https://www.google.com/search?q=foo%20%40%2B%25%2F&btnI=lucky'; - $this->assertEquals($expected, $url); + $this->assertEquals('', $url); + $this->assertEquals('', $shortcut); + } + + function testNonexistingWithDefault() { + $Renderer = new Doku_Renderer(); + $Renderer->interwiki = getInterwiki(); + $Renderer->interwiki['default'] = 'https://en.wikipedia.org/wiki/{NAME}'; + + $shortcut = 'nonexisting'; + $reference = 'foo'; + $url = $Renderer->_resolveInterWiki($shortcut, $reference); + + $this->assertEquals('https://en.wikipedia.org/wiki/foo', $url); + $this->assertEquals('default', $shortcut); } } diff --git a/conf/dokuwiki.php b/conf/dokuwiki.php index 9b576f4cf..290453d89 100644 --- a/conf/dokuwiki.php +++ b/conf/dokuwiki.php @@ -136,6 +136,7 @@ $conf['rss_media'] = 'both'; //what should be listed? // 'media' - media changes only $conf['rss_update'] = 5*60; //Update the RSS feed every n seconds (defaults to 5 minutes) $conf['rss_show_summary'] = 1; //Add revision summary to title? 0|1 +$conf['rss_show_deleted'] = 1; //Show deleted items 0|1 /* Advanced Settings */ $conf['updatecheck'] = 1; //automatically check for new releases? @@ -466,7 +466,8 @@ function rss_buildItems(&$rss, &$data, $opt) { */ function rssRecentChanges($opt) { global $conf; - $flags = RECENTS_SKIP_DELETED; + $flags = 0; + if(!$conf['rss_show_deleted']) $flags += RECENTS_SKIP_DELETED; if(!$opt['show_minor']) $flags += RECENTS_SKIP_MINORS; if($opt['content_type'] == 'media' && $conf['mediarevisions']) $flags += RECENTS_MEDIA_CHANGES; if($opt['content_type'] == 'both' && $conf['mediarevisions']) $flags += RECENTS_MEDIA_PAGES_MIXED; diff --git a/inc/Cache/Cache.php b/inc/Cache/Cache.php index ab64da5cb..af82e6bf6 100644 --- a/inc/Cache/Cache.php +++ b/inc/Cache/Cache.php @@ -78,9 +78,13 @@ class Cache $this->depends = $depends; $this->addDependencies(); - if ($this->_event) { - return $this->stats(Event::createAndTrigger( - $this->_event, $this, array($this, 'makeDefaultCacheDecision')) + if ($this->getEvent()) { + return $this->stats( + Event::createAndTrigger( + $this->getEvent(), + $this, + array($this, 'makeDefaultCacheDecision') + ) ); } @@ -105,7 +109,6 @@ class Cache */ public function makeDefaultCacheDecision() { - if ($this->_nocache) { return false; } // caching turned off @@ -170,7 +173,7 @@ class Cache return false; } - return io_savefile($this->cache, $data); + return io_saveFile($this->cache, $data); } /** diff --git a/inc/Cache/CacheInstructions.php b/inc/Cache/CacheInstructions.php index 3c4786105..acd02abae 100644 --- a/inc/Cache/CacheInstructions.php +++ b/inc/Cache/CacheInstructions.php @@ -41,6 +41,6 @@ class CacheInstructions extends \dokuwiki\Cache\CacheParser return false; } - return io_savefile($this->cache, serialize($instructions)); + return io_saveFile($this->cache, serialize($instructions)); } } diff --git a/inc/PassHash.php b/inc/PassHash.php index c0e61f409..1189da0b7 100644 --- a/inc/PassHash.php +++ b/inc/PassHash.php @@ -9,6 +9,7 @@ namespace dokuwiki; * This class implements various mechanisms used to hash passwords * * @author Andreas Gohr <andi@splitbrain.org> + * @author Schplurtz le Déboulonné <Schplurtz@laposte.net> * @license LGPL2 */ class PassHash { @@ -20,6 +21,7 @@ class PassHash { * match true is is returned else false * * @author Andreas Gohr <andi@splitbrain.org> + * @author Schplurtz le Déboulonné <Schplurtz@laposte.net> * * @param string $clear Clear-Text password * @param string $hash Hash to compare against @@ -31,6 +33,12 @@ class PassHash { $magic = ''; //determine the used method and salt + if (substr($hash, 0, 2) == 'U$') { + // This may be an updated password from user_update_7000(). Such hashes + // have 'U' added as the first character and need an extra md5(). + $hash = substr($hash, 1); + $clear = md5($clear); + } $len = strlen($hash); if(preg_match('/^\$1\$([^\$]{0,8})\$/', $hash, $m)) { $method = 'smd5'; @@ -40,6 +48,10 @@ class PassHash { $method = 'apr1'; $salt = $m[1]; $magic = 'apr1'; + } elseif(preg_match('/^\$S\$(.{52})$/', $hash, $m)) { + $method = 'drupal_sha512'; + $salt = $m[1]; + $magic = 'S'; } elseif(preg_match('/^\$P\$(.{31})$/', $hash, $m)) { $method = 'pmd5'; $salt = $m[1]; @@ -55,6 +67,13 @@ class PassHash { 'iter' => $m[2], ); $salt = $m[3]; + } elseif(preg_match('/^PBKDF2(SHA\d+)\$(\d+)\$([[:xdigit:]]+)\$([[:xdigit:]]+)$/', $hash, $m)) { + $method = 'seafilepbkdf2'; + $magic = array( + 'algo' => $m[1], + 'iter' => $m[2], + ); + $salt = $m[3]; } elseif(preg_match('/^sha1\$(.{5})\$/', $hash, $m)) { $method = 'djangosha1'; $salt = $m[1]; @@ -348,17 +367,24 @@ class PassHash { } /** - * Password hashing method 'pmd5' + * Password stretched hashing wrapper. * - * Uses salted MD5 hashs. Salt is 1+8 bytes long, 1st byte is the - * iteration count when given, for null salts $compute is used. + * Initial hash is repeatedly rehashed with same password. + * Any salted hash algorithm supported by PHP hash() can be used. Salt + * is 1+8 bytes long, 1st byte is the iteration count when given. For null + * salts $compute is used. * - * The actual iteration count is the given count squared, maximum is - * 30 (-> 1073741824). If a higher one is given, the function throws - * an exception. + * The actual iteration count is 2 to the power of the given count, + * maximum is 30 (-> 2^30 = 1_073_741_824). If a higher one is given, + * the function throws an exception. + * This iteration count is expected to grow with increasing power of + * new computers. * - * @link http://www.openwall.com/phpass/ + * @author Andreas Gohr <andi@splitbrain.org> + * @author Schplurtz le Déboulonné <Schplurtz@laposte.net> + * @link http://www.openwall.com/phpass/ * + * @param string $algo The hash algorithm to be used * @param string $clear The clear text to hash * @param string $salt The salt to use, null for random * @param string $magic The hash identifier (P or H) @@ -366,13 +392,13 @@ class PassHash { * @throws \Exception * @return string Hashed password */ - public function hash_pmd5($clear, $salt = null, $magic = 'P', $compute = 8) { + protected function stretched_hash($algo, $clear, $salt = null, $magic = 'P', $compute = 8) { $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; if(is_null($salt)) { $this->init_salt($salt); $salt = $itoa64[$compute].$salt; // prefix iteration count } - $iterc = $salt[0]; // pos 0 of salt is iteration count + $iterc = $salt[0]; // pos 0 of salt is log2(iteration count) $iter = strpos($itoa64, $iterc); if($iter > 30) { @@ -384,14 +410,14 @@ class PassHash { $salt = substr($salt, 1, 8); // iterate - $hash = md5($salt.$clear, true); + $hash = hash($algo, $salt . $clear, TRUE); do { - $hash = md5($hash.$clear, true); + $hash = hash($algo, $hash.$clear, true); } while(--$iter); // encode $output = ''; - $count = 16; + $count = strlen($hash); $i = 0; do { $value = ord($hash[$i++]); @@ -413,6 +439,49 @@ class PassHash { } /** + * Password hashing method 'pmd5' + * + * Repeatedly uses salted MD5 hashs. See stretched_hash() for the + * details. + * + * + * @author Schplurtz le Déboulonné <Schplurtz@laposte.net> + * @link http://www.openwall.com/phpass/ + * @see PassHash::stretched_hash() for the implementation details. + * + * @param string $clear The clear text to hash + * @param string $salt The salt to use, null for random + * @param string $magic The hash identifier (P or H) + * @param int $compute The iteration count for new passwords + * @throws Exception + * @return string Hashed password + */ + public function hash_pmd5($clear, $salt = null, $magic = 'P', $compute = 8) { + return $this->stretched_hash('md5', $clear, $salt, $magic, $compute); + } + + /** + * Password hashing method 'drupal_sha512' + * + * Implements Drupal salted sha512 hashs. Drupal truncates the hash at 55 + * characters. See stretched_hash() for the details; + * + * @author Schplurtz le Déboulonné <Schplurtz@laposte.net> + * @link https://api.drupal.org/api/drupal/includes%21password.inc/7.x + * @see PassHash::stretched_hash() for the implementation details. + * + * @param string $clear The clear text to hash + * @param string $salt The salt to use, null for random + * @param string $magic The hash identifier (S) + * @param int $compute The iteration count for new passwords (defautl is drupal 7's) + * @throws Exception + * @return string Hashed password + */ + public function hash_drupal_sha512($clear, $salt = null, $magic = 'S', $compute = 15) { + return substr($this->stretched_hash('sha512', $clear, $salt, $magic, $compute), 0, 55); + } + + /** * Alias for hash_pmd5 * * @param string $clear @@ -462,6 +531,47 @@ class PassHash { } /** + * Password hashing method 'seafilepbkdf2' + * + * An algorithm and iteration count should be given in the opts array. + * + * Hash algorithm is the string that is in the password string in seafile + * database. It has to be converted to a php algo name. + * + * @author Schplurtz le Déboulonné <Schplurtz@laposte.net> + * @see https://stackoverflow.com/a/23670177 + * + * @param string $clear The clear text to hash + * @param string $salt The salt to use, null for random + * @param array $opts ('algo' => hash algorithm, 'iter' => iterations) + * @return string Hashed password + * @throws Exception when PHP is missing support for the method/algo + */ + public function hash_seafilepbkdf2($clear, $salt=null, $opts=array()) { + $this->init_salt($salt, 64); + if(empty($opts['algo'])) { + $prefixalgo='SHA256'; + } else { + $prefixalgo=$opts['algo']; + } + $algo = strtolower($prefixalgo); + if(empty($opts['iter'])) { + $iter = 10000; + } else { + $iter = (int) $opts['iter']; + } + if(!function_exists('hash_pbkdf2')) { + throw new Exception('This PHP installation has no PBKDF2 support'); + } + if(!in_array($algo, hash_algos())) { + throw new Exception("This PHP installation has no $algo support"); + } + + $hash = hash_pbkdf2($algo, $clear, hex2bin($salt), $iter, 0); + return "PBKDF2$prefixalgo\$$iter\$$salt\$$hash"; + } + + /** * Password hashing method 'djangopbkdf2' * * An algorithm and iteration count should be given in the opts array. diff --git a/inc/Search/Indexer.php b/inc/Search/Indexer.php index 8770ac794..a29e5b28b 100644 --- a/inc/Search/Indexer.php +++ b/inc/Search/Indexer.php @@ -1000,7 +1000,7 @@ class Indexer { if (!empty($lines)) fwrite($fh, "\n"); fclose($fh); - if (isset($conf['fperm'])) + if ($conf['fperm']) chmod($fn.'.tmp', $conf['fperm']); io_rename($fn.'.tmp', $fn.'.idx'); return true; @@ -1067,7 +1067,7 @@ class Indexer { fwrite($fh, $line); } fclose($fh); - if (isset($conf['fperm'])) + if ($conf['fperm']) chmod($fn.'.tmp', $conf['fperm']); io_rename($fn.'.tmp', $fn.'.idx'); return true; diff --git a/inc/init.php b/inc/init.php index 69ef6f15a..f9bb53472 100644 --- a/inc/init.php +++ b/inc/init.php @@ -340,7 +340,7 @@ function init_files(){ $fh = @fopen($file,'a'); if($fh){ fclose($fh); - if(!empty($conf['fperm'])) chmod($file, $conf['fperm']); + if($conf['fperm']) chmod($file, $conf['fperm']); }else{ nice_die("$file is not writable. Check your permissions settings!"); } @@ -405,7 +405,7 @@ function init_creationmodes(){ // check what is set automatically by the system on file creation // and set the fperm param if it's not what we want - $auto_fmode = 0666 & ~$umask; + $auto_fmode = $conf['fmode'] & ~$umask; if($auto_fmode != $conf['fmode']) $conf['fperm'] = $conf['fmode']; // check what is set automatically by the system on file creation diff --git a/inc/io.php b/inc/io.php index 18aae25e7..1dfabe845 100644 --- a/inc/io.php +++ b/inc/io.php @@ -252,7 +252,7 @@ function _io_saveFile($file, $content, $append) { fclose($fh); } - if(!$fileexists and !empty($conf['fperm'])) chmod($file, $conf['fperm']); + if(!$fileexists and $conf['fperm']) chmod($file, $conf['fperm']); return true; } diff --git a/inc/lang/el/lang.php b/inc/lang/el/lang.php index be4ec2ee1..1a7b29b95 100644 --- a/inc/lang/el/lang.php +++ b/inc/lang/el/lang.php @@ -3,6 +3,7 @@ /** * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) * + * @author Apostolos Tsompanopoulos <info@aptlogs.com> * @author Katerina Katapodi <extragold1234@hotmail.com> * @author Thanos Massias <tm@thriasio.gr> * @author Αθανάσιος Νταής <homunculus@wana.gr> @@ -141,7 +142,7 @@ $lang['regmailfail'] = 'Φαίνεται να υπάρχει πρόβ $lang['regbadmail'] = 'Η διεύθυνση e-mail δεν είναι έγκυρη - εάν πιστεύετε ότι αυτό είναι λάθος, επικοινωνήστε μαζί μας'; $lang['regbadpass'] = 'Οι δύο κωδικοί δεν είναι ίδιοι, προσπαθήστε ξανά.'; $lang['regpwmail'] = 'Ο κωδικός σας'; -$lang['reghere'] = 'Δεν έχετε λογαριασμό ακόμη? Δημιουργήστε έναν'; +$lang['reghere'] = 'Δεν έχετε λογαριασμό ακόμη; Δημιουργήστε έναν'; $lang['profna'] = 'Αυτό το wiki δεν υποστηρίζει την επεξεργασία προφίλ.'; $lang['profnochange'] = 'Καμία αλλαγή.'; $lang['profnoempty'] = 'Δεν επιτρέπεται κενό όνομα χρήστη η κενή διεύθυνση email.'; diff --git a/inc/lang/en/lang.php b/inc/lang/en/lang.php index 4d11bac08..4054269a8 100644 --- a/inc/lang/en/lang.php +++ b/inc/lang/en/lang.php @@ -321,6 +321,7 @@ $lang['i_modified'] = 'For security reasons this script will only wor $lang['i_funcna'] = 'PHP function <code>%s</code> is not available. Maybe your hosting provider disabled it for some reason?'; $lang['i_phpver'] = 'Your PHP version <code>%s</code> is lower than the needed <code>%s</code>. You need to upgrade your PHP install.'; $lang['i_mbfuncoverload'] = 'mbstring.func_overload must be disabled in php.ini to run DokuWiki.'; +$lang['i_urandom'] = 'DokuWiki cannot create cryptographically secure numbers for cookies. You may want to check your open_basedir settings in php.ini for proper <code>/dev/urandom</code> access.'; $lang['i_permfail'] = '<code>%s</code> is not writable by DokuWiki. You need to fix the permission settings of this directory!'; $lang['i_confexists'] = '<code>%s</code> already exists'; $lang['i_writeerr'] = 'Unable to create <code>%s</code>. You will need to check directory/file permissions and create the file manually.'; diff --git a/inc/lang/ja/lang.php b/inc/lang/ja/lang.php index 968edf4eb..e2f3dd16e 100644 --- a/inc/lang/ja/lang.php +++ b/inc/lang/ja/lang.php @@ -304,6 +304,7 @@ $lang['i_modified'] = 'セキュリティの理由から、新規も $lang['i_funcna'] = 'PHPの関数 <code>%s</code> が使用できません。ホスティング会社が何らかの理由で無効にしている可能性があります。'; $lang['i_phpver'] = 'PHPのバージョン <code>%s</code> が必要なバージョン <code>%s</code> より以前のものです。PHPのアップグレードが必要です。'; $lang['i_mbfuncoverload'] = 'DokuWiki を実行する php.ini ファイルの mbstring.func_overload は無効にして下さい。'; +$lang['i_urandom'] = 'DokuWikiは、Cookieに対して暗号的に安全な番号を作成できません。<code>/dev/urandom</code>に対する適切なアクセスについて、php.iniの設定 open_basedir を確認する事をお勧めします。'; $lang['i_permfail'] = '<code>%s</code> に書き込みできません。このディレクトリの権限を確認して下さい。'; $lang['i_confexists'] = '<code>%s</code> は既に存在します'; $lang['i_writeerr'] = '<code>%s</code> を作成できません。ディレクトリとファイルの権限を確認し、それらを手動で作成する必要があります。'; diff --git a/inc/media.php b/inc/media.php index ec40df5ef..cc29bd16c 100644 --- a/inc/media.php +++ b/inc/media.php @@ -2082,7 +2082,7 @@ function media_resize_image($file, $ext, $w, $h=0){ media_resize_imageIM($ext, $file, $info[0], $info[1], $local, $w, $h) || media_resize_imageGD($ext, $file, $info[0], $info[1], $local, $w, $h) ) { - if(!empty($conf['fperm'])) @chmod($local, $conf['fperm']); + if($conf['fperm']) @chmod($local, $conf['fperm']); return $local; } //still here? resizing failed @@ -2149,7 +2149,7 @@ function media_crop_image($file, $ext, $w, $h=0){ if( $mtime > @filemtime($file) || media_crop_imageIM($ext,$file,$info[0],$info[1],$local,$cw,$ch,$cx,$cy) || media_resize_imageGD($ext,$file,$cw,$ch,$local,$cw,$ch,$cx,$cy) ){ - if(!empty($conf['fperm'])) @chmod($local, $conf['fperm']); + if($conf['fperm']) @chmod($local, $conf['fperm']); return media_resize_image($local,$ext, $w, $h); } diff --git a/inc/parser/renderer.php b/inc/parser/renderer.php index 6aba6506c..a03b84c8e 100644 --- a/inc/parser/renderer.php +++ b/inc/parser/renderer.php @@ -845,10 +845,13 @@ abstract class Doku_Renderer extends Plugin { //get interwiki URL if(isset($this->interwiki[$shortcut])) { $url = $this->interwiki[$shortcut]; - } else { - // Default to Google I'm feeling lucky - $url = 'https://www.google.com/search?q={URL}&btnI=lucky'; - $shortcut = 'go'; + }elseif(isset($this->interwiki['default'])) { + $shortcut = 'default'; + $url = $this->interwiki[$shortcut]; + }else{ + // not parsable interwiki outputs '' to make sure string manipluation works + $shortcut = ''; + $url = ''; } //split into hash and url part @@ -880,8 +883,9 @@ abstract class Doku_Renderer extends Plugin { '{PATH}' => $parsed['path'], '{QUERY}' => $parsed['query'] , ]); - } else { - //default + } else if($url != '') { + // make sure when no url is defined, we keep it null + // default $url = $url.rawurlencode($reference); } //handle as wiki links diff --git a/inc/parser/xhtml.php b/inc/parser/xhtml.php index 662883416..393d06d65 100644 --- a/inc/parser/xhtml.php +++ b/inc/parser/xhtml.php @@ -1062,11 +1062,13 @@ class Doku_Renderer_xhtml extends Doku_Renderer { $link['url'] = $url; $link['title'] = htmlspecialchars($link['url']); - //output formatted + // output formatted if($returnonly) { + if($url == '') return $link['name']; return $this->_formatLink($link); } else { - $this->doc .= $this->_formatLink($link); + if($url == '') $this->doc .= $link['name']; + else $this->doc .= $this->_formatLink($link); } } @@ -1248,9 +1250,17 @@ class Doku_Renderer_xhtml extends Doku_Renderer { list($shortcut, $reference) = explode('>', $src, 2); $exists = null; $src = $this->_resolveInterWiki($shortcut, $reference, $exists); + if($src == '' && empty($title)){ + // make sure at least something will be shown in this case + $title = $reference; + } } list($src, $hash) = explode('#', $src, 2); $noLink = false; + if($src == '') { + // only output plaintext without link if there is no src + $noLink = true; + } $render = ($linking == 'linkonly') ? false : true; $link = $this->_getMediaLinkConf($src, $title, $align, $width, $height, $cache, $render); @@ -1637,7 +1647,7 @@ class Doku_Renderer_xhtml extends Doku_Renderer { if(!$render) { // if the picture is not supposed to be rendered // return the title of the picture - if(!$title) { + if($title === null || $title === "") { // just show the sourcename $title = $this->_xmlEntities(\dokuwiki\Utf8\PhpString::basename(noNS($src))); } @@ -1671,11 +1681,11 @@ class Doku_Renderer_xhtml extends Doku_Renderer { } elseif(media_supportedav($mime, 'video') || media_supportedav($mime, 'audio')) { // first get the $title - $title = !is_null($title) ? $this->_xmlEntities($title) : false; + $title = !is_null($title) ? $title : false; if(!$render) { // if the file is not supposed to be rendered // return the title of the file (just the sourcename if there is no title) - return $title ? $title : $this->_xmlEntities(\dokuwiki\Utf8\PhpString::basename(noNS($src))); + return $this->_xmlEntities($title ? $title : \dokuwiki\Utf8\PhpString::basename(noNS($src))); } $att = array(); diff --git a/install.php b/install.php index 55dac2570..253cd3550 100644 --- a/install.php +++ b/install.php @@ -565,6 +565,20 @@ function check_functions(){ $ok = false; } + try { + random_bytes(1); + } catch (\Exception $th) { + // If an appropriate source of randomness cannot be found, an Exception will be thrown by PHP 7+ + // this exception is also thrown by paragonie/random_compat for PHP 5.6 support + $error[] = $lang['i_urandom']; + $ok = false; + } + + if(ini_get('mbstring.func_overload') != 0){ + $error[] = $lang['i_mbfuncoverload']; + $ok = false; + } + $funcs = explode(' ','addslashes call_user_func chmod copy fgets '. 'file file_exists fseek flush filesize ftell fopen '. 'glob header ignore_user_abort ini_get mail mkdir '. diff --git a/lib/plugins/config/lang/el/lang.php b/lib/plugins/config/lang/el/lang.php index 0b403b775..625f5a05b 100644 --- a/lib/plugins/config/lang/el/lang.php +++ b/lib/plugins/config/lang/el/lang.php @@ -3,6 +3,7 @@ /** * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) * + * @author Apostolos Tsompanopoulos <info@aptlogs.com> * @author Katerina Katapodi <extragold1234@hotmail.com> * @author Christopher Smith <chris@jalakai.co.uk> * @author Thanos Massias <tm@thriasio.gr> diff --git a/lib/plugins/config/lang/en/lang.php b/lib/plugins/config/lang/en/lang.php index 3a408e0e3..fb8186cd8 100644 --- a/lib/plugins/config/lang/en/lang.php +++ b/lib/plugins/config/lang/en/lang.php @@ -155,6 +155,7 @@ $lang['rss_linkto'] = 'XML feed links to'; $lang['rss_content'] = 'What to display in the XML feed items?'; $lang['rss_update'] = 'XML feed update interval (sec)'; $lang['rss_show_summary'] = 'XML feed show summary in title'; +$lang['rss_show_deleted'] = 'XML feed Show deleted feeds'; $lang['rss_media'] = 'What kind of changes should be listed in the XML feed?'; $lang['rss_media_o_both'] = 'both'; $lang['rss_media_o_pages'] = 'pages'; diff --git a/lib/plugins/config/lang/ja/lang.php b/lib/plugins/config/lang/ja/lang.php index d00e4c456..76d254142 100644 --- a/lib/plugins/config/lang/ja/lang.php +++ b/lib/plugins/config/lang/ja/lang.php @@ -131,6 +131,7 @@ $lang['rss_linkto'] = 'XMLフィード内リンク先'; $lang['rss_content'] = 'XMLフィードに表示する内容'; $lang['rss_update'] = 'XMLフィードの更新間隔(秒)'; $lang['rss_show_summary'] = 'XMLフィードのタイトルにサマリーを表示'; +$lang['rss_show_deleted'] = '削除されたフィードを含める'; $lang['rss_media'] = 'XMLフィードで、どんな種類の変更を記載するか'; $lang['rss_media_o_both'] = '両方'; $lang['rss_media_o_pages'] = 'ページ'; @@ -159,6 +160,8 @@ $lang['search_fragment_o_starts_with'] = '前方一致'; $lang['search_fragment_o_ends_with'] = '後方一致'; $lang['search_fragment_o_contains'] = '部分一致'; $lang['trustedproxy'] = '報告される真のクライアントIPに関して、ここで指定する正規表現に合う転送プロキシを信頼します。あらゆるプロキシを信頼する場合は、何も入力しないでおいて下さい。'; +$lang['_feature_flags'] = '機能フラグ'; +$lang['defer_js'] = 'ページのHTMLが解析されるまでJavascriptの実行を延期する(ページの読み込み速度が向上しますが、一部のプラグインが正常に動作しない可能性があります)'; $lang['dnslookups'] = 'ページを編集しているユーザーのIPアドレスからホスト名を逆引きする(利用できるDNSサーバーがない、あるいはこの機能が不要な場合にはオフにします。)'; $lang['jquerycdn'] = 'コンテンツ・デリバリー・ネットワーク (CDN) の選択(jQuery と jQuery UI スクリプトを CDN からロードさせる場合には、追加的な HTTP リクエストが発生しますが、ブラウザキャッシュが使用されるため、表示速度の向上が期待できます。)'; $lang['jquerycdn_o_0'] = 'CDN を使用せず、ローカルデリバリーのみ使用する'; diff --git a/lib/plugins/config/settings/config.metadata.php b/lib/plugins/config/settings/config.metadata.php index cd818cb88..6fdd64d6e 100644 --- a/lib/plugins/config/settings/config.metadata.php +++ b/lib/plugins/config/settings/config.metadata.php @@ -208,6 +208,7 @@ $meta['rss_content'] = array('multichoice','_choices' => array('abstract','diff' $meta['rss_media'] = array('multichoice','_choices' => array('both','pages','media')); $meta['rss_update'] = array('numeric'); $meta['rss_show_summary'] = array('onoff'); +$meta['rss_show_deleted'] = array('onoff'); $meta['_advanced'] = array('fieldset'); $meta['updatecheck'] = array('onoff'); diff --git a/lib/plugins/extension/helper/extension.php b/lib/plugins/extension/helper/extension.php index 47fe87373..3e7087eba 100644 --- a/lib/plugins/extension/helper/extension.php +++ b/lib/plugins/extension/helper/extension.php @@ -1245,10 +1245,10 @@ class helper_plugin_extension_extension extends DokuWiki_Plugin closedir($dh); return $ok; } else { - $exists = file_exists($dst); + $existed = file_exists($dst); if (!@copy($src, $dst)) return false; - if (!$exists && !empty($conf['fperm'])) chmod($dst, $conf['fperm']); + if (!$existed && $conf['fperm']) chmod($dst, $conf['fperm']); @touch($dst, filemtime($src)); } diff --git a/lib/plugins/usermanager/_test/csv_import.test.php b/lib/plugins/usermanager/_test/csv_import.test.php index 6047a9342..13034a012 100644 --- a/lib/plugins/usermanager/_test/csv_import.test.php +++ b/lib/plugins/usermanager/_test/csv_import.test.php @@ -14,13 +14,14 @@ require_once(dirname(__FILE__).'/mocks.class.php'); * * At present, users imported in individual tests remain in the user list for subsequent tests */ -class plugin_usermanager_csv_import_test extends DokuWikiTest { - +class plugin_usermanager_csv_import_test extends DokuWikiTest +{ private $old_files; protected $usermanager; protected $importfile; - function setUp() { + public function setUp() + { $this->importfile = tempnam(TMP_DIR, 'csv'); $this->old_files = $_FILES; @@ -38,16 +39,18 @@ class plugin_usermanager_csv_import_test extends DokuWikiTest { parent::setUp(); } - function tearDown() { + public function tearDown() + { $_FILES = $this->old_files; parent::tearDown(); } - function doImportTest($importCsv, $expectedResult, $expectedNewUsers, $expectedFailures) { + public function doImportTest($importCsv, $expectedResult, $expectedNewUsers, $expectedFailures) + { global $auth; $before_users = $auth->retrieveUsers(); - io_savefile($this->importfile, $importCsv); + io_saveFile($this->importfile, $importCsv); $result = $this->usermanager->tryImport(); $after_users = $auth->retrieveUsers(); @@ -69,7 +72,8 @@ class plugin_usermanager_csv_import_test extends DokuWikiTest { $this->assertEquals($expectedFailures, $this->usermanager->getImportFailures()); // failures as expected } - function test_cantImport(){ + public function test_cantImport() + { global $auth; $oldauth = $auth; @@ -85,7 +89,8 @@ importuser,"Ford Prefect",ford@example.com,user $auth = $oldauth; } - function test_import() { + public function test_import() + { $csv = 'User,"Real Name",Email,Groups importuser,"Ford Prefect",ford@example.com,user '; @@ -100,7 +105,8 @@ importuser,"Ford Prefect",ford@example.com,user $this->doImportTest($csv, true, $expected, array()); } - function test_importExisting() { + public function test_importExisting() + { $csv = 'User,"Real Name",Email,Groups importuser,"Ford Prefect",ford@example.com,user '; @@ -120,7 +126,8 @@ importuser,"Ford Prefect",ford@example.com,user $this->doImportTest($csv, true, array(), $failures); } - function test_importUtf8() { + public function test_importUtf8() + { $csv = 'User,"Real Name",Email,Groups importutf8,"Førd Prefect",ford@example.com,user '; @@ -138,7 +145,8 @@ importutf8,"Førd Prefect",ford@example.com,user /** * utf8: u+00F8 (ø) <=> 0xF8 :iso-8859-1 */ - function test_importIso8859() { + public function test_importIso8859() + { $csv = 'User,"Real Name",Email,Groups importiso8859,"F'.chr(0xF8).'rd Prefect",ford@example.com,user '; @@ -153,14 +161,16 @@ importiso8859,"F'.chr(0xF8).'rd Prefect",ford@example.com,user $this->doImportTest($csv, true, $expected, array()); } - private function stripPasswords($array){ + private function stripPasswords($array) + { foreach ($array as $user => $data) { unset($array[$user]['pass']); } return $array; } - private function countPasswords($array){ + private function countPasswords($array) + { $count = 0; foreach ($array as $user => $data) { if (!empty($data['pass'])) { @@ -169,6 +179,4 @@ importiso8859,"F'.chr(0xF8).'rd Prefect",ford@example.com,user } return $count; } - } - diff --git a/lib/tpl/dokuwiki/script.js b/lib/tpl/dokuwiki/script.js index 5a68e8b9c..88dae9023 100644 --- a/lib/tpl/dokuwiki/script.js +++ b/lib/tpl/dokuwiki/script.js @@ -71,10 +71,15 @@ jQuery(function(){ ); // increase sidebar length to match content (desktop mode only) - var $sidebar = jQuery('.desktop #dokuwiki__aside'); - if($sidebar.length) { + var sidebar_height = jQuery('.desktop #dokuwiki__aside').height(); + var pagetool_height = jQuery('.desktop #dokuwiki__pagetools ul:first').height(); + // pagetools div has no height; ul has a height + var content_min = Math.max(sidebar_height || 0, pagetool_height || 0); + + var content_height = jQuery('#dokuwiki__content div.page').height(); + if(content_min && content_min > content_height) { var $content = jQuery('#dokuwiki__content div.page'); - $content.css('min-height', $sidebar.height()); + $content.css('min-height', content_min); } // blur when clicked |