aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/inc
diff options
context:
space:
mode:
Diffstat (limited to 'inc')
-rw-r--r--inc/Action/Resendpwd.php4
-rw-r--r--inc/Ajax.php6
-rw-r--r--inc/Cache/Cache.php15
-rw-r--r--inc/Cache/CacheInstructions.php2
-rw-r--r--inc/Debug/DebugHelper.php42
-rw-r--r--inc/Extension/Event.php7
-rw-r--r--inc/Extension/SyntaxPlugin.php4
-rw-r--r--inc/Form/LegacyForm.php2
-rw-r--r--inc/IXR_Library.php3
-rw-r--r--inc/JpegMeta.php76
-rw-r--r--inc/Mailer.class.php14
-rw-r--r--inc/Menu/Item/AbstractItem.php2
-rw-r--r--inc/Parsing/Parser.php16
-rw-r--r--inc/PassHash.php44
-rw-r--r--inc/Remote/ApiCore.php2
-rw-r--r--inc/Remote/XmlRpcServer.php4
-rw-r--r--inc/Search/Indexer.php1214
-rw-r--r--inc/Subscriptions/MediaSubscriptionSender.php5
-rw-r--r--inc/Subscriptions/PageSubscriptionSender.php5
-rw-r--r--inc/Utf8/Clean.php4
-rw-r--r--inc/Utf8/Unicode.php2
-rw-r--r--inc/auth.php22
-rw-r--r--inc/changelog.php12
-rw-r--r--inc/cli.php4
-rw-r--r--inc/common.php76
-rw-r--r--inc/deprecated.php5
-rw-r--r--inc/fulltext.php12
-rw-r--r--inc/html.php12
-rw-r--r--inc/indexer.php1214
-rw-r--r--inc/init.php6
-rw-r--r--inc/io.php2
-rw-r--r--inc/lang/cs/lang.php1
-rw-r--r--inc/lang/de/lang.php2
-rw-r--r--inc/lang/en/mailtext.txt19
-rw-r--r--inc/lang/en/subscr_single.txt10
-rw-r--r--inc/lang/es/lang.php2
-rw-r--r--inc/lang/fi/lang.php79
-rw-r--r--inc/lang/fr/lang.php6
-rw-r--r--inc/lang/id/lang.php77
-rw-r--r--inc/lang/ja/lang.php14
-rw-r--r--inc/lang/ja/locked.txt2
-rw-r--r--inc/lang/ja/mailtext.txt18
-rw-r--r--inc/lang/ja/preview.txt2
-rw-r--r--inc/lang/ja/registermail.txt14
-rw-r--r--inc/lang/ja/resetpwd.txt2
-rw-r--r--inc/lang/ja/subscr_digest.txt5
-rw-r--r--inc/lang/ja/subscr_list.txt6
-rw-r--r--inc/lang/ja/subscr_single.txt15
-rw-r--r--inc/lang/ja/updateprofile.txt2
-rw-r--r--inc/lang/ja/uploadmail.txt17
-rw-r--r--inc/lang/nl/lang.php1
-rw-r--r--inc/lang/no/lang.php3
-rw-r--r--inc/lang/pt-br/lang.php1
-rw-r--r--inc/lang/pt/lang.php2
-rw-r--r--inc/lang/ru/lang.php6
-rw-r--r--inc/lang/ru/uploadmail.txt2
-rw-r--r--inc/lang/sk/lang.php1
-rw-r--r--inc/lang/sr/admin.txt2
-rw-r--r--inc/mail.php4
-rw-r--r--inc/media.php10
-rw-r--r--inc/pageutils.php4
-rw-r--r--inc/parser/handler.php10
-rw-r--r--inc/parser/metadata.php2
-rw-r--r--inc/parser/parser.php44
-rw-r--r--inc/parser/renderer.php4
-rw-r--r--inc/parser/xhtml.php4
-rw-r--r--inc/pluginutils.php28
-rw-r--r--inc/template.php23
68 files changed, 1738 insertions, 1544 deletions
diff --git a/inc/Action/Resendpwd.php b/inc/Action/Resendpwd.php
index f3f8d3bad..dfa4a99d0 100644
--- a/inc/Action/Resendpwd.php
+++ b/inc/Action/Resendpwd.php
@@ -73,7 +73,7 @@ class Resendpwd extends AbstractAclAction {
if($token) {
// we're in token phase - get user info from token
- $tfile = $conf['cachedir'] . '/' . $token{0} . '/' . $token . '.pwauth';
+ $tfile = $conf['cachedir'] . '/' . $token[0] . '/' . $token . '.pwauth';
if(!file_exists($tfile)) {
msg($lang['resendpwdbadauth'], -1);
$INPUT->remove('pwauth');
@@ -148,7 +148,7 @@ class Resendpwd extends AbstractAclAction {
// generate auth token
$token = md5(auth_randombytes(16)); // random secret
- $tfile = $conf['cachedir'] . '/' . $token{0} . '/' . $token . '.pwauth';
+ $tfile = $conf['cachedir'] . '/' . $token[0] . '/' . $token . '.pwauth';
$url = wl('', array('do' => 'resendpwd', 'pwauth' => $token), true, '&');
io_saveFile($tfile, $user);
diff --git a/inc/Ajax.php b/inc/Ajax.php
index e8cbc84d5..9f4b31668 100644
--- a/inc/Ajax.php
+++ b/inc/Ajax.php
@@ -257,7 +257,7 @@ class Ajax {
global $NS, $MSG, $INPUT;
$id = '';
- if($_FILES['qqfile']['tmp_name']) {
+ if(isset($_FILES['qqfile']['tmp_name'])) {
$id = $INPUT->post->str('mediaid', $_FILES['qqfile']['name']);
} elseif($INPUT->get->has('qqfile')) {
$id = $INPUT->get->str('qqfile');
@@ -273,10 +273,10 @@ class Ajax {
io_createNamespace("$ns:xxx", 'media');
}
- if($_FILES['qqfile']['error']) unset($_FILES['qqfile']);
+ if(isset($_FILES['qqfile']['error']) && $_FILES['qqfile']['error']) unset($_FILES['qqfile']);
$res = false;
- if($_FILES['qqfile']['tmp_name']) $res = media_upload($NS, $AUTH, $_FILES['qqfile']);
+ if(isset($_FILES['qqfile']['tmp_name'])) $res = media_upload($NS, $AUTH, $_FILES['qqfile']);
if($INPUT->get->has('qqfile')) $res = media_upload_xhr($NS, $AUTH);
if($res) {
diff --git a/inc/Cache/Cache.php b/inc/Cache/Cache.php
index 599dc5f59..af82e6bf6 100644
--- a/inc/Cache/Cache.php
+++ b/inc/Cache/Cache.php
@@ -2,7 +2,7 @@
namespace dokuwiki\Cache;
-use \dokuwiki\Debug\PropertyDeprecationHelper;
+use dokuwiki\Debug\PropertyDeprecationHelper;
use dokuwiki\Extension\Event;
/**
@@ -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/Debug/DebugHelper.php b/inc/Debug/DebugHelper.php
index 3d4b20d62..74dfa9420 100644
--- a/inc/Debug/DebugHelper.php
+++ b/inc/Debug/DebugHelper.php
@@ -84,6 +84,48 @@ class DebugHelper
}
/**
+ * Trigger a custom deprecation event
+ *
+ * Usually dbgDeprecatedFunction() or dbgDeprecatedProperty() should be used instead.
+ * This method is intended only for those situation where they are not applicable.
+ *
+ * @param string $alternative
+ * @param string $deprecatedThing
+ * @param string $caller
+ * @param string $file
+ * @param int $line
+ * @param int $callerOffset How many lines should be removed from the beginning of the backtrace
+ */
+ public static function dbgCustomDeprecationEvent(
+ $alternative,
+ $deprecatedThing,
+ $caller,
+ $file,
+ $line,
+ $callerOffset = 1
+ ) {
+ global $conf;
+ /** @var EventHandler $EVENT_HANDLER */
+ global $EVENT_HANDLER;
+ if (!$conf['allowdebug'] && !$EVENT_HANDLER->hasHandlerForEvent(self::INFO_DEPRECATION_LOG_EVENT)) {
+ // avoid any work if no one cares
+ return;
+ }
+
+ $backtrace = array_slice(debug_backtrace(), $callerOffset);
+
+ self::triggerDeprecationEvent(
+ $backtrace,
+ $alternative,
+ $deprecatedThing,
+ $caller,
+ $file,
+ $line
+ );
+
+ }
+
+ /**
* @param array $backtrace
* @param string $alternative
* @param string $deprecatedThing
diff --git a/inc/Extension/Event.php b/inc/Extension/Event.php
index bbaa52e55..32f346c72 100644
--- a/inc/Extension/Event.php
+++ b/inc/Extension/Event.php
@@ -126,12 +126,7 @@ class Event
}
if ($this->advise_before($enablePrevent) && is_callable($action)) {
- if (is_array($action)) {
- list($obj, $method) = $action;
- $this->result = $obj->$method($this->data);
- } else {
- $this->result = $action($this->data);
- }
+ $this->result = call_user_func_array($action, [&$this->data]);
}
$this->advise_after();
diff --git a/inc/Extension/SyntaxPlugin.php b/inc/Extension/SyntaxPlugin.php
index e5dda9bdc..ea8f51b4d 100644
--- a/inc/Extension/SyntaxPlugin.php
+++ b/inc/Extension/SyntaxPlugin.php
@@ -2,8 +2,8 @@
namespace dokuwiki\Extension;
-use \Doku_Handler;
-use \Doku_Renderer;
+use Doku_Handler;
+use Doku_Renderer;
/**
* Syntax Plugin Prototype
diff --git a/inc/Form/LegacyForm.php b/inc/Form/LegacyForm.php
index 1b47ba204..b30c8df9a 100644
--- a/inc/Form/LegacyForm.php
+++ b/inc/Form/LegacyForm.php
@@ -116,7 +116,7 @@ class LegacyForm extends Form {
$control = array();
foreach($legacy as $key => $val) {
- if($key{0} == '_') {
+ if($key[0] == '_') {
$control[substr($key, 1)] = $val;
} elseif($key == 'name') {
$control[$key] = $val;
diff --git a/inc/IXR_Library.php b/inc/IXR_Library.php
index e671b6880..bb1655f10 100644
--- a/inc/IXR_Library.php
+++ b/inc/IXR_Library.php
@@ -822,6 +822,7 @@ EOD;
*/
class IXR_Date {
+ const XMLRPC_ISO8601 = "Ymd\TH:i:sO" ;
/** @var DateTime */
protected $date;
@@ -861,7 +862,7 @@ class IXR_Date {
* @return string
*/
public function getIso() {
- return $this->date->format(DateTime::ISO8601);
+ return $this->date->format(self::XMLRPC_ISO8601);
}
/**
diff --git a/inc/JpegMeta.php b/inc/JpegMeta.php
index b11f07145..9ed1e2db4 100644
--- a/inc/JpegMeta.php
+++ b/inc/JpegMeta.php
@@ -414,7 +414,7 @@ class JpegMeta {
// make sure datetimes are in correct format
if(strlen($field) >= 8 && strtolower(substr($field, 0, 8)) == 'datetime') {
- if(strlen($value) < 8 || $value{4} != ':' || $value{7} != ':') {
+ if(strlen($value) < 8 || $value[4] != ':' || $value[7] != ':') {
$value = date('Y:m:d H:i:s', strtotime($value));
}
}
@@ -649,8 +649,8 @@ class JpegMeta {
$dates['ExifDateTime'] = $this->_info['exif']['DateTime'];
$aux = $this->_info['exif']['DateTime'];
- $aux{4} = "-";
- $aux{7} = "-";
+ $aux[4] = "-";
+ $aux[7] = "-";
$t = strtotime($aux);
if ($t && $t > $latestTime) {
@@ -668,8 +668,8 @@ class JpegMeta {
$dates['ExifDateTimeOriginal'] = $this->_info['exif']['DateTime'];
$aux = $this->_info['exif']['DateTimeOriginal'];
- $aux{4} = "-";
- $aux{7} = "-";
+ $aux[4] = "-";
+ $aux[7] = "-";
$t = strtotime($aux);
if ($t && $t > $latestTime) {
@@ -687,8 +687,8 @@ class JpegMeta {
$dates['ExifDateTimeDigitized'] = $this->_info['exif']['DateTime'];
$aux = $this->_info['exif']['DateTimeDigitized'];
- $aux{4} = "-";
- $aux{7} = "-";
+ $aux[4] = "-";
+ $aux[7] = "-";
$t = strtotime($aux);
if ($t && $t > $latestTime) {
@@ -2402,7 +2402,7 @@ class JpegMeta {
$pos += 1;
$header = '';
for ($i = 0; $i < $strlen; $i++) {
- $header .= $data{$pos + $i};
+ $header .= $data[$pos + $i];
}
$pos += $strlen + 1 - ($strlen % 2); // The string is padded to even length, counting the length byte itself
@@ -2983,7 +2983,7 @@ class JpegMeta {
* @return int
*/
function _getByte(&$data, $pos) {
- return ord($data{$pos});
+ return ord($data[$pos]);
}
/*************************************************************/
@@ -2999,7 +2999,7 @@ class JpegMeta {
function _putByte(&$data, $pos, $val) {
$val = intval($val);
- $data{$pos} = chr($val);
+ $data[$pos] = chr($val);
return $pos + 1;
}
@@ -3007,11 +3007,11 @@ class JpegMeta {
/*************************************************************/
function _getShort(&$data, $pos, $bigEndian = true) {
if ($bigEndian) {
- return (ord($data{$pos}) << 8)
- + ord($data{$pos + 1});
+ return (ord($data[$pos]) << 8)
+ + ord($data[$pos + 1]);
} else {
- return ord($data{$pos})
- + (ord($data{$pos + 1}) << 8);
+ return ord($data[$pos])
+ + (ord($data[$pos + 1]) << 8);
}
}
@@ -3020,11 +3020,11 @@ class JpegMeta {
$val = intval($val);
if ($bigEndian) {
- $data{$pos + 0} = chr(($val & 0x0000FF00) >> 8);
- $data{$pos + 1} = chr(($val & 0x000000FF) >> 0);
+ $data[$pos + 0] = chr(($val & 0x0000FF00) >> 8);
+ $data[$pos + 1] = chr(($val & 0x000000FF) >> 0);
} else {
- $data{$pos + 0} = chr(($val & 0x00FF) >> 0);
- $data{$pos + 1} = chr(($val & 0xFF00) >> 8);
+ $data[$pos + 0] = chr(($val & 0x00FF) >> 0);
+ $data[$pos + 1] = chr(($val & 0xFF00) >> 8);
}
return $pos + 2;
@@ -3042,15 +3042,15 @@ class JpegMeta {
*/
function _getLong(&$data, $pos, $bigEndian = true) {
if ($bigEndian) {
- return (ord($data{$pos}) << 24)
- + (ord($data{$pos + 1}) << 16)
- + (ord($data{$pos + 2}) << 8)
- + ord($data{$pos + 3});
+ return (ord($data[$pos]) << 24)
+ + (ord($data[$pos + 1]) << 16)
+ + (ord($data[$pos + 2]) << 8)
+ + ord($data[$pos + 3]);
} else {
- return ord($data{$pos})
- + (ord($data{$pos + 1}) << 8)
- + (ord($data{$pos + 2}) << 16)
- + (ord($data{$pos + 3}) << 24);
+ return ord($data[$pos])
+ + (ord($data[$pos + 1]) << 8)
+ + (ord($data[$pos + 2]) << 16)
+ + (ord($data[$pos + 3]) << 24);
}
}
@@ -3069,15 +3069,15 @@ class JpegMeta {
$val = intval($val);
if ($bigEndian) {
- $data{$pos + 0} = chr(($val & 0xFF000000) >> 24);
- $data{$pos + 1} = chr(($val & 0x00FF0000) >> 16);
- $data{$pos + 2} = chr(($val & 0x0000FF00) >> 8);
- $data{$pos + 3} = chr(($val & 0x000000FF) >> 0);
+ $data[$pos + 0] = chr(($val & 0xFF000000) >> 24);
+ $data[$pos + 1] = chr(($val & 0x00FF0000) >> 16);
+ $data[$pos + 2] = chr(($val & 0x0000FF00) >> 8);
+ $data[$pos + 3] = chr(($val & 0x000000FF) >> 0);
} else {
- $data{$pos + 0} = chr(($val & 0x000000FF) >> 0);
- $data{$pos + 1} = chr(($val & 0x0000FF00) >> 8);
- $data{$pos + 2} = chr(($val & 0x00FF0000) >> 16);
- $data{$pos + 3} = chr(($val & 0xFF000000) >> 24);
+ $data[$pos + 0] = chr(($val & 0x000000FF) >> 0);
+ $data[$pos + 1] = chr(($val & 0x0000FF00) >> 8);
+ $data[$pos + 2] = chr(($val & 0x00FF0000) >> 16);
+ $data[$pos + 3] = chr(($val & 0xFF000000) >> 24);
}
return $pos + 4;
@@ -3089,10 +3089,10 @@ class JpegMeta {
$max = strlen($data);
while ($pos < $max) {
- if (ord($data{$pos}) == 0) {
+ if (ord($data[$pos]) == 0) {
return $str;
} else {
- $str .= $data{$pos};
+ $str .= $data[$pos];
}
$pos++;
}
@@ -3114,7 +3114,7 @@ class JpegMeta {
function _putString(&$data, $pos, &$str) {
$len = strlen($str);
for ($i = 0; $i < $len; $i++) {
- $data{$pos + $i} = $str{$i};
+ $data[$pos + $i] = $str[$i];
}
return $pos + $len;
@@ -3138,7 +3138,7 @@ class JpegMeta {
echo sprintf('%04d', $count) . ': ';
}
- $c = ord($data{$start});
+ $c = ord($data[$start]);
$count++;
$start++;
diff --git a/inc/Mailer.class.php b/inc/Mailer.class.php
index 328073bc1..db9a2bdc2 100644
--- a/inc/Mailer.class.php
+++ b/inc/Mailer.class.php
@@ -350,7 +350,7 @@ class Mailer {
* addresses. Addresses must be separated by a comma. If the display
* name includes a comma then it MUST be properly enclosed by '"' to
* prevent spliting at the wrong point.
- *
+ *
* Example:
* cc("föö <foo@bar.com>, me@somewhere.com","TBcc");
* to("foo, Dr." <foo@bar.com>, me@somewhere.com");
@@ -635,6 +635,8 @@ class Mailer {
$ip = clientIP();
$cip = gethostsbyaddrs($ip);
+ $name = isset($INFO) ? $INFO['userinfo']['name'] : '';
+ $mail = isset($INFO) ? $INFO['userinfo']['mail'] : '';
$this->replacements['text'] = array(
'DATE' => dformat(),
@@ -644,8 +646,8 @@ class Mailer {
'TITLE' => $conf['title'],
'DOKUWIKIURL' => DOKU_URL,
'USER' => $INPUT->server->str('REMOTE_USER'),
- 'NAME' => $INFO['userinfo']['name'],
- 'MAIL' => $INFO['userinfo']['mail']
+ 'NAME' => $name,
+ 'MAIL' => $mail
);
$signature = str_replace(
'@DOKUWIKIURL@',
@@ -662,9 +664,9 @@ class Mailer {
'TITLE' => hsc($conf['title']),
'DOKUWIKIURL' => '<a href="' . DOKU_URL . '">' . DOKU_URL . '</a>',
'USER' => hsc($INPUT->server->str('REMOTE_USER')),
- 'NAME' => hsc($INFO['userinfo']['name']),
- 'MAIL' => '<a href="mailto:"' . hsc($INFO['userinfo']['mail']) . '">' .
- hsc($INFO['userinfo']['mail']) . '</a>'
+ 'NAME' => hsc($name),
+ 'MAIL' => '<a href="mailto:"' . hsc($mail) . '">' .
+ hsc($mail) . '</a>'
);
$signature = $lang['email_signature_text'];
if(!empty($lang['email_signature_html'])) {
diff --git a/inc/Menu/Item/AbstractItem.php b/inc/Menu/Item/AbstractItem.php
index 45ead5562..c6b04bfd3 100644
--- a/inc/Menu/Item/AbstractItem.php
+++ b/inc/Menu/Item/AbstractItem.php
@@ -114,7 +114,7 @@ abstract class AbstractItem {
* @return string
*/
public function getLink() {
- if($this->id[0] == '#') {
+ if($this->id && $this->id[0] == '#') {
return $this->id;
} else {
return wl($this->id, $this->params, false, '&');
diff --git a/inc/Parsing/Parser.php b/inc/Parsing/Parser.php
index b2070569f..63f014161 100644
--- a/inc/Parsing/Parser.php
+++ b/inc/Parsing/Parser.php
@@ -107,7 +107,21 @@ class Parser {
// Normalize CRs and pad doc
$doc = "\n" . str_replace("\r\n", "\n", $doc) . "\n";
$this->lexer->parse($doc);
- $this->handler->finalize();
+
+ if (!method_exists($this->handler, 'finalize')) {
+ /** @deprecated 2019-10 we have a legacy handler from a plugin, assume legacy _finalize exists */
+
+ \dokuwiki\Debug\DebugHelper::dbgCustomDeprecationEvent(
+ 'finalize()',
+ get_class($this->handler) . '::_finalize()',
+ __METHOD__,
+ __FILE__,
+ __LINE__
+ );
+ $this->handler->_finalize();
+ } else {
+ $this->handler->finalize();
+ }
return $this->handler->calls;
}
diff --git a/inc/PassHash.php b/inc/PassHash.php
index 8ac623cd1..c0e61f409 100644
--- a/inc/PassHash.php
+++ b/inc/PassHash.php
@@ -77,6 +77,11 @@ class PassHash {
$method = 'sha512';
$salt = $m[2];
$magic = $m[1];
+ } elseif(preg_match('/^\$(argon2id?)/', $hash, $m)) {
+ if(!defined('PASSWORD_'.strtoupper($m[1]))) {
+ throw new \Exception('This PHP installation has no '.strtoupper($m[1]).' support');
+ }
+ return password_verify($clear,$hash);
} elseif($len == 32) {
$method = 'md5';
} elseif($len == 40) {
@@ -206,7 +211,7 @@ class PassHash {
$text .= substr($bin, 0, min(16, $i));
}
for($i = $len; $i > 0; $i >>= 1) {
- $text .= ($i & 1) ? chr(0) : $clear{0};
+ $text .= ($i & 1) ? chr(0) : $clear[0];
}
$bin = pack("H32", md5($text));
for($i = 0; $i < 1000; $i++) {
@@ -591,6 +596,43 @@ class PassHash {
return ':B:'.$salt.':'.md5($salt.'-'.md5($clear));
}
+
+ /**
+ * Password hashing method 'argon2i'
+ *
+ * Uses php's own password_hash function to create argon2i password hash
+ * Default Cost and thread options are used for now.
+ *
+ * @link https://www.php.net/manual/de/function.password-hash.php
+ *
+ * @param string $clear The clear text to hash
+ * @return string Hashed password
+ */
+ public function hash_argon2i($clear) {
+ if(!defined('PASSWORD_ARGON2I')) {
+ throw new \Exception('This PHP installation has no ARGON2I support');
+ }
+ return password_hash($clear,PASSWORD_ARGON2I);
+ }
+
+ /**
+ * Password hashing method 'argon2id'
+ *
+ * Uses php's own password_hash function to create argon2id password hash
+ * Default Cost and thread options are used for now.
+ *
+ * @link https://www.php.net/manual/de/function.password-hash.php
+ *
+ * @param string $clear The clear text to hash
+ * @return string Hashed password
+ */
+ public function hash_argon2id($clear) {
+ if(!defined('PASSWORD_ARGON2ID')) {
+ throw new \Exception('This PHP installation has no ARGON2ID support');
+ }
+ return password_hash($clear,PASSWORD_ARGON2ID);
+ }
+
/**
* Wraps around native hash_hmac() or reimplents it
*
diff --git a/inc/Remote/ApiCore.php b/inc/Remote/ApiCore.php
index 9a041452d..12a32b8a4 100644
--- a/inc/Remote/ApiCore.php
+++ b/inc/Remote/ApiCore.php
@@ -489,7 +489,7 @@ class ApiCore
$data = array(
'name' => $id,
'lastModified' => $this->api->toDate($rev),
- 'author' => (($info['user']) ? $info['user'] : $info['ip']),
+ 'author' => is_array($info) ? (($info['user']) ? $info['user'] : $info['ip']) : null,
'version' => $rev
);
diff --git a/inc/Remote/XmlRpcServer.php b/inc/Remote/XmlRpcServer.php
index 1b0097856..0a16af1cc 100644
--- a/inc/Remote/XmlRpcServer.php
+++ b/inc/Remote/XmlRpcServer.php
@@ -12,12 +12,12 @@ class XmlRpcServer extends \IXR_Server
/**
* Constructor. Register methods and run Server
*/
- public function __construct()
+ public function __construct($wait=false)
{
$this->remote = new Api();
$this->remote->setDateTransformation(array($this, 'toDate'));
$this->remote->setFileTransformation(array($this, 'toFile'));
- parent::__construct();
+ parent::__construct(false, false, $wait);
}
/**
diff --git a/inc/Search/Indexer.php b/inc/Search/Indexer.php
new file mode 100644
index 000000000..a29e5b28b
--- /dev/null
+++ b/inc/Search/Indexer.php
@@ -0,0 +1,1214 @@
+<?php
+
+namespace dokuwiki\Search;
+
+use dokuwiki\Extension\Event;
+
+/**
+ * Class that encapsulates operations on the indexer database.
+ *
+ * @author Tom N Harris <tnharris@whoopdedo.org>
+ */
+class Indexer {
+ /**
+ * @var array $pidCache Cache for getPID()
+ */
+ protected $pidCache = array();
+
+ /**
+ * Adds the contents of a page to the fulltext index
+ *
+ * The added text replaces previous words for the same page.
+ * An empty value erases the page.
+ *
+ * @param string $page a page name
+ * @param string $text the body of the page
+ * @return string|boolean the function completed successfully
+ *
+ * @author Tom N Harris <tnharris@whoopdedo.org>
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+ public function addPageWords($page, $text) {
+ if (!$this->lock())
+ return "locked";
+
+ // load known documents
+ $pid = $this->getPIDNoLock($page);
+ if ($pid === false) {
+ $this->unlock();
+ return false;
+ }
+
+ $pagewords = array();
+ // get word usage in page
+ $words = $this->getPageWords($text);
+ if ($words === false) {
+ $this->unlock();
+ return false;
+ }
+
+ if (!empty($words)) {
+ foreach (array_keys($words) as $wlen) {
+ $index = $this->getIndex('i', $wlen);
+ foreach ($words[$wlen] as $wid => $freq) {
+ $idx = ($wid<count($index)) ? $index[$wid] : '';
+ $index[$wid] = $this->updateTuple($idx, $pid, $freq);
+ $pagewords[] = "$wlen*$wid";
+ }
+ if (!$this->saveIndex('i', $wlen, $index)) {
+ $this->unlock();
+ return false;
+ }
+ }
+ }
+
+ // Remove obsolete index entries
+ $pageword_idx = $this->getIndexKey('pageword', '', $pid);
+ if ($pageword_idx !== '') {
+ $oldwords = explode(':',$pageword_idx);
+ $delwords = array_diff($oldwords, $pagewords);
+ $upwords = array();
+ foreach ($delwords as $word) {
+ if ($word != '') {
+ list($wlen, $wid) = explode('*', $word);
+ $wid = (int)$wid;
+ $upwords[$wlen][] = $wid;
+ }
+ }
+ foreach ($upwords as $wlen => $widx) {
+ $index = $this->getIndex('i', $wlen);
+ foreach ($widx as $wid) {
+ $index[$wid] = $this->updateTuple($index[$wid], $pid, 0);
+ }
+ $this->saveIndex('i', $wlen, $index);
+ }
+ }
+ // Save the reverse index
+ $pageword_idx = join(':', $pagewords);
+ if (!$this->saveIndexKey('pageword', '', $pid, $pageword_idx)) {
+ $this->unlock();
+ return false;
+ }
+
+ $this->unlock();
+ return true;
+ }
+
+ /**
+ * Split the words in a page and add them to the index.
+ *
+ * @param string $text content of the page
+ * @return array list of word IDs and number of times used
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ * @author Christopher Smith <chris@jalakai.co.uk>
+ * @author Tom N Harris <tnharris@whoopdedo.org>
+ */
+ protected function getPageWords($text) {
+
+ $tokens = $this->tokenizer($text);
+ $tokens = array_count_values($tokens); // count the frequency of each token
+
+ $words = array();
+ foreach ($tokens as $w=>$c) {
+ $l = wordlen($w);
+ if (isset($words[$l])){
+ $words[$l][$w] = $c + (isset($words[$l][$w]) ? $words[$l][$w] : 0);
+ }else{
+ $words[$l] = array($w => $c);
+ }
+ }
+
+ // arrive here with $words = array(wordlen => array(word => frequency))
+ $word_idx_modified = false;
+ $index = array(); //resulting index
+ foreach (array_keys($words) as $wlen) {
+ $word_idx = $this->getIndex('w', $wlen);
+ foreach ($words[$wlen] as $word => $freq) {
+ $word = (string)$word;
+ $wid = array_search($word, $word_idx, true);
+ if ($wid === false) {
+ $wid = count($word_idx);
+ $word_idx[] = $word;
+ $word_idx_modified = true;
+ }
+ if (!isset($index[$wlen]))
+ $index[$wlen] = array();
+ $index[$wlen][$wid] = $freq;
+ }
+ // save back the word index
+ if ($word_idx_modified && !$this->saveIndex('w', $wlen, $word_idx))
+ return false;
+ }
+
+ return $index;
+ }
+
+ /**
+ * Add/update keys to/of the metadata index.
+ *
+ * Adding new keys does not remove other keys for the page.
+ * An empty value will erase the key.
+ * The $key parameter can be an array to add multiple keys. $value will
+ * not be used if $key is an array.
+ *
+ * @param string $page a page name
+ * @param mixed $key a key string or array of key=>value pairs
+ * @param mixed $value the value or list of values
+ * @return boolean|string the function completed successfully
+ *
+ * @author Tom N Harris <tnharris@whoopdedo.org>
+ * @author Michael Hamann <michael@content-space.de>
+ */
+ public function addMetaKeys($page, $key, $value=null) {
+ if (!is_array($key)) {
+ $key = array($key => $value);
+ } elseif (!is_null($value)) {
+ // $key is array, but $value is not null
+ trigger_error("array passed to addMetaKeys but value is not null", E_USER_WARNING);
+ }
+
+ if (!$this->lock())
+ return "locked";
+
+ // load known documents
+ $pid = $this->getPIDNoLock($page);
+ if ($pid === false) {
+ $this->unlock();
+ return false;
+ }
+
+ // Special handling for titles so the index file is simpler
+ if (array_key_exists('title', $key)) {
+ $value = $key['title'];
+ if (is_array($value)) {
+ $value = $value[0];
+ }
+ $this->saveIndexKey('title', '', $pid, $value);
+ unset($key['title']);
+ }
+
+ foreach ($key as $name => $values) {
+ $metaname = idx_cleanName($name);
+ $this->addIndexKey('metadata', '', $metaname);
+ $metaidx = $this->getIndex($metaname.'_i', '');
+ $metawords = $this->getIndex($metaname.'_w', '');
+ $addwords = false;
+
+ if (!is_array($values)) $values = array($values);
+
+ $val_idx = $this->getIndexKey($metaname.'_p', '', $pid);
+ if ($val_idx !== '') {
+ $val_idx = explode(':', $val_idx);
+ // -1 means remove, 0 keep, 1 add
+ $val_idx = array_combine($val_idx, array_fill(0, count($val_idx), -1));
+ } else {
+ $val_idx = array();
+ }
+
+ foreach ($values as $val) {
+ $val = (string)$val;
+ if ($val !== "") {
+ $id = array_search($val, $metawords, true);
+ if ($id === false) {
+ // didn't find $val, so we'll add it to the end of metawords and create a placeholder in metaidx
+ $id = count($metawords);
+ $metawords[$id] = $val;
+ $metaidx[$id] = '';
+ $addwords = true;
+ }
+ // test if value is already in the index
+ if (isset($val_idx[$id]) && $val_idx[$id] <= 0){
+ $val_idx[$id] = 0;
+ } else { // else add it
+ $val_idx[$id] = 1;
+ }
+ }
+ }
+
+ if ($addwords) {
+ $this->saveIndex($metaname.'_w', '', $metawords);
+ }
+ $vals_changed = false;
+ foreach ($val_idx as $id => $action) {
+ if ($action == -1) {
+ $metaidx[$id] = $this->updateTuple($metaidx[$id], $pid, 0);
+ $vals_changed = true;
+ unset($val_idx[$id]);
+ } elseif ($action == 1) {
+ $metaidx[$id] = $this->updateTuple($metaidx[$id], $pid, 1);
+ $vals_changed = true;
+ }
+ }
+
+ if ($vals_changed) {
+ $this->saveIndex($metaname.'_i', '', $metaidx);
+ $val_idx = implode(':', array_keys($val_idx));
+ $this->saveIndexKey($metaname.'_p', '', $pid, $val_idx);
+ }
+
+ unset($metaidx);
+ unset($metawords);
+ }
+
+ $this->unlock();
+ return true;
+ }
+
+ /**
+ * Rename a page in the search index without changing the indexed content. This function doesn't check if the
+ * old or new name exists in the filesystem. It returns an error if the old page isn't in the page list of the
+ * indexer and it deletes all previously indexed content of the new page.
+ *
+ * @param string $oldpage The old page name
+ * @param string $newpage The new page name
+ * @return string|bool If the page was successfully renamed, can be a message in the case of an error
+ */
+ public function renamePage($oldpage, $newpage) {
+ if (!$this->lock()) return 'locked';
+
+ $pages = $this->getPages();
+
+ $id = array_search($oldpage, $pages, true);
+ if ($id === false) {
+ $this->unlock();
+ return 'page is not in index';
+ }
+
+ $new_id = array_search($newpage, $pages, true);
+ if ($new_id !== false) {
+ // make sure the page is not in the index anymore
+ if ($this->deletePageNoLock($newpage) !== true) {
+ return false;
+ }
+
+ $pages[$new_id] = 'deleted:'.time().rand(0, 9999);
+ }
+
+ $pages[$id] = $newpage;
+
+ // update index
+ if (!$this->saveIndex('page', '', $pages)) {
+ $this->unlock();
+ return false;
+ }
+
+ // reset the pid cache
+ $this->pidCache = array();
+
+ $this->unlock();
+ return true;
+ }
+
+ /**
+ * Renames a meta value in the index. This doesn't change the meta value in the pages, it assumes that all pages
+ * will be updated.
+ *
+ * @param string $key The metadata key of which a value shall be changed
+ * @param string $oldvalue The old value that shall be renamed
+ * @param string $newvalue The new value to which the old value shall be renamed, if exists values will be merged
+ * @return bool|string If renaming the value has been successful, false or error message on error.
+ */
+ public function renameMetaValue($key, $oldvalue, $newvalue) {
+ if (!$this->lock()) return 'locked';
+
+ // change the relation references index
+ $metavalues = $this->getIndex($key, '_w');
+ $oldid = array_search($oldvalue, $metavalues, true);
+ if ($oldid !== false) {
+ $newid = array_search($newvalue, $metavalues, true);
+ if ($newid !== false) {
+ // free memory
+ unset ($metavalues);
+
+ // okay, now we have two entries for the same value. we need to merge them.
+ $indexline = $this->getIndexKey($key.'_i', '', $oldid);
+ if ($indexline != '') {
+ $newindexline = $this->getIndexKey($key.'_i', '', $newid);
+ $pagekeys = $this->getIndex($key.'_p', '');
+ $parts = explode(':', $indexline);
+ foreach ($parts as $part) {
+ list($id, $count) = explode('*', $part);
+ $newindexline = $this->updateTuple($newindexline, $id, $count);
+
+ $keyline = explode(':', $pagekeys[$id]);
+ // remove old meta value
+ $keyline = array_diff($keyline, array($oldid));
+ // add new meta value when not already present
+ if (!in_array($newid, $keyline)) {
+ array_push($keyline, $newid);
+ }
+ $pagekeys[$id] = implode(':', $keyline);
+ }
+ $this->saveIndex($key.'_p', '', $pagekeys);
+ unset($pagekeys);
+ $this->saveIndexKey($key.'_i', '', $oldid, '');
+ $this->saveIndexKey($key.'_i', '', $newid, $newindexline);
+ }
+ } else {
+ $metavalues[$oldid] = $newvalue;
+ if (!$this->saveIndex($key.'_w', '', $metavalues)) {
+ $this->unlock();
+ return false;
+ }
+ }
+ }
+
+ $this->unlock();
+ return true;
+ }
+
+ /**
+ * Remove a page from the index
+ *
+ * Erases entries in all known indexes.
+ *
+ * @param string $page a page name
+ * @return string|boolean the function completed successfully
+ *
+ * @author Tom N Harris <tnharris@whoopdedo.org>
+ */
+ public function deletePage($page) {
+ if (!$this->lock())
+ return "locked";
+
+ $result = $this->deletePageNoLock($page);
+
+ $this->unlock();
+
+ return $result;
+ }
+
+ /**
+ * Remove a page from the index without locking the index, only use this function if the index is already locked
+ *
+ * Erases entries in all known indexes.
+ *
+ * @param string $page a page name
+ * @return boolean the function completed successfully
+ *
+ * @author Tom N Harris <tnharris@whoopdedo.org>
+ */
+ protected function deletePageNoLock($page) {
+ // load known documents
+ $pid = $this->getPIDNoLock($page);
+ if ($pid === false) {
+ return false;
+ }
+
+ // Remove obsolete index entries
+ $pageword_idx = $this->getIndexKey('pageword', '', $pid);
+ if ($pageword_idx !== '') {
+ $delwords = explode(':',$pageword_idx);
+ $upwords = array();
+ foreach ($delwords as $word) {
+ if ($word != '') {
+ list($wlen,$wid) = explode('*', $word);
+ $wid = (int)$wid;
+ $upwords[$wlen][] = $wid;
+ }
+ }
+ foreach ($upwords as $wlen => $widx) {
+ $index = $this->getIndex('i', $wlen);
+ foreach ($widx as $wid) {
+ $index[$wid] = $this->updateTuple($index[$wid], $pid, 0);
+ }
+ $this->saveIndex('i', $wlen, $index);
+ }
+ }
+ // Save the reverse index
+ if (!$this->saveIndexKey('pageword', '', $pid, "")) {
+ return false;
+ }
+
+ $this->saveIndexKey('title', '', $pid, "");
+ $keyidx = $this->getIndex('metadata', '');
+ foreach ($keyidx as $metaname) {
+ $val_idx = explode(':', $this->getIndexKey($metaname.'_p', '', $pid));
+ $meta_idx = $this->getIndex($metaname.'_i', '');
+ foreach ($val_idx as $id) {
+ if ($id === '') continue;
+ $meta_idx[$id] = $this->updateTuple($meta_idx[$id], $pid, 0);
+ }
+ $this->saveIndex($metaname.'_i', '', $meta_idx);
+ $this->saveIndexKey($metaname.'_p', '', $pid, '');
+ }
+
+ return true;
+ }
+
+ /**
+ * Clear the whole index
+ *
+ * @return bool If the index has been cleared successfully
+ */
+ public function clear() {
+ global $conf;
+
+ if (!$this->lock()) return false;
+
+ @unlink($conf['indexdir'].'/page.idx');
+ @unlink($conf['indexdir'].'/title.idx');
+ @unlink($conf['indexdir'].'/pageword.idx');
+ @unlink($conf['indexdir'].'/metadata.idx');
+ $dir = @opendir($conf['indexdir']);
+ if($dir!==false){
+ while(($f = readdir($dir)) !== false){
+ if(substr($f,-4)=='.idx' &&
+ (substr($f,0,1)=='i' || substr($f,0,1)=='w'
+ || substr($f,-6)=='_w.idx' || substr($f,-6)=='_i.idx' || substr($f,-6)=='_p.idx'))
+ @unlink($conf['indexdir']."/$f");
+ }
+ }
+ @unlink($conf['indexdir'].'/lengths.idx');
+
+ // clear the pid cache
+ $this->pidCache = array();
+
+ $this->unlock();
+ return true;
+ }
+
+ /**
+ * Split the text into words for fulltext search
+ *
+ * TODO: does this also need &$stopwords ?
+ *
+ * @triggers INDEXER_TEXT_PREPARE
+ * This event allows plugins to modify the text before it gets tokenized.
+ * Plugins intercepting this event should also intercept INDEX_VERSION_GET
+ *
+ * @param string $text plain text
+ * @param boolean $wc are wildcards allowed?
+ * @return array list of words in the text
+ *
+ * @author Tom N Harris <tnharris@whoopdedo.org>
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+ public function tokenizer($text, $wc=false) {
+ $wc = ($wc) ? '' : '\*';
+ $stopwords =& idx_get_stopwords();
+
+ // prepare the text to be tokenized
+ $evt = new Event('INDEXER_TEXT_PREPARE', $text);
+ if ($evt->advise_before(true)) {
+ if (preg_match('/[^0-9A-Za-z ]/u', $text)) {
+ $text = \dokuwiki\Utf8\Asian::separateAsianWords($text);
+ }
+ }
+ $evt->advise_after();
+ unset($evt);
+
+ $text = strtr($text,
+ array(
+ "\r" => ' ',
+ "\n" => ' ',
+ "\t" => ' ',
+ "\xC2\xAD" => '', //soft-hyphen
+ )
+ );
+ if (preg_match('/[^0-9A-Za-z ]/u', $text))
+ $text = \dokuwiki\Utf8\Clean::stripspecials($text, ' ', '\._\-:'.$wc);
+
+ $wordlist = explode(' ', $text);
+ foreach ($wordlist as $i => $word) {
+ $wordlist[$i] = (preg_match('/[^0-9A-Za-z]/u', $word)) ?
+ \dokuwiki\Utf8\PhpString::strtolower($word) : strtolower($word);
+ }
+
+ foreach ($wordlist as $i => $word) {
+ if ((!is_numeric($word) && strlen($word) < IDX_MINWORDLENGTH)
+ || array_search($word, $stopwords, true) !== false)
+ unset($wordlist[$i]);
+ }
+ return array_values($wordlist);
+ }
+
+ /**
+ * Get the numeric PID of a page
+ *
+ * @param string $page The page to get the PID for
+ * @return bool|int The page id on success, false on error
+ */
+ public function getPID($page) {
+ // return PID without locking when it is in the cache
+ if (isset($this->pidCache[$page])) return $this->pidCache[$page];
+
+ if (!$this->lock())
+ return false;
+
+ // load known documents
+ $pid = $this->getPIDNoLock($page);
+ if ($pid === false) {
+ $this->unlock();
+ return false;
+ }
+
+ $this->unlock();
+ return $pid;
+ }
+
+ /**
+ * Get the numeric PID of a page without locking the index.
+ * Only use this function when the index is already locked.
+ *
+ * @param string $page The page to get the PID for
+ * @return bool|int The page id on success, false on error
+ */
+ protected function getPIDNoLock($page) {
+ // avoid expensive addIndexKey operation for the most recently requested pages by using a cache
+ if (isset($this->pidCache[$page])) return $this->pidCache[$page];
+ $pid = $this->addIndexKey('page', '', $page);
+ // limit cache to 10 entries by discarding the oldest element as in DokuWiki usually only the most recently
+ // added item will be requested again
+ if (count($this->pidCache) > 10) array_shift($this->pidCache);
+ $this->pidCache[$page] = $pid;
+ return $pid;
+ }
+
+ /**
+ * Get the page id of a numeric PID
+ *
+ * @param int $pid The PID to get the page id for
+ * @return string The page id
+ */
+ public function getPageFromPID($pid) {
+ return $this->getIndexKey('page', '', $pid);
+ }
+
+ /**
+ * Find pages in the fulltext index containing the words,
+ *
+ * The search words must be pre-tokenized, meaning only letters and
+ * numbers with an optional wildcard
+ *
+ * The returned array will have the original tokens as key. The values
+ * in the returned list is an array with the page names as keys and the
+ * number of times that token appears on the page as value.
+ *
+ * @param array $tokens list of words to search for
+ * @return array list of page names with usage counts
+ *
+ * @author Tom N Harris <tnharris@whoopdedo.org>
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+ public function lookup(&$tokens) {
+ $result = array();
+ $wids = $this->getIndexWords($tokens, $result);
+ if (empty($wids)) return array();
+ // load known words and documents
+ $page_idx = $this->getIndex('page', '');
+ $docs = array();
+ foreach (array_keys($wids) as $wlen) {
+ $wids[$wlen] = array_unique($wids[$wlen]);
+ $index = $this->getIndex('i', $wlen);
+ foreach($wids[$wlen] as $ixid) {
+ if ($ixid < count($index))
+ $docs["$wlen*$ixid"] = $this->parseTuples($page_idx, $index[$ixid]);
+ }
+ }
+ // merge found pages into final result array
+ $final = array();
+ foreach ($result as $word => $res) {
+ $final[$word] = array();
+ foreach ($res as $wid) {
+ // handle the case when ($ixid < count($index)) has been false
+ // and thus $docs[$wid] hasn't been set.
+ if (!isset($docs[$wid])) continue;
+ $hits = &$docs[$wid];
+ foreach ($hits as $hitkey => $hitcnt) {
+ // make sure the document still exists
+ if (!page_exists($hitkey, '', false)) continue;
+ if (!isset($final[$word][$hitkey]))
+ $final[$word][$hitkey] = $hitcnt;
+ else
+ $final[$word][$hitkey] += $hitcnt;
+ }
+ }
+ }
+ return $final;
+ }
+
+ /**
+ * Find pages containing a metadata key.
+ *
+ * The metadata values are compared as case-sensitive strings. Pass a
+ * callback function that returns true or false to use a different
+ * comparison function. The function will be called with the $value being
+ * searched for as the first argument, and the word in the index as the
+ * second argument. The function preg_match can be used directly if the
+ * values are regexes.
+ *
+ * @param string $key name of the metadata key to look for
+ * @param string $value search term to look for, must be a string or array of strings
+ * @param callback $func comparison function
+ * @return array lists with page names, keys are query values if $value is array
+ *
+ * @author Tom N Harris <tnharris@whoopdedo.org>
+ * @author Michael Hamann <michael@content-space.de>
+ */
+ public function lookupKey($key, &$value, $func=null) {
+ if (!is_array($value))
+ $value_array = array($value);
+ else
+ $value_array =& $value;
+
+ // the matching ids for the provided value(s)
+ $value_ids = array();
+
+ $metaname = idx_cleanName($key);
+
+ // get all words in order to search the matching ids
+ if ($key == 'title') {
+ $words = $this->getIndex('title', '');
+ } else {
+ $words = $this->getIndex($metaname.'_w', '');
+ }
+
+ if (!is_null($func)) {
+ foreach ($value_array as $val) {
+ foreach ($words as $i => $word) {
+ if (call_user_func_array($func, array($val, $word)))
+ $value_ids[$i][] = $val;
+ }
+ }
+ } else {
+ foreach ($value_array as $val) {
+ $xval = $val;
+ $caret = '^';
+ $dollar = '$';
+ // check for wildcards
+ if (substr($xval, 0, 1) == '*') {
+ $xval = substr($xval, 1);
+ $caret = '';
+ }
+ if (substr($xval, -1, 1) == '*') {
+ $xval = substr($xval, 0, -1);
+ $dollar = '';
+ }
+ if (!$caret || !$dollar) {
+ $re = $caret.preg_quote($xval, '/').$dollar;
+ foreach(array_keys(preg_grep('/'.$re.'/', $words)) as $i)
+ $value_ids[$i][] = $val;
+ } else {
+ if (($i = array_search($val, $words, true)) !== false)
+ $value_ids[$i][] = $val;
+ }
+ }
+ }
+
+ unset($words); // free the used memory
+
+ // initialize the result so it won't be null
+ $result = array();
+ foreach ($value_array as $val) {
+ $result[$val] = array();
+ }
+
+ $page_idx = $this->getIndex('page', '');
+
+ // Special handling for titles
+ if ($key == 'title') {
+ foreach ($value_ids as $pid => $val_list) {
+ $page = $page_idx[$pid];
+ foreach ($val_list as $val) {
+ $result[$val][] = $page;
+ }
+ }
+ } else {
+ // load all lines and pages so the used lines can be taken and matched with the pages
+ $lines = $this->getIndex($metaname.'_i', '');
+
+ foreach ($value_ids as $value_id => $val_list) {
+ // parse the tuples of the form page_id*1:page2_id*1 and so on, return value
+ // is an array with page_id => 1, page2_id => 1 etc. so take the keys only
+ $pages = array_keys($this->parseTuples($page_idx, $lines[$value_id]));
+ foreach ($val_list as $val) {
+ $result[$val] = array_merge($result[$val], $pages);
+ }
+ }
+ }
+ if (!is_array($value)) $result = $result[$value];
+ return $result;
+ }
+
+ /**
+ * Find the index ID of each search term.
+ *
+ * The query terms should only contain valid characters, with a '*' at
+ * either the beginning or end of the word (or both).
+ * The $result parameter can be used to merge the index locations with
+ * the appropriate query term.
+ *
+ * @param array $words The query terms.
+ * @param array $result Set to word => array("length*id" ...)
+ * @return array Set to length => array(id ...)
+ *
+ * @author Tom N Harris <tnharris@whoopdedo.org>
+ */
+ protected function getIndexWords(&$words, &$result) {
+ $tokens = array();
+ $tokenlength = array();
+ $tokenwild = array();
+ foreach ($words as $word) {
+ $result[$word] = array();
+ $caret = '^';
+ $dollar = '$';
+ $xword = $word;
+ $wlen = wordlen($word);
+
+ // check for wildcards
+ if (substr($xword, 0, 1) == '*') {
+ $xword = substr($xword, 1);
+ $caret = '';
+ $wlen -= 1;
+ }
+ if (substr($xword, -1, 1) == '*') {
+ $xword = substr($xword, 0, -1);
+ $dollar = '';
+ $wlen -= 1;
+ }
+ if ($wlen < IDX_MINWORDLENGTH && $caret && $dollar && !is_numeric($xword))
+ continue;
+ if (!isset($tokens[$xword]))
+ $tokenlength[$wlen][] = $xword;
+ if (!$caret || !$dollar) {
+ $re = $caret.preg_quote($xword, '/').$dollar;
+ $tokens[$xword][] = array($word, '/'.$re.'/');
+ if (!isset($tokenwild[$xword]))
+ $tokenwild[$xword] = $wlen;
+ } else {
+ $tokens[$xword][] = array($word, null);
+ }
+ }
+ asort($tokenwild);
+ // $tokens = array( base word => array( [ query term , regexp ] ... ) ... )
+ // $tokenlength = array( base word length => base word ... )
+ // $tokenwild = array( base word => base word length ... )
+ $length_filter = empty($tokenwild) ? $tokenlength : min(array_keys($tokenlength));
+ $indexes_known = $this->indexLengths($length_filter);
+ if (!empty($tokenwild)) sort($indexes_known);
+ // get word IDs
+ $wids = array();
+ foreach ($indexes_known as $ixlen) {
+ $word_idx = $this->getIndex('w', $ixlen);
+ // handle exact search
+ if (isset($tokenlength[$ixlen])) {
+ foreach ($tokenlength[$ixlen] as $xword) {
+ $wid = array_search($xword, $word_idx, true);
+ if ($wid !== false) {
+ $wids[$ixlen][] = $wid;
+ foreach ($tokens[$xword] as $w)
+ $result[$w[0]][] = "$ixlen*$wid";
+ }
+ }
+ }
+ // handle wildcard search
+ foreach ($tokenwild as $xword => $wlen) {
+ if ($wlen >= $ixlen) break;
+ foreach ($tokens[$xword] as $w) {
+ if (is_null($w[1])) continue;
+ foreach(array_keys(preg_grep($w[1], $word_idx)) as $wid) {
+ $wids[$ixlen][] = $wid;
+ $result[$w[0]][] = "$ixlen*$wid";
+ }
+ }
+ }
+ }
+ return $wids;
+ }
+
+ /**
+ * Return a list of all pages
+ * Warning: pages may not exist!
+ *
+ * @param string $key list only pages containing the metadata key (optional)
+ * @return array list of page names
+ *
+ * @author Tom N Harris <tnharris@whoopdedo.org>
+ */
+ public function getPages($key=null) {
+ $page_idx = $this->getIndex('page', '');
+ if (is_null($key)) return $page_idx;
+
+ $metaname = idx_cleanName($key);
+
+ // Special handling for titles
+ if ($key == 'title') {
+ $title_idx = $this->getIndex('title', '');
+ array_splice($page_idx, count($title_idx));
+ foreach ($title_idx as $i => $title)
+ if ($title === "") unset($page_idx[$i]);
+ return array_values($page_idx);
+ }
+
+ $pages = array();
+ $lines = $this->getIndex($metaname.'_i', '');
+ foreach ($lines as $line) {
+ $pages = array_merge($pages, $this->parseTuples($page_idx, $line));
+ }
+ return array_keys($pages);
+ }
+
+ /**
+ * Return a list of words sorted by number of times used
+ *
+ * @param int $min bottom frequency threshold
+ * @param int $max upper frequency limit. No limit if $max<$min
+ * @param int $minlen minimum length of words to count
+ * @param string $key metadata key to list. Uses the fulltext index if not given
+ * @return array list of words as the keys and frequency as values
+ *
+ * @author Tom N Harris <tnharris@whoopdedo.org>
+ */
+ public function histogram($min=1, $max=0, $minlen=3, $key=null) {
+ if ($min < 1)
+ $min = 1;
+ if ($max < $min)
+ $max = 0;
+
+ $result = array();
+
+ if ($key == 'title') {
+ $index = $this->getIndex('title', '');
+ $index = array_count_values($index);
+ foreach ($index as $val => $cnt) {
+ if ($cnt >= $min && (!$max || $cnt <= $max) && strlen($val) >= $minlen)
+ $result[$val] = $cnt;
+ }
+ }
+ elseif (!is_null($key)) {
+ $metaname = idx_cleanName($key);
+ $index = $this->getIndex($metaname.'_i', '');
+ $val_idx = array();
+ foreach ($index as $wid => $line) {
+ $freq = $this->countTuples($line);
+ if ($freq >= $min && (!$max || $freq <= $max))
+ $val_idx[$wid] = $freq;
+ }
+ if (!empty($val_idx)) {
+ $words = $this->getIndex($metaname.'_w', '');
+ foreach ($val_idx as $wid => $freq) {
+ if (strlen($words[$wid]) >= $minlen)
+ $result[$words[$wid]] = $freq;
+ }
+ }
+ }
+ else {
+ $lengths = idx_listIndexLengths();
+ foreach ($lengths as $length) {
+ if ($length < $minlen) continue;
+ $index = $this->getIndex('i', $length);
+ $words = null;
+ foreach ($index as $wid => $line) {
+ $freq = $this->countTuples($line);
+ if ($freq >= $min && (!$max || $freq <= $max)) {
+ if ($words === null)
+ $words = $this->getIndex('w', $length);
+ $result[$words[$wid]] = $freq;
+ }
+ }
+ }
+ }
+
+ arsort($result);
+ return $result;
+ }
+
+ /**
+ * Lock the indexer.
+ *
+ * @author Tom N Harris <tnharris@whoopdedo.org>
+ *
+ * @return bool|string
+ */
+ protected function lock() {
+ global $conf;
+ $status = true;
+ $run = 0;
+ $lock = $conf['lockdir'].'/_indexer.lock';
+ while (!@mkdir($lock, $conf['dmode'])) {
+ usleep(50);
+ if(is_dir($lock) && time()-@filemtime($lock) > 60*5){
+ // looks like a stale lock - remove it
+ if (!@rmdir($lock)) {
+ $status = "removing the stale lock failed";
+ return false;
+ } else {
+ $status = "stale lock removed";
+ }
+ }elseif($run++ == 1000){
+ // we waited 5 seconds for that lock
+ return false;
+ }
+ }
+ if (!empty($conf['dperm'])) {
+ chmod($lock, $conf['dperm']);
+ }
+ return $status;
+ }
+
+ /**
+ * Release the indexer lock.
+ *
+ * @author Tom N Harris <tnharris@whoopdedo.org>
+ *
+ * @return bool
+ */
+ protected function unlock() {
+ global $conf;
+ @rmdir($conf['lockdir'].'/_indexer.lock');
+ return true;
+ }
+
+ /**
+ * Retrieve the entire index.
+ *
+ * The $suffix argument is for an index that is split into
+ * multiple parts. Different index files should use different
+ * base names.
+ *
+ * @param string $idx name of the index
+ * @param string $suffix subpart identifier
+ * @return array list of lines without CR or LF
+ *
+ * @author Tom N Harris <tnharris@whoopdedo.org>
+ */
+ protected function getIndex($idx, $suffix) {
+ global $conf;
+ $fn = $conf['indexdir'].'/'.$idx.$suffix.'.idx';
+ if (!file_exists($fn)) return array();
+ return file($fn, FILE_IGNORE_NEW_LINES);
+ }
+
+ /**
+ * Replace the contents of the index with an array.
+ *
+ * @param string $idx name of the index
+ * @param string $suffix subpart identifier
+ * @param array $lines list of lines without LF
+ * @return bool If saving succeeded
+ *
+ * @author Tom N Harris <tnharris@whoopdedo.org>
+ */
+ protected function saveIndex($idx, $suffix, &$lines) {
+ global $conf;
+ $fn = $conf['indexdir'].'/'.$idx.$suffix;
+ $fh = @fopen($fn.'.tmp', 'w');
+ if (!$fh) return false;
+ fwrite($fh, join("\n", $lines));
+ if (!empty($lines))
+ fwrite($fh, "\n");
+ fclose($fh);
+ if ($conf['fperm'])
+ chmod($fn.'.tmp', $conf['fperm']);
+ io_rename($fn.'.tmp', $fn.'.idx');
+ return true;
+ }
+
+ /**
+ * Retrieve a line from the index.
+ *
+ * @param string $idx name of the index
+ * @param string $suffix subpart identifier
+ * @param int $id the line number
+ * @return string a line with trailing whitespace removed
+ *
+ * @author Tom N Harris <tnharris@whoopdedo.org>
+ */
+ protected function getIndexKey($idx, $suffix, $id) {
+ global $conf;
+ $fn = $conf['indexdir'].'/'.$idx.$suffix.'.idx';
+ if (!file_exists($fn)) return '';
+ $fh = @fopen($fn, 'r');
+ if (!$fh) return '';
+ $ln = -1;
+ while (($line = fgets($fh)) !== false) {
+ if (++$ln == $id) break;
+ }
+ fclose($fh);
+ return rtrim((string)$line);
+ }
+
+ /**
+ * Write a line into the index.
+ *
+ * @param string $idx name of the index
+ * @param string $suffix subpart identifier
+ * @param int $id the line number
+ * @param string $line line to write
+ * @return bool If saving succeeded
+ *
+ * @author Tom N Harris <tnharris@whoopdedo.org>
+ */
+ protected function saveIndexKey($idx, $suffix, $id, $line) {
+ global $conf;
+ if (substr($line, -1) != "\n")
+ $line .= "\n";
+ $fn = $conf['indexdir'].'/'.$idx.$suffix;
+ $fh = @fopen($fn.'.tmp', 'w');
+ if (!$fh) return false;
+ $ih = @fopen($fn.'.idx', 'r');
+ if ($ih) {
+ $ln = -1;
+ while (($curline = fgets($ih)) !== false) {
+ fwrite($fh, (++$ln == $id) ? $line : $curline);
+ }
+ if ($id > $ln) {
+ while ($id > ++$ln)
+ fwrite($fh, "\n");
+ fwrite($fh, $line);
+ }
+ fclose($ih);
+ } else {
+ $ln = -1;
+ while ($id > ++$ln)
+ fwrite($fh, "\n");
+ fwrite($fh, $line);
+ }
+ fclose($fh);
+ if ($conf['fperm'])
+ chmod($fn.'.tmp', $conf['fperm']);
+ io_rename($fn.'.tmp', $fn.'.idx');
+ return true;
+ }
+
+ /**
+ * Retrieve or insert a value in the index.
+ *
+ * @param string $idx name of the index
+ * @param string $suffix subpart identifier
+ * @param string $value line to find in the index
+ * @return int|bool line number of the value in the index or false if writing the index failed
+ *
+ * @author Tom N Harris <tnharris@whoopdedo.org>
+ */
+ protected function addIndexKey($idx, $suffix, $value) {
+ $index = $this->getIndex($idx, $suffix);
+ $id = array_search($value, $index, true);
+ if ($id === false) {
+ $id = count($index);
+ $index[$id] = $value;
+ if (!$this->saveIndex($idx, $suffix, $index)) {
+ trigger_error("Failed to write $idx index", E_USER_ERROR);
+ return false;
+ }
+ }
+ return $id;
+ }
+
+ /**
+ * Get the list of lengths indexed in the wiki.
+ *
+ * Read the index directory or a cache file and returns
+ * a sorted array of lengths of the words used in the wiki.
+ *
+ * @author YoBoY <yoboy.leguesh@gmail.com>
+ *
+ * @return array
+ */
+ protected function listIndexLengths() {
+ return idx_listIndexLengths();
+ }
+
+ /**
+ * Get the word lengths that have been indexed.
+ *
+ * Reads the index directory and returns an array of lengths
+ * that there are indices for.
+ *
+ * @author YoBoY <yoboy.leguesh@gmail.com>
+ *
+ * @param array|int $filter
+ * @return array
+ */
+ protected function indexLengths($filter) {
+ global $conf;
+ $idx = array();
+ if (is_array($filter)) {
+ // testing if index files exist only
+ $path = $conf['indexdir']."/i";
+ foreach ($filter as $key => $value) {
+ if (file_exists($path.$key.'.idx'))
+ $idx[] = $key;
+ }
+ } else {
+ $lengths = idx_listIndexLengths();
+ foreach ($lengths as $key => $length) {
+ // keep all the values equal or superior
+ if ((int)$length >= (int)$filter)
+ $idx[] = $length;
+ }
+ }
+ return $idx;
+ }
+
+ /**
+ * Insert or replace a tuple in a line.
+ *
+ * @author Tom N Harris <tnharris@whoopdedo.org>
+ *
+ * @param string $line
+ * @param string|int $id
+ * @param int $count
+ * @return string
+ */
+ protected function updateTuple($line, $id, $count) {
+ if ($line != ''){
+ $line = preg_replace('/(^|:)'.preg_quote($id,'/').'\*\d*/', '', $line);
+ }
+ $line = trim($line, ':');
+ if ($count) {
+ if ($line) {
+ return "$id*$count:".$line;
+ } else {
+ return "$id*$count";
+ }
+ }
+ return $line;
+ }
+
+ /**
+ * Split a line into an array of tuples.
+ *
+ * @author Tom N Harris <tnharris@whoopdedo.org>
+ * @author Andreas Gohr <andi@splitbrain.org>
+ *
+ * @param array $keys
+ * @param string $line
+ * @return array
+ */
+ protected function parseTuples(&$keys, $line) {
+ $result = array();
+ if ($line == '') return $result;
+ $parts = explode(':', $line);
+ foreach ($parts as $tuple) {
+ if ($tuple === '') continue;
+ list($key, $cnt) = explode('*', $tuple);
+ if (!$cnt) continue;
+ $key = $keys[$key];
+ if ($key === false || is_null($key)) continue;
+ $result[$key] = $cnt;
+ }
+ return $result;
+ }
+
+ /**
+ * Sum the counts in a list of tuples.
+ *
+ * @author Tom N Harris <tnharris@whoopdedo.org>
+ *
+ * @param string $line
+ * @return int
+ */
+ protected function countTuples($line) {
+ $freq = 0;
+ $parts = explode(':', $line);
+ foreach ($parts as $tuple) {
+ if ($tuple === '') continue;
+ list(/* $pid */, $cnt) = explode('*', $tuple);
+ $freq += (int)$cnt;
+ }
+ return $freq;
+ }
+}
diff --git a/inc/Subscriptions/MediaSubscriptionSender.php b/inc/Subscriptions/MediaSubscriptionSender.php
index 5a0f86ed6..1757c2b1c 100644
--- a/inc/Subscriptions/MediaSubscriptionSender.php
+++ b/inc/Subscriptions/MediaSubscriptionSender.php
@@ -16,8 +16,9 @@ class MediaSubscriptionSender extends SubscriptionSender
* @param string $template Mail template ('uploadmail', ...)
* @param string $id Media file for which the notification is
* @param int|bool $rev Old revision if any
+ * @param int|bool $current_rev New revision if any
*/
- public function sendMediaDiff($subscriber_mail, $template, $id, $rev = false)
+ public function sendMediaDiff($subscriber_mail, $template, $id, $rev = false, $current_rev = false)
{
global $conf;
@@ -26,7 +27,7 @@ class MediaSubscriptionSender extends SubscriptionSender
$trep = [
'MIME' => $mime,
- 'MEDIA' => ml($id, '', true, '&', true),
+ 'MEDIA' => ml($id, $current_rev?('rev='.$current_rev):'', true, '&', true),
'SIZE' => filesize_h(filesize($file)),
];
diff --git a/inc/Subscriptions/PageSubscriptionSender.php b/inc/Subscriptions/PageSubscriptionSender.php
index 8fca582a4..e5577c1af 100644
--- a/inc/Subscriptions/PageSubscriptionSender.php
+++ b/inc/Subscriptions/PageSubscriptionSender.php
@@ -19,17 +19,18 @@ class PageSubscriptionSender extends SubscriptionSender
* @param string $id Page for which the notification is
* @param int|null $rev Old revision if any
* @param string $summary Change summary if any
+ * @param int|null $current_rev New revision if any
*
* @return bool true if successfully sent
*/
- public function sendPageDiff($subscriber_mail, $template, $id, $rev = null, $summary = '')
+ public function sendPageDiff($subscriber_mail, $template, $id, $rev = null, $summary = '', $current_rev = null)
{
global $DIFF_INLINESTYLES;
// prepare replacements (keys not set in hrep will be taken from trep)
$trep = [
'PAGE' => $id,
- 'NEWPAGE' => wl($id, '', true, '&'),
+ 'NEWPAGE' => wl($id, $current_rev?('rev='.$current_rev):'', true, '&'),
'SUMMARY' => $summary,
'SUBSCRIBE' => wl($id, ['do' => 'subscribe'], true, '&'),
];
diff --git a/inc/Utf8/Clean.php b/inc/Utf8/Clean.php
index 79c46810c..0975ff559 100644
--- a/inc/Utf8/Clean.php
+++ b/inc/Utf8/Clean.php
@@ -65,8 +65,8 @@ class Clean
$ascii = '';
$len = strlen($str);
for ($i = 0; $i < $len; $i++) {
- if (ord($str{$i}) < 128) {
- $ascii .= $str{$i};
+ if (ord($str[$i]) < 128) {
+ $ascii .= $str[$i];
}
}
return $ascii;
diff --git a/inc/Utf8/Unicode.php b/inc/Utf8/Unicode.php
index c706d716b..4b6426533 100644
--- a/inc/Utf8/Unicode.php
+++ b/inc/Utf8/Unicode.php
@@ -46,7 +46,7 @@ class Unicode
for ($i = 0; $i < $len; $i++) {
- $in = ord($str{$i});
+ $in = ord($str[$i]);
if ($mState === 0) {
diff --git a/inc/auth.php b/inc/auth.php
index f99f6501f..0630a76f0 100644
--- a/inc/auth.php
+++ b/inc/auth.php
@@ -139,7 +139,7 @@ function auth_loadACL() {
$out = array();
foreach($acl as $line) {
$line = trim($line);
- if(empty($line) || ($line{0} == '#')) continue; // skip blank lines & comments
+ if(empty($line) || ($line[0] == '#')) continue; // skip blank lines & comments
list($id,$rest) = preg_split('/[ \t]+/',$line,2);
// substitute user wildcard first (its 1:1)
@@ -154,10 +154,12 @@ function auth_loadACL() {
// substitute group wildcard (its 1:m)
if(strstr($line, '%GROUP%')){
// if user is not logged in, grps is empty, no output will be added (i.e. skipped)
- foreach((array) $USERINFO['grps'] as $grp){
- $nid = str_replace('%GROUP%',cleanID($grp),$id);
- $nrest = str_replace('%GROUP%','@'.auth_nameencode($grp),$rest);
- $out[] = "$nid\t$nrest";
+ if(isset($USERINFO['grps'])){
+ foreach((array) $USERINFO['grps'] as $grp){
+ $nid = str_replace('%GROUP%',cleanID($grp),$id);
+ $nrest = str_replace('%GROUP%','@'.auth_nameencode($grp),$rest);
+ $out[] = "$nid\t$nrest";
+ }
}
} else {
$out[] = "$id\t$rest";
@@ -470,7 +472,7 @@ function auth_ismanager($user = null, $groups = null, $adminonly = false) {
}
}
if(is_null($groups)) {
- $groups = (array) $USERINFO['grps'];
+ $groups = $USERINFO ? (array) $USERINFO['grps'] : array();
}
// check superuser match
@@ -564,7 +566,7 @@ function auth_quickaclcheck($id) {
global $INPUT;
# if no ACL is used always return upload rights
if(!$conf['useacl']) return AUTH_UPLOAD;
- return auth_aclcheck($id, $INPUT->server->str('REMOTE_USER'), $USERINFO['grps']);
+ return auth_aclcheck($id, $INPUT->server->str('REMOTE_USER'), is_array($USERINFO) ? $USERINFO['grps'] : array());
}
/**
@@ -737,7 +739,7 @@ function auth_nameencode($name, $skip_group = false) {
if($name == '%GROUP%') return $name;
if(!isset($cache[$name][$skip_group])) {
- if($skip_group && $name{0} == '@') {
+ if($skip_group && $name[0] == '@') {
$cache[$name][$skip_group] = '@'.preg_replace_callback(
'/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f])/',
'auth_nameencode_callback', substr($name, 1)
@@ -1076,7 +1078,7 @@ function act_resendpwd() {
if($token) {
// we're in token phase - get user info from token
- $tfile = $conf['cachedir'].'/'.$token{0}.'/'.$token.'.pwauth';
+ $tfile = $conf['cachedir'].'/'.$token[0].'/'.$token.'.pwauth';
if(!file_exists($tfile)) {
msg($lang['resendpwdbadauth'], -1);
$INPUT->remove('pwauth');
@@ -1151,7 +1153,7 @@ function act_resendpwd() {
// generate auth token
$token = md5(auth_randombytes(16)); // random secret
- $tfile = $conf['cachedir'].'/'.$token{0}.'/'.$token.'.pwauth';
+ $tfile = $conf['cachedir'].'/'.$token[0].'/'.$token.'.pwauth';
$url = wl('', array('do'=> 'resendpwd', 'pwauth'=> $token), true, '&');
io_saveFile($tfile, $user);
diff --git a/inc/changelog.php b/inc/changelog.php
index b96a1d43a..652016cb8 100644
--- a/inc/changelog.php
+++ b/inc/changelog.php
@@ -113,7 +113,7 @@ function addLogEntry($date, $id, $type=DOKU_CHANGE_TYPE_EDIT, $summary='', $extr
// newly created
$meta['date']['created'] = $created;
if ($user){
- $meta['creator'] = $INFO['userinfo']['name'];
+ $meta['creator'] = isset($INFO) ? $INFO['userinfo']['name'] : null;
$meta['user'] = $user;
}
} elseif (($wasCreated || $wasReverted) && !empty($oldmeta['persistent']['date']['created'])) {
@@ -121,10 +121,10 @@ function addLogEntry($date, $id, $type=DOKU_CHANGE_TYPE_EDIT, $summary='', $extr
$meta['date']['created'] = $oldmeta['persistent']['date']['created'];
$meta['date']['modified'] = $created; // use the files ctime here
$meta['creator'] = $oldmeta['persistent']['creator'];
- if ($user) $meta['contributor'][$user] = $INFO['userinfo']['name'];
+ if ($user) $meta['contributor'][$user] = isset($INFO) ? $INFO['userinfo']['name'] : null;
} elseif (!$minor) { // non-minor modification
$meta['date']['modified'] = $date;
- if ($user) $meta['contributor'][$user] = $INFO['userinfo']['name'];
+ if ($user) $meta['contributor'][$user] = isset($INFO) ? $INFO['userinfo']['name'] : null;
}
$meta['last_change'] = $logline;
p_set_metadata($id, $meta);
@@ -228,9 +228,9 @@ function getRecents($first,$num,$ns='',$flags=0){
// read all recent changes. (kept short)
if ($flags & RECENTS_MEDIA_CHANGES) {
- $lines = @file($conf['media_changelog']);
+ $lines = @file($conf['media_changelog']) ?: [];
} else {
- $lines = @file($conf['changelog']);
+ $lines = @file($conf['changelog']) ?: [];
}
if (!is_array($lines)) {
$lines = array();
@@ -240,7 +240,7 @@ function getRecents($first,$num,$ns='',$flags=0){
$media_lines = array();
if ($flags & RECENTS_MEDIA_PAGES_MIXED) {
- $media_lines = @file($conf['media_changelog']);
+ $media_lines = @file($conf['media_changelog']) ?: [];
if (!is_array($media_lines)) {
$media_lines = array();
}
diff --git a/inc/cli.php b/inc/cli.php
index e64798386..cb4dabf04 100644
--- a/inc/cli.php
+++ b/inc/cli.php
@@ -402,13 +402,13 @@ class DokuCLI_Options {
}
// first non-option
- if($arg{0} != '-') {
+ if($arg[0] != '-') {
$non_opts = array_merge($non_opts, array_slice($this->args, $i));
break;
}
// long option
- if(strlen($arg) > 1 && $arg{1} == '-') {
+ if(strlen($arg) > 1 && $arg[1] == '-') {
list($opt, $val) = explode('=', substr($arg, 2), 2);
if(!isset($this->setup[$this->command]['opts'][$opt])) {
diff --git a/inc/common.php b/inc/common.php
index 6822ccbd3..c462934b2 100644
--- a/inc/common.php
+++ b/inc/common.php
@@ -280,16 +280,23 @@ function pageinfo() {
p_set_metadata($ID, array('last_change' => $revinfo));
}
- $info['ip'] = $revinfo['ip'];
- $info['user'] = $revinfo['user'];
- $info['sum'] = $revinfo['sum'];
- // See also $INFO['meta']['last_change'] which is the most recent log line for page $ID.
- // Use $INFO['meta']['last_change']['type']===DOKU_CHANGE_TYPE_MINOR_EDIT in place of $info['minor'].
-
- if($revinfo['user']) {
- $info['editor'] = $revinfo['user'];
- } else {
- $info['editor'] = $revinfo['ip'];
+ if($revinfo !== false){
+ $info['ip'] = $revinfo['ip'];
+ $info['user'] = $revinfo['user'];
+ $info['sum'] = $revinfo['sum'];
+ // See also $INFO['meta']['last_change'] which is the most recent log line for page $ID.
+ // Use $INFO['meta']['last_change']['type']===DOKU_CHANGE_TYPE_MINOR_EDIT in place of $info['minor'].
+
+ if($revinfo['user']) {
+ $info['editor'] = $revinfo['user'];
+ } else {
+ $info['editor'] = $revinfo['ip'];
+ }
+ }else{
+ $info['ip'] = null;
+ $info['user'] = null;
+ $info['sum'] = null;
+ $info['editor'] = null;
}
// draft
@@ -312,7 +319,7 @@ function jsinfo() {
}
//export minimal info to JS, plugins can add more
$JSINFO['id'] = $ID;
- $JSINFO['namespace'] = (string) $INFO['namespace'];
+ $JSINFO['namespace'] = isset($INFO) ? (string) $INFO['namespace'] : '';
$JSINFO['ACT'] = act_clean($ACT);
$JSINFO['useHeadingNavigation'] = (int) useHeading('navigation');
$JSINFO['useHeadingContent'] = (int) useHeading('content');
@@ -362,16 +369,16 @@ function buildURLparams($params, $sep = '&amp;') {
*
* @author Andreas Gohr
*
- * @param array $params array with (attribute name-attribute value) pairs
- * @param bool $skipempty skip empty string values?
+ * @param array $params array with (attribute name-attribute value) pairs
+ * @param bool $skipEmptyStrings skip empty string values?
* @return string
*/
-function buildAttributes($params, $skipempty = false) {
+function buildAttributes($params, $skipEmptyStrings = false) {
$url = '';
$white = false;
foreach($params as $key => $val) {
- if($key{0} == '_') continue;
- if($val === '' && $skipempty) continue;
+ if($key[0] == '_') continue;
+ if($val === '' && $skipEmptyStrings) continue;
if($white) $url .= ' ';
$url .= $key.'="';
@@ -397,12 +404,13 @@ function breadcrumbs() {
global $ID;
global $ACT;
global $conf;
+ global $INFO;
//first visit?
$crumbs = isset($_SESSION[DOKU_COOKIE]['bc']) ? $_SESSION[DOKU_COOKIE]['bc'] : array();
- //we only save on show and existing visible wiki documents
+ //we only save on show and existing visible readable wiki documents
$file = wikiFN($ID);
- if($ACT != 'show' || isHiddenPage($ID) || !file_exists($file)) {
+ if($ACT != 'show' || $INFO['perm'] < AUTH_READ || isHiddenPage($ID) || !file_exists($file)) {
$_SESSION[DOKU_COOKIE]['bc'] = $crumbs;
return $crumbs;
}
@@ -782,7 +790,7 @@ function checkwordblock($text = '') {
*/
function clientIP($single = false) {
/* @var Input $INPUT */
- global $INPUT;
+ global $INPUT, $conf;
$ip = array();
$ip[] = $INPUT->server->str('REMOTE_ADDR');
@@ -829,17 +837,18 @@ function clientIP($single = false) {
if(!$single) return join(',', $ip);
- // decide which IP to use, trying to avoid local addresses
- $ip = array_reverse($ip);
+ // skip trusted local addresses
foreach($ip as $i) {
- if(preg_match('/^(::1|[fF][eE]80:|127\.|10\.|192\.168\.|172\.((1[6-9])|(2[0-9])|(3[0-1]))\.)/', $i)) {
+ if(!empty($conf['trustedproxy']) && preg_match('/'.$conf['trustedproxy'].'/', $i)) {
continue;
} else {
return $i;
}
}
- // still here? just use the first (last) address
- return $ip[0];
+
+ // still here? just use the last address
+ // this case all ips in the list are trusted
+ return $ip[count($ip)-1];
}
/**
@@ -1180,8 +1189,8 @@ function parsePageTemplate(&$data) {
\dokuwiki\Utf8\PhpString::ucwords($page),
\dokuwiki\Utf8\PhpString::strtoupper($page),
$INPUT->server->str('REMOTE_USER'),
- $USERINFO['name'],
- $USERINFO['mail'],
+ $USERINFO ? $USERINFO['name'] : '',
+ $USERINFO ? $USERINFO['mail'] : '',
$conf['dformat'],
), $tpl
);
@@ -1425,8 +1434,8 @@ function saveWikiText($id, $text, $summary, $minor = false) {
);
// send notify mails
- notify($svdta['id'], 'admin', $svdta['oldRevision'], $svdta['summary'], $minor);
- notify($svdta['id'], 'subscribers', $svdta['oldRevision'], $svdta['summary'], $minor);
+ notify($svdta['id'], 'admin', $svdta['oldRevision'], $svdta['summary'], $minor, $svdta['newRevision']);
+ notify($svdta['id'], 'subscribers', $svdta['oldRevision'], $svdta['summary'], $minor, $svdta['newRevision']);
// update the purgefile (timestamp of the last time anything within the wiki was changed)
io_saveFile($conf['cachedir'].'/purgefile', time());
@@ -1468,11 +1477,12 @@ function saveOldRevision($id) {
* @param string $summary What changed
* @param boolean $minor Is this a minor edit?
* @param string[] $replace Additional string substitutions, @KEY@ to be replaced by value
+ * @param int|string $current_rev New page revision
* @return bool
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
-function notify($id, $who, $rev = '', $summary = '', $minor = false, $replace = array()) {
+function notify($id, $who, $rev = '', $summary = '', $minor = false, $replace = array(), $current_rev = false) {
global $conf;
/* @var Input $INPUT */
global $INPUT;
@@ -1499,7 +1509,7 @@ function notify($id, $who, $rev = '', $summary = '', $minor = false, $replace =
// prepare content
$subscription = new PageSubscriptionSender();
- return $subscription->sendPageDiff($to, $tpl, $id, $rev, $summary);
+ return $subscription->sendPageDiff($to, $tpl, $id, $rev, $summary, $current_rev);
}
/**
@@ -1523,11 +1533,7 @@ function getGoogleQuery() {
if(!preg_match('/(google|bing|yahoo|ask|duckduckgo|babylon|aol|yandex)/',$url['host'])) return '';
$query = array();
- // temporary workaround against PHP bug #49733
- // see http://bugs.php.net/bug.php?id=49733
- if(UTF8_MBSTRING) $enc = mb_internal_encoding();
parse_str($url['query'], $query);
- if(UTF8_MBSTRING) mb_internal_encoding($enc);
$q = '';
if(isset($query['q'])){
@@ -1540,6 +1546,8 @@ function getGoogleQuery() {
$q = trim($q);
if(!$q) return '';
+ // ignore if query includes a full URL
+ if(strpos($q, '//') !== false) return '';
$q = preg_split('/[\s\'"\\\\`()\]\[?:!\.{};,#+*<>\\/]+/', $q, -1, PREG_SPLIT_NO_EMPTY);
return $q;
}
diff --git a/inc/deprecated.php b/inc/deprecated.php
index d3de76080..205037344 100644
--- a/inc/deprecated.php
+++ b/inc/deprecated.php
@@ -563,3 +563,8 @@ class Subscription {
$manager->notifyAddresses($data);
}
}
+
+/**
+ * @deprecated 2019-12-29 use \dokuwiki\Search\Indexer
+ */
+class Doku_Indexer extends \dokuwiki\Search\Indexer {};
diff --git a/inc/fulltext.php b/inc/fulltext.php
index 48fe28da2..670f048d3 100644
--- a/inc/fulltext.php
+++ b/inc/fulltext.php
@@ -629,8 +629,8 @@ function ft_resultComplement($args) {
* @author Andreas Gohr <andi@splitbrain.org>
* @author Kazutaka Miyasaka <kazmiya@gmail.com>
*
- * @param Doku_Indexer $Indexer
- * @param string $query search query
+ * @param dokuwiki\Search\Indexer $Indexer
+ * @param string $query search query
* @return array of search formulas
*/
function ft_queryParser($Indexer, $query){
@@ -864,10 +864,10 @@ function ft_queryParser($Indexer, $query){
*
* @author Kazutaka Miyasaka <kazmiya@gmail.com>
*
- * @param Doku_Indexer $Indexer
- * @param string $term
- * @param bool $consider_asian
- * @param bool $phrase_mode
+ * @param dokuwiki\Search\Indexer $Indexer
+ * @param string $term
+ * @param bool $consider_asian
+ * @param bool $phrase_mode
* @return string
*/
function ft_termParser($Indexer, $term, $consider_asian = true, $phrase_mode = false) {
diff --git a/inc/html.php b/inc/html.php
index 773d55364..1f494d46b 100644
--- a/inc/html.php
+++ b/inc/html.php
@@ -50,7 +50,7 @@ function html_login($svg = false){
print p_locale_xhtml('login');
print '<div class="centeralign">'.NL;
- $form = new Doku_Form(array('id' => 'dw__login'));
+ $form = new Doku_Form(array('id' => 'dw__login', 'action'=>wl($ID)));
$form->startFieldset($lang['btn_login']);
$form->addHidden('id', $ID);
$form->addHidden('do', 'login');
@@ -108,7 +108,7 @@ function html_denied() {
function html_secedit($text,$show=true){
global $INFO;
- if(!$INFO['writable'] || !$show || $INFO['rev']){
+ if((isset($INFO) && !$INFO['writable']) || !$show || (isset($INFO) && $INFO['rev'])){
return preg_replace(SEC_EDIT_PATTERN,'',$text);
}
@@ -707,7 +707,7 @@ function html_recent($first = 0, $show_changes = 'both') {
'</p></div>';
}
- $form = new Doku_Form(array('id' => 'dw__recent', 'method' => 'GET', 'class' => 'changes'));
+ $form = new Doku_Form(array('id' => 'dw__recent', 'method' => 'GET', 'class' => 'changes', 'action'=>wl($ID)));
$form->addHidden('sectok', null);
$form->addHidden('do', 'recent');
$form->addHidden('id', $ID);
@@ -964,7 +964,7 @@ function html_li_index($item){
if($item['type'] == "f"){
// scroll to the current item
- if($item['id'] == $INFO['id'] && $ACT == 'index') {
+ if(isset($INFO) && $item['id'] == $INFO['id'] && $ACT == 'index') {
$id = ' id="scroll__here"';
$class = ' bounce';
}
@@ -1585,7 +1585,7 @@ function html_insert_softbreaks($diffhtml) {
*/
function html_softbreak_callback($match){
// if match is an html tag, return it intact
- if ($match[0]{0} == '<') return $match[0];
+ if ($match[0][0] == '<') return $match[0];
// its a long string without a breaking character,
// make certain characters into breaking characters by inserting a
@@ -1940,7 +1940,7 @@ function html_edit(){
if ($wr) {
// sets changed to true when previewed
- echo '<script type="text/javascript">/*<![CDATA[*/'. NL;
+ echo '<script>/*<![CDATA[*/'. NL;
echo 'textChanged = ' . ($mod ? 'true' : 'false');
echo '/*!]]>*/</script>' . NL;
} ?>
diff --git a/inc/indexer.php b/inc/indexer.php
index f2c8a9aa8..ab02b8ea2 100644
--- a/inc/indexer.php
+++ b/inc/indexer.php
@@ -8,6 +8,7 @@
*/
use dokuwiki\Extension\Event;
+use dokuwiki\Search\Indexer;
// Version tag used to force rebuild on upgrade
define('INDEXER_VERSION', 8);
@@ -67,1225 +68,16 @@ function wordlen($w){
}
/**
- * Class that encapsulates operations on the indexer database.
- *
- * @author Tom N Harris <tnharris@whoopdedo.org>
- */
-class Doku_Indexer {
- /**
- * @var array $pidCache Cache for getPID()
- */
- protected $pidCache = array();
-
- /**
- * Adds the contents of a page to the fulltext index
- *
- * The added text replaces previous words for the same page.
- * An empty value erases the page.
- *
- * @param string $page a page name
- * @param string $text the body of the page
- * @return string|boolean the function completed successfully
- *
- * @author Tom N Harris <tnharris@whoopdedo.org>
- * @author Andreas Gohr <andi@splitbrain.org>
- */
- public function addPageWords($page, $text) {
- if (!$this->lock())
- return "locked";
-
- // load known documents
- $pid = $this->getPIDNoLock($page);
- if ($pid === false) {
- $this->unlock();
- return false;
- }
-
- $pagewords = array();
- // get word usage in page
- $words = $this->getPageWords($text);
- if ($words === false) {
- $this->unlock();
- return false;
- }
-
- if (!empty($words)) {
- foreach (array_keys($words) as $wlen) {
- $index = $this->getIndex('i', $wlen);
- foreach ($words[$wlen] as $wid => $freq) {
- $idx = ($wid<count($index)) ? $index[$wid] : '';
- $index[$wid] = $this->updateTuple($idx, $pid, $freq);
- $pagewords[] = "$wlen*$wid";
- }
- if (!$this->saveIndex('i', $wlen, $index)) {
- $this->unlock();
- return false;
- }
- }
- }
-
- // Remove obsolete index entries
- $pageword_idx = $this->getIndexKey('pageword', '', $pid);
- if ($pageword_idx !== '') {
- $oldwords = explode(':',$pageword_idx);
- $delwords = array_diff($oldwords, $pagewords);
- $upwords = array();
- foreach ($delwords as $word) {
- if ($word != '') {
- list($wlen,$wid) = explode('*', $word);
- $wid = (int)$wid;
- $upwords[$wlen][] = $wid;
- }
- }
- foreach ($upwords as $wlen => $widx) {
- $index = $this->getIndex('i', $wlen);
- foreach ($widx as $wid) {
- $index[$wid] = $this->updateTuple($index[$wid], $pid, 0);
- }
- $this->saveIndex('i', $wlen, $index);
- }
- }
- // Save the reverse index
- $pageword_idx = join(':', $pagewords);
- if (!$this->saveIndexKey('pageword', '', $pid, $pageword_idx)) {
- $this->unlock();
- return false;
- }
-
- $this->unlock();
- return true;
- }
-
- /**
- * Split the words in a page and add them to the index.
- *
- * @param string $text content of the page
- * @return array list of word IDs and number of times used
- *
- * @author Andreas Gohr <andi@splitbrain.org>
- * @author Christopher Smith <chris@jalakai.co.uk>
- * @author Tom N Harris <tnharris@whoopdedo.org>
- */
- protected function getPageWords($text) {
-
- $tokens = $this->tokenizer($text);
- $tokens = array_count_values($tokens); // count the frequency of each token
-
- $words = array();
- foreach ($tokens as $w=>$c) {
- $l = wordlen($w);
- if (isset($words[$l])){
- $words[$l][$w] = $c + (isset($words[$l][$w]) ? $words[$l][$w] : 0);
- }else{
- $words[$l] = array($w => $c);
- }
- }
-
- // arrive here with $words = array(wordlen => array(word => frequency))
- $word_idx_modified = false;
- $index = array(); //resulting index
- foreach (array_keys($words) as $wlen) {
- $word_idx = $this->getIndex('w', $wlen);
- foreach ($words[$wlen] as $word => $freq) {
- $word = (string)$word;
- $wid = array_search($word, $word_idx, true);
- if ($wid === false) {
- $wid = count($word_idx);
- $word_idx[] = $word;
- $word_idx_modified = true;
- }
- if (!isset($index[$wlen]))
- $index[$wlen] = array();
- $index[$wlen][$wid] = $freq;
- }
- // save back the word index
- if ($word_idx_modified && !$this->saveIndex('w', $wlen, $word_idx))
- return false;
- }
-
- return $index;
- }
-
- /**
- * Add/update keys to/of the metadata index.
- *
- * Adding new keys does not remove other keys for the page.
- * An empty value will erase the key.
- * The $key parameter can be an array to add multiple keys. $value will
- * not be used if $key is an array.
- *
- * @param string $page a page name
- * @param mixed $key a key string or array of key=>value pairs
- * @param mixed $value the value or list of values
- * @return boolean|string the function completed successfully
- *
- * @author Tom N Harris <tnharris@whoopdedo.org>
- * @author Michael Hamann <michael@content-space.de>
- */
- public function addMetaKeys($page, $key, $value=null) {
- if (!is_array($key)) {
- $key = array($key => $value);
- } elseif (!is_null($value)) {
- // $key is array, but $value is not null
- trigger_error("array passed to addMetaKeys but value is not null", E_USER_WARNING);
- }
-
- if (!$this->lock())
- return "locked";
-
- // load known documents
- $pid = $this->getPIDNoLock($page);
- if ($pid === false) {
- $this->unlock();
- return false;
- }
-
- // Special handling for titles so the index file is simpler
- if (array_key_exists('title', $key)) {
- $value = $key['title'];
- if (is_array($value)) {
- $value = $value[0];
- }
- $this->saveIndexKey('title', '', $pid, $value);
- unset($key['title']);
- }
-
- foreach ($key as $name => $values) {
- $metaname = idx_cleanName($name);
- $this->addIndexKey('metadata', '', $metaname);
- $metaidx = $this->getIndex($metaname.'_i', '');
- $metawords = $this->getIndex($metaname.'_w', '');
- $addwords = false;
-
- if (!is_array($values)) $values = array($values);
-
- $val_idx = $this->getIndexKey($metaname.'_p', '', $pid);
- if ($val_idx !== '') {
- $val_idx = explode(':', $val_idx);
- // -1 means remove, 0 keep, 1 add
- $val_idx = array_combine($val_idx, array_fill(0, count($val_idx), -1));
- } else {
- $val_idx = array();
- }
-
- foreach ($values as $val) {
- $val = (string)$val;
- if ($val !== "") {
- $id = array_search($val, $metawords, true);
- if ($id === false) {
- // didn't find $val, so we'll add it to the end of metawords and create a placeholder in metaidx
- $id = count($metawords);
- $metawords[$id] = $val;
- $metaidx[$id] = '';
- $addwords = true;
- }
- // test if value is already in the index
- if (isset($val_idx[$id]) && $val_idx[$id] <= 0){
- $val_idx[$id] = 0;
- } else { // else add it
- $val_idx[$id] = 1;
- }
- }
- }
-
- if ($addwords) {
- $this->saveIndex($metaname.'_w', '', $metawords);
- }
- $vals_changed = false;
- foreach ($val_idx as $id => $action) {
- if ($action == -1) {
- $metaidx[$id] = $this->updateTuple($metaidx[$id], $pid, 0);
- $vals_changed = true;
- unset($val_idx[$id]);
- } elseif ($action == 1) {
- $metaidx[$id] = $this->updateTuple($metaidx[$id], $pid, 1);
- $vals_changed = true;
- }
- }
-
- if ($vals_changed) {
- $this->saveIndex($metaname.'_i', '', $metaidx);
- $val_idx = implode(':', array_keys($val_idx));
- $this->saveIndexKey($metaname.'_p', '', $pid, $val_idx);
- }
-
- unset($metaidx);
- unset($metawords);
- }
-
- $this->unlock();
- return true;
- }
-
- /**
- * Rename a page in the search index without changing the indexed content. This function doesn't check if the
- * old or new name exists in the filesystem. It returns an error if the old page isn't in the page list of the
- * indexer and it deletes all previously indexed content of the new page.
- *
- * @param string $oldpage The old page name
- * @param string $newpage The new page name
- * @return string|bool If the page was successfully renamed, can be a message in the case of an error
- */
- public function renamePage($oldpage, $newpage) {
- if (!$this->lock()) return 'locked';
-
- $pages = $this->getPages();
-
- $id = array_search($oldpage, $pages, true);
- if ($id === false) {
- $this->unlock();
- return 'page is not in index';
- }
-
- $new_id = array_search($newpage, $pages, true);
- if ($new_id !== false) {
- // make sure the page is not in the index anymore
- if ($this->deletePageNoLock($newpage) !== true) {
- return false;
- }
-
- $pages[$new_id] = 'deleted:'.time().rand(0, 9999);
- }
-
- $pages[$id] = $newpage;
-
- // update index
- if (!$this->saveIndex('page', '', $pages)) {
- $this->unlock();
- return false;
- }
-
- // reset the pid cache
- $this->pidCache = array();
-
- $this->unlock();
- return true;
- }
-
- /**
- * Renames a meta value in the index. This doesn't change the meta value in the pages, it assumes that all pages
- * will be updated.
- *
- * @param string $key The metadata key of which a value shall be changed
- * @param string $oldvalue The old value that shall be renamed
- * @param string $newvalue The new value to which the old value shall be renamed, if exists values will be merged
- * @return bool|string If renaming the value has been successful, false or error message on error.
- */
- public function renameMetaValue($key, $oldvalue, $newvalue) {
- if (!$this->lock()) return 'locked';
-
- // change the relation references index
- $metavalues = $this->getIndex($key, '_w');
- $oldid = array_search($oldvalue, $metavalues, true);
- if ($oldid !== false) {
- $newid = array_search($newvalue, $metavalues, true);
- if ($newid !== false) {
- // free memory
- unset ($metavalues);
-
- // okay, now we have two entries for the same value. we need to merge them.
- $indexline = $this->getIndexKey($key.'_i', '', $oldid);
- if ($indexline != '') {
- $newindexline = $this->getIndexKey($key.'_i', '', $newid);
- $pagekeys = $this->getIndex($key.'_p', '');
- $parts = explode(':', $indexline);
- foreach ($parts as $part) {
- list($id, $count) = explode('*', $part);
- $newindexline = $this->updateTuple($newindexline, $id, $count);
-
- $keyline = explode(':', $pagekeys[$id]);
- // remove old meta value
- $keyline = array_diff($keyline, array($oldid));
- // add new meta value when not already present
- if (!in_array($newid, $keyline)) {
- array_push($keyline, $newid);
- }
- $pagekeys[$id] = implode(':', $keyline);
- }
- $this->saveIndex($key.'_p', '', $pagekeys);
- unset($pagekeys);
- $this->saveIndexKey($key.'_i', '', $oldid, '');
- $this->saveIndexKey($key.'_i', '', $newid, $newindexline);
- }
- } else {
- $metavalues[$oldid] = $newvalue;
- if (!$this->saveIndex($key.'_w', '', $metavalues)) {
- $this->unlock();
- return false;
- }
- }
- }
-
- $this->unlock();
- return true;
- }
-
- /**
- * Remove a page from the index
- *
- * Erases entries in all known indexes.
- *
- * @param string $page a page name
- * @return string|boolean the function completed successfully
- *
- * @author Tom N Harris <tnharris@whoopdedo.org>
- */
- public function deletePage($page) {
- if (!$this->lock())
- return "locked";
-
- $result = $this->deletePageNoLock($page);
-
- $this->unlock();
-
- return $result;
- }
-
- /**
- * Remove a page from the index without locking the index, only use this function if the index is already locked
- *
- * Erases entries in all known indexes.
- *
- * @param string $page a page name
- * @return boolean the function completed successfully
- *
- * @author Tom N Harris <tnharris@whoopdedo.org>
- */
- protected function deletePageNoLock($page) {
- // load known documents
- $pid = $this->getPIDNoLock($page);
- if ($pid === false) {
- return false;
- }
-
- // Remove obsolete index entries
- $pageword_idx = $this->getIndexKey('pageword', '', $pid);
- if ($pageword_idx !== '') {
- $delwords = explode(':',$pageword_idx);
- $upwords = array();
- foreach ($delwords as $word) {
- if ($word != '') {
- list($wlen,$wid) = explode('*', $word);
- $wid = (int)$wid;
- $upwords[$wlen][] = $wid;
- }
- }
- foreach ($upwords as $wlen => $widx) {
- $index = $this->getIndex('i', $wlen);
- foreach ($widx as $wid) {
- $index[$wid] = $this->updateTuple($index[$wid], $pid, 0);
- }
- $this->saveIndex('i', $wlen, $index);
- }
- }
- // Save the reverse index
- if (!$this->saveIndexKey('pageword', '', $pid, "")) {
- return false;
- }
-
- $this->saveIndexKey('title', '', $pid, "");
- $keyidx = $this->getIndex('metadata', '');
- foreach ($keyidx as $metaname) {
- $val_idx = explode(':', $this->getIndexKey($metaname.'_p', '', $pid));
- $meta_idx = $this->getIndex($metaname.'_i', '');
- foreach ($val_idx as $id) {
- if ($id === '') continue;
- $meta_idx[$id] = $this->updateTuple($meta_idx[$id], $pid, 0);
- }
- $this->saveIndex($metaname.'_i', '', $meta_idx);
- $this->saveIndexKey($metaname.'_p', '', $pid, '');
- }
-
- return true;
- }
-
- /**
- * Clear the whole index
- *
- * @return bool If the index has been cleared successfully
- */
- public function clear() {
- global $conf;
-
- if (!$this->lock()) return false;
-
- @unlink($conf['indexdir'].'/page.idx');
- @unlink($conf['indexdir'].'/title.idx');
- @unlink($conf['indexdir'].'/pageword.idx');
- @unlink($conf['indexdir'].'/metadata.idx');
- $dir = @opendir($conf['indexdir']);
- if($dir!==false){
- while(($f = readdir($dir)) !== false){
- if(substr($f,-4)=='.idx' &&
- (substr($f,0,1)=='i' || substr($f,0,1)=='w'
- || substr($f,-6)=='_w.idx' || substr($f,-6)=='_i.idx' || substr($f,-6)=='_p.idx'))
- @unlink($conf['indexdir']."/$f");
- }
- }
- @unlink($conf['indexdir'].'/lengths.idx');
-
- // clear the pid cache
- $this->pidCache = array();
-
- $this->unlock();
- return true;
- }
-
- /**
- * Split the text into words for fulltext search
- *
- * TODO: does this also need &$stopwords ?
- *
- * @triggers INDEXER_TEXT_PREPARE
- * This event allows plugins to modify the text before it gets tokenized.
- * Plugins intercepting this event should also intercept INDEX_VERSION_GET
- *
- * @param string $text plain text
- * @param boolean $wc are wildcards allowed?
- * @return array list of words in the text
- *
- * @author Tom N Harris <tnharris@whoopdedo.org>
- * @author Andreas Gohr <andi@splitbrain.org>
- */
- public function tokenizer($text, $wc=false) {
- $wc = ($wc) ? '' : '\*';
- $stopwords =& idx_get_stopwords();
-
- // prepare the text to be tokenized
- $evt = new Event('INDEXER_TEXT_PREPARE', $text);
- if ($evt->advise_before(true)) {
- if (preg_match('/[^0-9A-Za-z ]/u', $text)) {
- $text = \dokuwiki\Utf8\Asian::separateAsianWords($text);
- }
- }
- $evt->advise_after();
- unset($evt);
-
- $text = strtr($text,
- array(
- "\r" => ' ',
- "\n" => ' ',
- "\t" => ' ',
- "\xC2\xAD" => '', //soft-hyphen
- )
- );
- if (preg_match('/[^0-9A-Za-z ]/u', $text))
- $text = \dokuwiki\Utf8\Clean::stripspecials($text, ' ', '\._\-:'.$wc);
-
- $wordlist = explode(' ', $text);
- foreach ($wordlist as $i => $word) {
- $wordlist[$i] = (preg_match('/[^0-9A-Za-z]/u', $word)) ?
- \dokuwiki\Utf8\PhpString::strtolower($word) : strtolower($word);
- }
-
- foreach ($wordlist as $i => $word) {
- if ((!is_numeric($word) && strlen($word) < IDX_MINWORDLENGTH)
- || array_search($word, $stopwords, true) !== false)
- unset($wordlist[$i]);
- }
- return array_values($wordlist);
- }
-
- /**
- * Get the numeric PID of a page
- *
- * @param string $page The page to get the PID for
- * @return bool|int The page id on success, false on error
- */
- public function getPID($page) {
- // return PID without locking when it is in the cache
- if (isset($this->pidCache[$page])) return $this->pidCache[$page];
-
- if (!$this->lock())
- return false;
-
- // load known documents
- $pid = $this->getPIDNoLock($page);
- if ($pid === false) {
- $this->unlock();
- return false;
- }
-
- $this->unlock();
- return $pid;
- }
-
- /**
- * Get the numeric PID of a page without locking the index.
- * Only use this function when the index is already locked.
- *
- * @param string $page The page to get the PID for
- * @return bool|int The page id on success, false on error
- */
- protected function getPIDNoLock($page) {
- // avoid expensive addIndexKey operation for the most recently requested pages by using a cache
- if (isset($this->pidCache[$page])) return $this->pidCache[$page];
- $pid = $this->addIndexKey('page', '', $page);
- // limit cache to 10 entries by discarding the oldest element as in DokuWiki usually only the most recently
- // added item will be requested again
- if (count($this->pidCache) > 10) array_shift($this->pidCache);
- $this->pidCache[$page] = $pid;
- return $pid;
- }
-
- /**
- * Get the page id of a numeric PID
- *
- * @param int $pid The PID to get the page id for
- * @return string The page id
- */
- public function getPageFromPID($pid) {
- return $this->getIndexKey('page', '', $pid);
- }
-
- /**
- * Find pages in the fulltext index containing the words,
- *
- * The search words must be pre-tokenized, meaning only letters and
- * numbers with an optional wildcard
- *
- * The returned array will have the original tokens as key. The values
- * in the returned list is an array with the page names as keys and the
- * number of times that token appears on the page as value.
- *
- * @param array $tokens list of words to search for
- * @return array list of page names with usage counts
- *
- * @author Tom N Harris <tnharris@whoopdedo.org>
- * @author Andreas Gohr <andi@splitbrain.org>
- */
- public function lookup(&$tokens) {
- $result = array();
- $wids = $this->getIndexWords($tokens, $result);
- if (empty($wids)) return array();
- // load known words and documents
- $page_idx = $this->getIndex('page', '');
- $docs = array();
- foreach (array_keys($wids) as $wlen) {
- $wids[$wlen] = array_unique($wids[$wlen]);
- $index = $this->getIndex('i', $wlen);
- foreach($wids[$wlen] as $ixid) {
- if ($ixid < count($index))
- $docs["$wlen*$ixid"] = $this->parseTuples($page_idx, $index[$ixid]);
- }
- }
- // merge found pages into final result array
- $final = array();
- foreach ($result as $word => $res) {
- $final[$word] = array();
- foreach ($res as $wid) {
- // handle the case when ($ixid < count($index)) has been false
- // and thus $docs[$wid] hasn't been set.
- if (!isset($docs[$wid])) continue;
- $hits = &$docs[$wid];
- foreach ($hits as $hitkey => $hitcnt) {
- // make sure the document still exists
- if (!page_exists($hitkey, '', false)) continue;
- if (!isset($final[$word][$hitkey]))
- $final[$word][$hitkey] = $hitcnt;
- else
- $final[$word][$hitkey] += $hitcnt;
- }
- }
- }
- return $final;
- }
-
- /**
- * Find pages containing a metadata key.
- *
- * The metadata values are compared as case-sensitive strings. Pass a
- * callback function that returns true or false to use a different
- * comparison function. The function will be called with the $value being
- * searched for as the first argument, and the word in the index as the
- * second argument. The function preg_match can be used directly if the
- * values are regexes.
- *
- * @param string $key name of the metadata key to look for
- * @param string $value search term to look for, must be a string or array of strings
- * @param callback $func comparison function
- * @return array lists with page names, keys are query values if $value is array
- *
- * @author Tom N Harris <tnharris@whoopdedo.org>
- * @author Michael Hamann <michael@content-space.de>
- */
- public function lookupKey($key, &$value, $func=null) {
- if (!is_array($value))
- $value_array = array($value);
- else
- $value_array =& $value;
-
- // the matching ids for the provided value(s)
- $value_ids = array();
-
- $metaname = idx_cleanName($key);
-
- // get all words in order to search the matching ids
- if ($key == 'title') {
- $words = $this->getIndex('title', '');
- } else {
- $words = $this->getIndex($metaname.'_w', '');
- }
-
- if (!is_null($func)) {
- foreach ($value_array as $val) {
- foreach ($words as $i => $word) {
- if (call_user_func_array($func, array($val, $word)))
- $value_ids[$i][] = $val;
- }
- }
- } else {
- foreach ($value_array as $val) {
- $xval = $val;
- $caret = '^';
- $dollar = '$';
- // check for wildcards
- if (substr($xval, 0, 1) == '*') {
- $xval = substr($xval, 1);
- $caret = '';
- }
- if (substr($xval, -1, 1) == '*') {
- $xval = substr($xval, 0, -1);
- $dollar = '';
- }
- if (!$caret || !$dollar) {
- $re = $caret.preg_quote($xval, '/').$dollar;
- foreach(array_keys(preg_grep('/'.$re.'/', $words)) as $i)
- $value_ids[$i][] = $val;
- } else {
- if (($i = array_search($val, $words, true)) !== false)
- $value_ids[$i][] = $val;
- }
- }
- }
-
- unset($words); // free the used memory
-
- // initialize the result so it won't be null
- $result = array();
- foreach ($value_array as $val) {
- $result[$val] = array();
- }
-
- $page_idx = $this->getIndex('page', '');
-
- // Special handling for titles
- if ($key == 'title') {
- foreach ($value_ids as $pid => $val_list) {
- $page = $page_idx[$pid];
- foreach ($val_list as $val) {
- $result[$val][] = $page;
- }
- }
- } else {
- // load all lines and pages so the used lines can be taken and matched with the pages
- $lines = $this->getIndex($metaname.'_i', '');
-
- foreach ($value_ids as $value_id => $val_list) {
- // parse the tuples of the form page_id*1:page2_id*1 and so on, return value
- // is an array with page_id => 1, page2_id => 1 etc. so take the keys only
- $pages = array_keys($this->parseTuples($page_idx, $lines[$value_id]));
- foreach ($val_list as $val) {
- $result[$val] = array_merge($result[$val], $pages);
- }
- }
- }
- if (!is_array($value)) $result = $result[$value];
- return $result;
- }
-
- /**
- * Find the index ID of each search term.
- *
- * The query terms should only contain valid characters, with a '*' at
- * either the beginning or end of the word (or both).
- * The $result parameter can be used to merge the index locations with
- * the appropriate query term.
- *
- * @param array $words The query terms.
- * @param array $result Set to word => array("length*id" ...)
- * @return array Set to length => array(id ...)
- *
- * @author Tom N Harris <tnharris@whoopdedo.org>
- */
- protected function getIndexWords(&$words, &$result) {
- $tokens = array();
- $tokenlength = array();
- $tokenwild = array();
- foreach ($words as $word) {
- $result[$word] = array();
- $caret = '^';
- $dollar = '$';
- $xword = $word;
- $wlen = wordlen($word);
-
- // check for wildcards
- if (substr($xword, 0, 1) == '*') {
- $xword = substr($xword, 1);
- $caret = '';
- $wlen -= 1;
- }
- if (substr($xword, -1, 1) == '*') {
- $xword = substr($xword, 0, -1);
- $dollar = '';
- $wlen -= 1;
- }
- if ($wlen < IDX_MINWORDLENGTH && $caret && $dollar && !is_numeric($xword))
- continue;
- if (!isset($tokens[$xword]))
- $tokenlength[$wlen][] = $xword;
- if (!$caret || !$dollar) {
- $re = $caret.preg_quote($xword, '/').$dollar;
- $tokens[$xword][] = array($word, '/'.$re.'/');
- if (!isset($tokenwild[$xword]))
- $tokenwild[$xword] = $wlen;
- } else {
- $tokens[$xword][] = array($word, null);
- }
- }
- asort($tokenwild);
- // $tokens = array( base word => array( [ query term , regexp ] ... ) ... )
- // $tokenlength = array( base word length => base word ... )
- // $tokenwild = array( base word => base word length ... )
- $length_filter = empty($tokenwild) ? $tokenlength : min(array_keys($tokenlength));
- $indexes_known = $this->indexLengths($length_filter);
- if (!empty($tokenwild)) sort($indexes_known);
- // get word IDs
- $wids = array();
- foreach ($indexes_known as $ixlen) {
- $word_idx = $this->getIndex('w', $ixlen);
- // handle exact search
- if (isset($tokenlength[$ixlen])) {
- foreach ($tokenlength[$ixlen] as $xword) {
- $wid = array_search($xword, $word_idx, true);
- if ($wid !== false) {
- $wids[$ixlen][] = $wid;
- foreach ($tokens[$xword] as $w)
- $result[$w[0]][] = "$ixlen*$wid";
- }
- }
- }
- // handle wildcard search
- foreach ($tokenwild as $xword => $wlen) {
- if ($wlen >= $ixlen) break;
- foreach ($tokens[$xword] as $w) {
- if (is_null($w[1])) continue;
- foreach(array_keys(preg_grep($w[1], $word_idx)) as $wid) {
- $wids[$ixlen][] = $wid;
- $result[$w[0]][] = "$ixlen*$wid";
- }
- }
- }
- }
- return $wids;
- }
-
- /**
- * Return a list of all pages
- * Warning: pages may not exist!
- *
- * @param string $key list only pages containing the metadata key (optional)
- * @return array list of page names
- *
- * @author Tom N Harris <tnharris@whoopdedo.org>
- */
- public function getPages($key=null) {
- $page_idx = $this->getIndex('page', '');
- if (is_null($key)) return $page_idx;
-
- $metaname = idx_cleanName($key);
-
- // Special handling for titles
- if ($key == 'title') {
- $title_idx = $this->getIndex('title', '');
- array_splice($page_idx, count($title_idx));
- foreach ($title_idx as $i => $title)
- if ($title === "") unset($page_idx[$i]);
- return array_values($page_idx);
- }
-
- $pages = array();
- $lines = $this->getIndex($metaname.'_i', '');
- foreach ($lines as $line) {
- $pages = array_merge($pages, $this->parseTuples($page_idx, $line));
- }
- return array_keys($pages);
- }
-
- /**
- * Return a list of words sorted by number of times used
- *
- * @param int $min bottom frequency threshold
- * @param int $max upper frequency limit. No limit if $max<$min
- * @param int $minlen minimum length of words to count
- * @param string $key metadata key to list. Uses the fulltext index if not given
- * @return array list of words as the keys and frequency as values
- *
- * @author Tom N Harris <tnharris@whoopdedo.org>
- */
- public function histogram($min=1, $max=0, $minlen=3, $key=null) {
- if ($min < 1)
- $min = 1;
- if ($max < $min)
- $max = 0;
-
- $result = array();
-
- if ($key == 'title') {
- $index = $this->getIndex('title', '');
- $index = array_count_values($index);
- foreach ($index as $val => $cnt) {
- if ($cnt >= $min && (!$max || $cnt <= $max) && strlen($val) >= $minlen)
- $result[$val] = $cnt;
- }
- }
- elseif (!is_null($key)) {
- $metaname = idx_cleanName($key);
- $index = $this->getIndex($metaname.'_i', '');
- $val_idx = array();
- foreach ($index as $wid => $line) {
- $freq = $this->countTuples($line);
- if ($freq >= $min && (!$max || $freq <= $max))
- $val_idx[$wid] = $freq;
- }
- if (!empty($val_idx)) {
- $words = $this->getIndex($metaname.'_w', '');
- foreach ($val_idx as $wid => $freq) {
- if (strlen($words[$wid]) >= $minlen)
- $result[$words[$wid]] = $freq;
- }
- }
- }
- else {
- $lengths = idx_listIndexLengths();
- foreach ($lengths as $length) {
- if ($length < $minlen) continue;
- $index = $this->getIndex('i', $length);
- $words = null;
- foreach ($index as $wid => $line) {
- $freq = $this->countTuples($line);
- if ($freq >= $min && (!$max || $freq <= $max)) {
- if ($words === null)
- $words = $this->getIndex('w', $length);
- $result[$words[$wid]] = $freq;
- }
- }
- }
- }
-
- arsort($result);
- return $result;
- }
-
- /**
- * Lock the indexer.
- *
- * @author Tom N Harris <tnharris@whoopdedo.org>
- *
- * @return bool|string
- */
- protected function lock() {
- global $conf;
- $status = true;
- $run = 0;
- $lock = $conf['lockdir'].'/_indexer.lock';
- while (!@mkdir($lock, $conf['dmode'])) {
- usleep(50);
- if(is_dir($lock) && time()-@filemtime($lock) > 60*5){
- // looks like a stale lock - remove it
- if (!@rmdir($lock)) {
- $status = "removing the stale lock failed";
- return false;
- } else {
- $status = "stale lock removed";
- }
- }elseif($run++ == 1000){
- // we waited 5 seconds for that lock
- return false;
- }
- }
- if (!empty($conf['dperm'])) {
- chmod($lock, $conf['dperm']);
- }
- return $status;
- }
-
- /**
- * Release the indexer lock.
- *
- * @author Tom N Harris <tnharris@whoopdedo.org>
- *
- * @return bool
- */
- protected function unlock() {
- global $conf;
- @rmdir($conf['lockdir'].'/_indexer.lock');
- return true;
- }
-
- /**
- * Retrieve the entire index.
- *
- * The $suffix argument is for an index that is split into
- * multiple parts. Different index files should use different
- * base names.
- *
- * @param string $idx name of the index
- * @param string $suffix subpart identifier
- * @return array list of lines without CR or LF
- *
- * @author Tom N Harris <tnharris@whoopdedo.org>
- */
- protected function getIndex($idx, $suffix) {
- global $conf;
- $fn = $conf['indexdir'].'/'.$idx.$suffix.'.idx';
- if (!file_exists($fn)) return array();
- return file($fn, FILE_IGNORE_NEW_LINES);
- }
-
- /**
- * Replace the contents of the index with an array.
- *
- * @param string $idx name of the index
- * @param string $suffix subpart identifier
- * @param array $lines list of lines without LF
- * @return bool If saving succeeded
- *
- * @author Tom N Harris <tnharris@whoopdedo.org>
- */
- protected function saveIndex($idx, $suffix, &$lines) {
- global $conf;
- $fn = $conf['indexdir'].'/'.$idx.$suffix;
- $fh = @fopen($fn.'.tmp', 'w');
- if (!$fh) return false;
- fwrite($fh, join("\n", $lines));
- if (!empty($lines))
- fwrite($fh, "\n");
- fclose($fh);
- if (isset($conf['fperm']))
- chmod($fn.'.tmp', $conf['fperm']);
- io_rename($fn.'.tmp', $fn.'.idx');
- return true;
- }
-
- /**
- * Retrieve a line from the index.
- *
- * @param string $idx name of the index
- * @param string $suffix subpart identifier
- * @param int $id the line number
- * @return string a line with trailing whitespace removed
- *
- * @author Tom N Harris <tnharris@whoopdedo.org>
- */
- protected function getIndexKey($idx, $suffix, $id) {
- global $conf;
- $fn = $conf['indexdir'].'/'.$idx.$suffix.'.idx';
- if (!file_exists($fn)) return '';
- $fh = @fopen($fn, 'r');
- if (!$fh) return '';
- $ln = -1;
- while (($line = fgets($fh)) !== false) {
- if (++$ln == $id) break;
- }
- fclose($fh);
- return rtrim((string)$line);
- }
-
- /**
- * Write a line into the index.
- *
- * @param string $idx name of the index
- * @param string $suffix subpart identifier
- * @param int $id the line number
- * @param string $line line to write
- * @return bool If saving succeeded
- *
- * @author Tom N Harris <tnharris@whoopdedo.org>
- */
- protected function saveIndexKey($idx, $suffix, $id, $line) {
- global $conf;
- if (substr($line, -1) != "\n")
- $line .= "\n";
- $fn = $conf['indexdir'].'/'.$idx.$suffix;
- $fh = @fopen($fn.'.tmp', 'w');
- if (!$fh) return false;
- $ih = @fopen($fn.'.idx', 'r');
- if ($ih) {
- $ln = -1;
- while (($curline = fgets($ih)) !== false) {
- fwrite($fh, (++$ln == $id) ? $line : $curline);
- }
- if ($id > $ln) {
- while ($id > ++$ln)
- fwrite($fh, "\n");
- fwrite($fh, $line);
- }
- fclose($ih);
- } else {
- $ln = -1;
- while ($id > ++$ln)
- fwrite($fh, "\n");
- fwrite($fh, $line);
- }
- fclose($fh);
- if (isset($conf['fperm']))
- chmod($fn.'.tmp', $conf['fperm']);
- io_rename($fn.'.tmp', $fn.'.idx');
- return true;
- }
-
- /**
- * Retrieve or insert a value in the index.
- *
- * @param string $idx name of the index
- * @param string $suffix subpart identifier
- * @param string $value line to find in the index
- * @return int|bool line number of the value in the index or false if writing the index failed
- *
- * @author Tom N Harris <tnharris@whoopdedo.org>
- */
- protected function addIndexKey($idx, $suffix, $value) {
- $index = $this->getIndex($idx, $suffix);
- $id = array_search($value, $index, true);
- if ($id === false) {
- $id = count($index);
- $index[$id] = $value;
- if (!$this->saveIndex($idx, $suffix, $index)) {
- trigger_error("Failed to write $idx index", E_USER_ERROR);
- return false;
- }
- }
- return $id;
- }
-
- /**
- * Get the list of lengths indexed in the wiki.
- *
- * Read the index directory or a cache file and returns
- * a sorted array of lengths of the words used in the wiki.
- *
- * @author YoBoY <yoboy.leguesh@gmail.com>
- *
- * @return array
- */
- protected function listIndexLengths() {
- return idx_listIndexLengths();
- }
-
- /**
- * Get the word lengths that have been indexed.
- *
- * Reads the index directory and returns an array of lengths
- * that there are indices for.
- *
- * @author YoBoY <yoboy.leguesh@gmail.com>
- *
- * @param array|int $filter
- * @return array
- */
- protected function indexLengths($filter) {
- global $conf;
- $idx = array();
- if (is_array($filter)) {
- // testing if index files exist only
- $path = $conf['indexdir']."/i";
- foreach ($filter as $key => $value) {
- if (file_exists($path.$key.'.idx'))
- $idx[] = $key;
- }
- } else {
- $lengths = idx_listIndexLengths();
- foreach ($lengths as $key => $length) {
- // keep all the values equal or superior
- if ((int)$length >= (int)$filter)
- $idx[] = $length;
- }
- }
- return $idx;
- }
-
- /**
- * Insert or replace a tuple in a line.
- *
- * @author Tom N Harris <tnharris@whoopdedo.org>
- *
- * @param string $line
- * @param string|int $id
- * @param int $count
- * @return string
- */
- protected function updateTuple($line, $id, $count) {
- if ($line != ''){
- $line = preg_replace('/(^|:)'.preg_quote($id,'/').'\*\d*/', '', $line);
- }
- $line = trim($line, ':');
- if ($count) {
- if ($line) {
- return "$id*$count:".$line;
- } else {
- return "$id*$count";
- }
- }
- return $line;
- }
-
- /**
- * Split a line into an array of tuples.
- *
- * @author Tom N Harris <tnharris@whoopdedo.org>
- * @author Andreas Gohr <andi@splitbrain.org>
- *
- * @param array $keys
- * @param string $line
- * @return array
- */
- protected function parseTuples(&$keys, $line) {
- $result = array();
- if ($line == '') return $result;
- $parts = explode(':', $line);
- foreach ($parts as $tuple) {
- if ($tuple === '') continue;
- list($key, $cnt) = explode('*', $tuple);
- if (!$cnt) continue;
- $key = $keys[$key];
- if ($key === false || is_null($key)) continue;
- $result[$key] = $cnt;
- }
- return $result;
- }
-
- /**
- * Sum the counts in a list of tuples.
- *
- * @author Tom N Harris <tnharris@whoopdedo.org>
- *
- * @param string $line
- * @return int
- */
- protected function countTuples($line) {
- $freq = 0;
- $parts = explode(':', $line);
- foreach ($parts as $tuple) {
- if ($tuple === '') continue;
- list(/* $pid */, $cnt) = explode('*', $tuple);
- $freq += (int)$cnt;
- }
- return $freq;
- }
-}
-
-/**
* Create an instance of the indexer.
*
- * @return Doku_Indexer a Doku_Indexer
+ * @return Indexer an Indexer
*
* @author Tom N Harris <tnharris@whoopdedo.org>
*/
function idx_get_indexer() {
static $Indexer;
if (!isset($Indexer)) {
- $Indexer = new Doku_Indexer();
+ $Indexer = new Indexer();
}
return $Indexer;
}
diff --git a/inc/init.php b/inc/init.php
index 548ac80db..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
@@ -576,7 +576,7 @@ function fullpath($path,$exists=false){
$iswin = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' || !empty($GLOBALS['DOKU_UNITTEST_ASSUME_WINDOWS']));
// find the (indestructable) root of the path - keeps windows stuff intact
- if($path{0} == '/'){
+ if($path[0] == '/'){
$root = '/';
}elseif($iswin){
// match drive letter and UNC paths
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/cs/lang.php b/inc/lang/cs/lang.php
index 7a9b7200a..8d46dd0aa 100644
--- a/inc/lang/cs/lang.php
+++ b/inc/lang/cs/lang.php
@@ -3,6 +3,7 @@
/**
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*
+ * @author Petr Kajzar <petr.kajzar@lf1.cuni.cz>
* @author Robert Surý <rsurycz@seznam.cz>
* @author Martin Hořínek <hev@hev.cz>
* @author Jonáš Dyba <jonas.dyba@gmail.com>
diff --git a/inc/lang/de/lang.php b/inc/lang/de/lang.php
index d4a325559..47bc6af8f 100644
--- a/inc/lang/de/lang.php
+++ b/inc/lang/de/lang.php
@@ -3,6 +3,8 @@
/**
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*
+ * @author Eric Haberstroh <ehaberstroh@gmail.com>
+ * @author C!own77 <clown77@posteo.de>
* @author Anonymous <anonymous@example.org>
* @author Michaelsy <github@informantum.de>
* @author Benjamin Molitor <bmolitor@uos.de>
diff --git a/inc/lang/en/mailtext.txt b/inc/lang/en/mailtext.txt
index aea14d459..eac40356d 100644
--- a/inc/lang/en/mailtext.txt
+++ b/inc/lang/en/mailtext.txt
@@ -1,12 +1,15 @@
A page in your DokuWiki was added or changed. Here are the details:
-Date : @DATE@
-Browser : @BROWSER@
-IP-Address : @IPADDRESS@
-Hostname : @HOSTNAME@
-Old Revision: @OLDPAGE@
-New Revision: @NEWPAGE@
-Edit Summary: @SUMMARY@
-User : @USER@
+Browser : @BROWSER@
+IP Address : @IPADDRESS@
+Hostname : @HOSTNAME@
+Old Revision : @OLDPAGE@
+New Revision : @NEWPAGE@
+Date of New Revision: @DATE@
+Edit Summary : @SUMMARY@
+User : @USER@
+
+There may be newer changes after this revision. If this
+happens, a message will be shown on the top of the rev page.
@DIFF@
diff --git a/inc/lang/en/subscr_single.txt b/inc/lang/en/subscr_single.txt
index 8f097dc3e..046b9945d 100644
--- a/inc/lang/en/subscr_single.txt
+++ b/inc/lang/en/subscr_single.txt
@@ -7,11 +7,11 @@ Here are the changes:
@DIFF@
--------------------------------------------------------
-Date : @DATE@
-User : @USER@
-Edit Summary: @SUMMARY@
-Old Revision: @OLDPAGE@
-New Revision: @NEWPAGE@
+User : @USER@
+Edit Summary : @SUMMARY@
+Old Revision : @OLDPAGE@
+New Revision : @NEWPAGE@
+Date of New Revision: @DATE@
To cancel the page notifications, log into the wiki at
@DOKUWIKIURL@ then visit
diff --git a/inc/lang/es/lang.php b/inc/lang/es/lang.php
index 7924a7da7..95a1d644e 100644
--- a/inc/lang/es/lang.php
+++ b/inc/lang/es/lang.php
@@ -3,10 +3,10 @@
/**
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*
+ * @author Domingo Redal <docxml@gmail.com>
* @author Liliana <lilianasaidon@gmail.com>
* @author Alex Cachinero <anarres@protonmail.com>
* @author WIRESLINKEA <wireslinkea@gmail.com>
- * @author Domingo Redal <docxml@gmail.com>
* @author Zigor Astarbe <zigor@astarbe.com>
* @author Adrián Ariza <adrian_ariza.ciudad.com.ar>
* @author Gabiel Molina <gabriel191@gmail.com>
diff --git a/inc/lang/fi/lang.php b/inc/lang/fi/lang.php
index ebcca3347..a872b156f 100644
--- a/inc/lang/fi/lang.php
+++ b/inc/lang/fi/lang.php
@@ -2,7 +2,8 @@
/**
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
- *
+ *
+ * @author Tuomo Hartikainen <tuomo.hartikainen@heksia.fi>
* @author Petteri <petteri@gmail.com>
* @author Matti Pöllä <mpo@iki.fi>
* @author Otto Vainio <otto@valjakko.net>
@@ -71,43 +72,6 @@ $lang['badpassconfirm'] = 'Valitan. Salasana oli väärin';
$lang['minoredit'] = 'Pieni muutos';
$lang['draftdate'] = 'Luonnos tallennettu automaattisesti';
$lang['nosecedit'] = 'Sivu on muuttunut välillä ja kappaleen tiedot olivat vanhentuneet. Koko sivu ladattu.';
-$lang['regmissing'] = 'Kaikki kentät tulee täyttää.';
-$lang['reguexists'] = 'Käyttäjä tällä käyttäjänimellä on jo olemassa.';
-$lang['regsuccess'] = 'Käyttäjä luotiin ja salasana lähetettiin sähköpostilla.';
-$lang['regsuccess2'] = 'Käyttäjänimi on luotu.';
-$lang['regfail'] = 'Valitsemaasi käyttäjää ei voitu luoda.';
-$lang['regmailfail'] = 'Näyttää siltä, että salasanan lähettämisessä tapahtui virhe. Ota yhteys ylläpitäjään!';
-$lang['regbadmail'] = 'Antamasi sähköpostiosoite näyttää epäkelvolta. Jos pidät tätä virheenä ota yhteys ylläpitäjään.';
-$lang['regbadpass'] = 'Annetut kaksi salasanaa eivät täsmää. Yritä uudelleen.';
-$lang['regpwmail'] = 'DokuWiki salasanasi';
-$lang['reghere'] = 'Puuttuuko sinulta käyttäjätili? Hanki sellainen';
-$lang['profna'] = 'Tässä wikissä profiilien muokkaaminen ei ole mahdollista';
-$lang['profnochange'] = 'Ei muutoksia.';
-$lang['profnoempty'] = 'Tyhjä nimi tai sähköpostiosoite ei ole sallittu.';
-$lang['profchanged'] = 'Käyttäjän profiilin päivitys onnistui.';
-$lang['profnodelete'] = 'Tässä wikissä ei voi poistaa käyttäjiä';
-$lang['profdeleteuser'] = 'Poista tili';
-$lang['profdeleted'] = 'Käyttäjätilisi on postettu tästä wikistä';
-$lang['profconfdelete'] = 'Haluan poistaa käyttäjätilini tästä wikistä. <br/> Tätä toimintoa ei voi myöhemmin peruuttaa.';
-$lang['profconfdeletemissing'] = 'Vahvistus rastia ei valittu';
-$lang['pwdforget'] = 'Unohtuiko salasana? Hanki uusi';
-$lang['resendna'] = 'Tämä wiki ei tue salasanan uudelleenlähettämistä.';
-$lang['resendpwd'] = 'Aseta uusisalasana';
-$lang['resendpwdmissing'] = 'Kaikki kentät on täytettävä.';
-$lang['resendpwdnouser'] = 'Käyttäjää ei löydy tietokannastamme.';
-$lang['resendpwdbadauth'] = 'Tunnistuskoodi on virheellinen. Varmista, että käytit koko varmistuslinkkiä.';
-$lang['resendpwdconfirm'] = 'Varmistuslinkki on lähetetty sähköpostilla';
-$lang['resendpwdsuccess'] = 'Uusi salasanasi on lähetetty sähköpostilla.';
-$lang['license'] = 'Jollei muuta ole mainittu, niin sisältö tässä wikissä on lisensoitu seuraavalla lisenssillä:';
-$lang['licenseok'] = 'Huom: Muokkaamalla tätä sivua suostut lisensoimaan sisällön seuraavan lisenssin mukaisesti:';
-$lang['searchmedia'] = 'Etsi tiedostoa nimeltä:';
-$lang['searchmedia_in'] = 'Etsi kohteesta %s';
-$lang['txt_upload'] = 'Valitse tiedosto lähetettäväksi:';
-$lang['txt_filename'] = 'Lähetä nimellä (valinnainen):';
-$lang['txt_overwrt'] = 'Ylikirjoita olemassa oleva';
-$lang['maxuploadsize'] = 'Palvelimelle siirto max. %s / tiedosto.';
-$lang['lockedby'] = 'Tällä hetkellä tiedoston on lukinnut:';
-$lang['lockexpire'] = 'Lukitus päättyy:';
$lang['js']['willexpire'] = 'Lukituksesi tämän sivun muokkaukseen päättyy minuutin kuluttua.\nRistiriitojen välttämiseksi paina esikatselu-nappia nollataksesi lukitusajan.';
$lang['js']['notsavedyet'] = 'Dokumentissa on tallentamattomia muutoksia, jotka häviävät.
Haluatko varmasti jatkaa?';
@@ -151,6 +115,43 @@ $lang['js']['media_done_btn'] = 'Valmis';
$lang['js']['media_drop'] = 'Pudota lähetettävät tiedostot tähän';
$lang['js']['media_cancel'] = 'Poista';
$lang['js']['media_overwrt'] = 'Ylikirjoita olemassa olevat tiedostot';
+$lang['regmissing'] = 'Kaikki kentät tulee täyttää.';
+$lang['reguexists'] = 'Käyttäjä tällä käyttäjänimellä on jo olemassa.';
+$lang['regsuccess'] = 'Käyttäjä luotiin ja salasana lähetettiin sähköpostilla.';
+$lang['regsuccess2'] = 'Käyttäjänimi on luotu.';
+$lang['regfail'] = 'Valitsemaasi käyttäjää ei voitu luoda.';
+$lang['regmailfail'] = 'Näyttää siltä, että salasanan lähettämisessä tapahtui virhe. Ota yhteys ylläpitäjään!';
+$lang['regbadmail'] = 'Antamasi sähköpostiosoite näyttää epäkelvolta. Jos pidät tätä virheenä ota yhteys ylläpitäjään.';
+$lang['regbadpass'] = 'Annetut kaksi salasanaa eivät täsmää. Yritä uudelleen.';
+$lang['regpwmail'] = 'DokuWiki salasanasi';
+$lang['reghere'] = 'Puuttuuko sinulta käyttäjätili? Hanki sellainen';
+$lang['profna'] = 'Tässä wikissä profiilien muokkaaminen ei ole mahdollista';
+$lang['profnochange'] = 'Ei muutoksia.';
+$lang['profnoempty'] = 'Tyhjä nimi tai sähköpostiosoite ei ole sallittu.';
+$lang['profchanged'] = 'Käyttäjän profiilin päivitys onnistui.';
+$lang['profnodelete'] = 'Tässä wikissä ei voi poistaa käyttäjiä';
+$lang['profdeleteuser'] = 'Poista tili';
+$lang['profdeleted'] = 'Käyttäjätilisi on postettu tästä wikistä';
+$lang['profconfdelete'] = 'Haluan poistaa käyttäjätilini tästä wikistä. <br/> Tätä toimintoa ei voi myöhemmin peruuttaa.';
+$lang['profconfdeletemissing'] = 'Vahvistus rastia ei valittu';
+$lang['pwdforget'] = 'Unohtuiko salasana? Hanki uusi';
+$lang['resendna'] = 'Tämä wiki ei tue salasanan uudelleenlähettämistä.';
+$lang['resendpwd'] = 'Aseta uusisalasana';
+$lang['resendpwdmissing'] = 'Kaikki kentät on täytettävä.';
+$lang['resendpwdnouser'] = 'Käyttäjää ei löydy tietokannastamme.';
+$lang['resendpwdbadauth'] = 'Tunnistuskoodi on virheellinen. Varmista, että käytit koko varmistuslinkkiä.';
+$lang['resendpwdconfirm'] = 'Varmistuslinkki on lähetetty sähköpostilla';
+$lang['resendpwdsuccess'] = 'Uusi salasanasi on lähetetty sähköpostilla.';
+$lang['license'] = 'Jollei muuta ole mainittu, niin sisältö tässä wikissä on lisensoitu seuraavalla lisenssillä:';
+$lang['licenseok'] = 'Huom: Muokkaamalla tätä sivua suostut lisensoimaan sisällön seuraavan lisenssin mukaisesti:';
+$lang['searchmedia'] = 'Etsi tiedostoa nimeltä:';
+$lang['searchmedia_in'] = 'Etsi kohteesta %s';
+$lang['txt_upload'] = 'Valitse tiedosto lähetettäväksi:';
+$lang['txt_filename'] = 'Lähetä nimellä (valinnainen):';
+$lang['txt_overwrt'] = 'Ylikirjoita olemassa oleva';
+$lang['maxuploadsize'] = 'Palvelimelle siirto max. %s / tiedosto.';
+$lang['lockedby'] = 'Tällä hetkellä tiedoston on lukinnut:';
+$lang['lockexpire'] = 'Lukitus päättyy:';
$lang['rssfailed'] = 'Virhe tapahtui noudettaessa tätä syötettä: ';
$lang['nothingfound'] = 'Mitään ei löytynyt.';
$lang['mediaselect'] = 'Mediatiedoston valinta';
@@ -338,5 +339,5 @@ $lang['searchresult'] = 'Haun tulokset';
$lang['plainhtml'] = 'pelkkä HTML';
$lang['wikimarkup'] = 'Wiki markup';
$lang['unable_to_parse_date'] = 'Parametrin "%s" jäsennys ei onnistu.';
-$lang['email_signature_text'] = 'Tämän postin loi DokuWiki osoitteessa
+$lang['email_signature_text'] = 'Tämän postin loi DokuWiki osoitteessa
@DOKUWIKIURL@';
diff --git a/inc/lang/fr/lang.php b/inc/lang/fr/lang.php
index b43e2fc94..2480f2936 100644
--- a/inc/lang/fr/lang.php
+++ b/inc/lang/fr/lang.php
@@ -3,8 +3,8 @@
/**
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*
+ * @author Schplurtz le Déboulonné <Schplurtz@laposte.net>
* @author PaliPalo <palipalo@hotmail.fr>
- * @author Schplurtz le Déboulonné <schplurtz@laposte.net>
* @author Laurent Ponthieu <contact@coopindus.fr>
* @author Damien Regad <dregad@mantisbt.org>
* @author Michael Bohn <mjbohn@gmail.com>
@@ -304,10 +304,10 @@ $lang['img_camera'] = 'Appareil photo:';
$lang['img_keywords'] = 'Mots-clés:';
$lang['img_width'] = 'Largeur:';
$lang['img_height'] = 'Hauteur:';
-$lang['subscr_subscribe_success'] = '%s a été ajouté à la liste des abonnés de %s';
+$lang['subscr_subscribe_success'] = '%s a été ajouté à la liste des abonnés à %s';
$lang['subscr_subscribe_error'] = 'Erreur à l\'ajout de %s à la liste des abonnés de %s';
$lang['subscr_subscribe_noaddress'] = 'Il n\'y a pas d\'adresse associée à votre identifiant, vous ne pouvez pas être ajouté à la liste des abonnés.';
-$lang['subscr_unsubscribe_success'] = '%s a été supprimé de la liste des abonnés de %s';
+$lang['subscr_unsubscribe_success'] = '%s a été supprimé de la liste des abonnés à %s';
$lang['subscr_unsubscribe_error'] = 'Erreur au retrait de %s de la liste des abonnés de %s';
$lang['subscr_already_subscribed'] = '%s est déjà abonné à %s';
$lang['subscr_not_subscribed'] = '%s n\'est pas abonné à %s';
diff --git a/inc/lang/id/lang.php b/inc/lang/id/lang.php
index dc9d66259..61f279839 100644
--- a/inc/lang/id/lang.php
+++ b/inc/lang/id/lang.php
@@ -3,6 +3,7 @@
/**
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*
+ * @author rusly-id <rusly-id@users.noreply.github.com>
* @author mubaidillah <mubaidillah@gmail.com>
* @author Irwan Butar Butar <irwansah.putra@gmail.com>
* @author Yustinus Waruwu <juswaruwu@gmail.com>
@@ -64,43 +65,6 @@ $lang['badlogin'] = 'Maaf, username atau password salah.';
$lang['badpassconfirm'] = 'Maaf, password salah';
$lang['minoredit'] = 'Perubahan Minor';
$lang['draftdate'] = 'Simpan draft secara otomatis';
-$lang['regmissing'] = 'Maaf, Anda harus mengisi semua field.';
-$lang['reguexists'] = 'Maaf, user dengan user login ini telah ada.';
-$lang['regsuccess'] = 'User telah didaftarkan dan password telah dikirim ke email Anda.';
-$lang['regsuccess2'] = 'User telah dibuatkan.';
-$lang['regmailfail'] = 'Kami menemukan kesalahan saat mengirimkan password ke alamat email Anda. Mohon hubungi administrator.';
-$lang['regbadmail'] = 'Alamat email yang Anda masukkan tidak valid - jika menurut Anda hal ini adalah kesalahan sistem, mohon hubungi admin.';
-$lang['regbadpass'] = 'Passwod yang dimasukkan tidak sama. Silahkan ulangi lagi.';
-$lang['regpwmail'] = 'Password DokuWiki Anda';
-$lang['reghere'] = 'Anda belum mempunyai account? silahkan ';
-$lang['profna'] = 'Wiki ini tidak mengijinkan perubahan profil.';
-$lang['profnochange'] = 'Tidak ada perubahan.';
-$lang['profnoempty'] = 'Mohon mengisikan nama atau alamat email.';
-$lang['profchanged'] = 'Profil User berhasil diubah.';
-$lang['profnodelete'] = 'Wiki ini tidak mendukung penghapusan pengguna';
-$lang['profdeleteuser'] = 'Hapus Akun';
-$lang['profdeleted'] = 'Akun anda telah dihapus dari wiki ini';
-$lang['profconfdelete'] = 'Saya berharap menghapus akun saya dari wiki ini.
-Aksi ini tidak bisa diselesaikan.';
-$lang['profconfdeletemissing'] = 'Knfirmasi check box tidak tercentang';
-$lang['pwdforget'] = 'Lupa Password? Dapatkan yang baru';
-$lang['resendna'] = 'Wiki ini tidak mendukung pengiriman ulang password.';
-$lang['resendpwd'] = 'Atur password baru';
-$lang['resendpwdmissing'] = 'Maaf, Anda harus mengisikan semua field.';
-$lang['resendpwdnouser'] = 'Maaf, user ini tidak ditemukan.';
-$lang['resendpwdbadauth'] = 'Maaf, kode autentikasi tidak valid. Pastikan Anda menggunakan keseluruhan link konfirmasi.';
-$lang['resendpwdconfirm'] = 'Link konfirmasi telah dikirim melalui email.';
-$lang['resendpwdsuccess'] = 'Password baru Anda telah dikirim melalui email.';
-$lang['license'] = 'Kecuali jika dinyatakan lain, konten pada wiki ini dilisensikan dibawah lisensi berikut:';
-$lang['licenseok'] = 'Catatan: Dengan menyunting halaman ini, Anda setuju untuk melisensikan konten Anda dibawah lisensi berikut:';
-$lang['searchmedia'] = 'Cari nama file:';
-$lang['searchmedia_in'] = 'Cari di %s';
-$lang['txt_upload'] = 'File yang akan diupload:';
-$lang['txt_filename'] = 'Masukkan nama wiki (opsional):';
-$lang['txt_overwrt'] = 'File yang telah ada akan ditindih';
-$lang['maxuploadsize'] = 'Unggah maks. %s per berkas';
-$lang['lockedby'] = 'Sedang dikunci oleh:';
-$lang['lockexpire'] = 'Penguncian artikel sampai dengan:';
$lang['js']['willexpire'] = 'Halaman yang sedang Anda kunci akan berakhir dalam waktu kurang lebih satu menit.\nUntuk menghindari konflik, gunakan tombol Preview untuk me-reset timer pengunci.';
$lang['js']['notsavedyet'] = 'Perubahan yang belum disimpan akan hilang.\nYakin akan dilanjutkan?';
$lang['js']['searchmedia'] = 'Cari file';
@@ -142,6 +106,43 @@ $lang['js']['media_done_btn'] = 'Selesai';
$lang['js']['media_drop'] = 'Tarik file disini untuk mengunggah';
$lang['js']['media_cancel'] = 'Buang';
$lang['js']['media_overwrt'] = 'Timpa berkas yang ada';
+$lang['regmissing'] = 'Maaf, Anda harus mengisi semua field.';
+$lang['reguexists'] = 'Maaf, user dengan user login ini telah ada.';
+$lang['regsuccess'] = 'User telah didaftarkan dan password telah dikirim ke email Anda.';
+$lang['regsuccess2'] = 'User telah dibuatkan.';
+$lang['regmailfail'] = 'Kami menemukan kesalahan saat mengirimkan password ke alamat email Anda. Mohon hubungi administrator.';
+$lang['regbadmail'] = 'Alamat email yang Anda masukkan tidak valid - jika menurut Anda hal ini adalah kesalahan sistem, mohon hubungi admin.';
+$lang['regbadpass'] = 'Passwod yang dimasukkan tidak sama. Silahkan ulangi lagi.';
+$lang['regpwmail'] = 'Password DokuWiki Anda';
+$lang['reghere'] = 'Anda belum mempunyai account? silahkan ';
+$lang['profna'] = 'Wiki ini tidak mengijinkan perubahan profil.';
+$lang['profnochange'] = 'Tidak ada perubahan.';
+$lang['profnoempty'] = 'Mohon mengisikan nama atau alamat email.';
+$lang['profchanged'] = 'Profil User berhasil diubah.';
+$lang['profnodelete'] = 'Wiki ini tidak mendukung penghapusan pengguna';
+$lang['profdeleteuser'] = 'Hapus Akun';
+$lang['profdeleted'] = 'Akun anda telah dihapus dari wiki ini';
+$lang['profconfdelete'] = 'Saya berharap menghapus akun saya dari wiki ini.
+Aksi ini tidak bisa diselesaikan.';
+$lang['profconfdeletemissing'] = 'Knfirmasi check box tidak tercentang';
+$lang['pwdforget'] = 'Lupa Password? Dapatkan yang baru';
+$lang['resendna'] = 'Wiki ini tidak mendukung pengiriman ulang password.';
+$lang['resendpwd'] = 'Atur password baru';
+$lang['resendpwdmissing'] = 'Maaf, Anda harus mengisikan semua field.';
+$lang['resendpwdnouser'] = 'Maaf, user ini tidak ditemukan.';
+$lang['resendpwdbadauth'] = 'Maaf, kode autentikasi tidak valid. Pastikan Anda menggunakan keseluruhan link konfirmasi.';
+$lang['resendpwdconfirm'] = 'Link konfirmasi telah dikirim melalui email.';
+$lang['resendpwdsuccess'] = 'Password baru Anda telah dikirim melalui email.';
+$lang['license'] = 'Kecuali jika dinyatakan lain, konten pada wiki ini dilisensikan dibawah lisensi berikut:';
+$lang['licenseok'] = 'Catatan: Dengan menyunting halaman ini, Anda setuju untuk melisensikan konten Anda dibawah lisensi berikut:';
+$lang['searchmedia'] = 'Cari nama file:';
+$lang['searchmedia_in'] = 'Cari di %s';
+$lang['txt_upload'] = 'File yang akan diupload:';
+$lang['txt_filename'] = 'Masukkan nama wiki (opsional):';
+$lang['txt_overwrt'] = 'File yang telah ada akan ditindih';
+$lang['maxuploadsize'] = 'Unggah maks. %s per berkas';
+$lang['lockedby'] = 'Sedang dikunci oleh:';
+$lang['lockexpire'] = 'Penguncian artikel sampai dengan:';
$lang['rssfailed'] = 'Error terjadi saat mengambil feed: ';
$lang['nothingfound'] = 'Tidak menemukan samasekali.';
$lang['mediaselect'] = 'Pilihan Mediafile';
@@ -308,5 +309,5 @@ $lang['media_restore'] = 'Kembalikan versi ini';
$lang['currentns'] = 'Namespace saat ini';
$lang['searchresult'] = 'Hasil Pencarian';
$lang['wikimarkup'] = 'Markah Wiki';
-$lang['email_signature_text'] = 'Email ini dibuat otomatis oleh DokuWiki
+$lang['email_signature_text'] = 'Email ini dibuat otomatis oleh DokuWiki
@DOKUWIKIURL@';
diff --git a/inc/lang/ja/lang.php b/inc/lang/ja/lang.php
index 17b410a43..968edf4eb 100644
--- a/inc/lang/ja/lang.php
+++ b/inc/lang/ja/lang.php
@@ -97,10 +97,10 @@ $lang['js']['mediaoriginal'] = 'オリジナルのサイズ';
$lang['js']['medialnk'] = '詳細ページへのリンク';
$lang['js']['mediadirect'] = 'オリジナルへの直リンク';
$lang['js']['medianolnk'] = 'リンク無し';
-$lang['js']['medianolink'] = 'イメージをリンクしない';
-$lang['js']['medialeft'] = 'イメージを左に寄せる';
-$lang['js']['mediaright'] = 'イメージを右に寄せる';
-$lang['js']['mediacenter'] = 'イメージを中央に寄せる';
+$lang['js']['medianolink'] = '画像へリンクしない';
+$lang['js']['medialeft'] = '画像を左に寄せる';
+$lang['js']['mediaright'] = '画像を右に寄せる';
+$lang['js']['mediacenter'] = '画像を中央に寄せる';
$lang['js']['medianoalign'] = '位置を設定しない';
$lang['js']['nosmblinks'] = 'Windows の共有フォルダへリンクは Microsoft Internet Explorer でしか機能しませんが、リンクをコピーして貼り付けることは可能です。';
$lang['js']['linkwiz'] = 'リンクウィザード';
@@ -124,9 +124,9 @@ $lang['search_contains'] = '部分一致';
$lang['search_custom_match'] = 'カスタム';
$lang['search_any_ns'] = '全ての名前空間';
$lang['search_any_time'] = '全期間';
-$lang['search_past_7_days'] = 'この1週間';
-$lang['search_past_month'] = 'この1ヶ月';
-$lang['search_past_year'] = 'この1年';
+$lang['search_past_7_days'] = '1週間以内';
+$lang['search_past_month'] = '1カ月以内';
+$lang['search_past_year'] = '1年以内';
$lang['search_sort_by_hits'] = 'ヒット数順に並べる';
$lang['search_sort_by_mtime'] = '最終更新順に並べる';
$lang['regmissing'] = 'お手数ですが、全ての項目を入力してください。';
diff --git a/inc/lang/ja/locked.txt b/inc/lang/ja/locked.txt
index 89d5f7ed6..d501af456 100644
--- a/inc/lang/ja/locked.txt
+++ b/inc/lang/ja/locked.txt
@@ -1,3 +1,3 @@
====== 文書ロック中 ======
-この文書は、他のユーザーが編集中のためロックされています。編集が完了するか、ロックの期限が切れるのを待って下さい。
+この文書は、他のユーザーが編集中のためロックされています。編集が完了するか、ロックの期限が切れるまでお待ち下さい。
diff --git a/inc/lang/ja/mailtext.txt b/inc/lang/ja/mailtext.txt
index 8d1daed86..7ea2636f2 100644
--- a/inc/lang/ja/mailtext.txt
+++ b/inc/lang/ja/mailtext.txt
@@ -1,12 +1,14 @@
DokuWiki 内の文書が追加もしくは変更されました。詳細は以下の通りです。
-日付 : @DATE@
-ブラウザ : @BROWSER@
-IPアドレス : @IPADDRESS@
-ホスト名 : @HOSTNAME@
-前リビジョン: @OLDPAGE@
-新リビジョン: @NEWPAGE@
-編集の概要: @SUMMARY@
-ユーザー名 : @USER@
+ブラウザ     : @BROWSER@
+IPアドレス    : @IPADDRESS@
+ホスト名     : @HOSTNAME@
+前リビジョン   : @OLDPAGE@
+新リビジョン   : @NEWPAGE@
+新リビジョンの日付: @DATE@
+編集の概要    : @SUMMARY@
+ユーザー名    : @USER@
+
+この通知の後、新たなリビジョンが追加されている可能性があります。その場合、本リビジョンのページ上部にメッセージが表示されます。
@DIFF@
diff --git a/inc/lang/ja/preview.txt b/inc/lang/ja/preview.txt
index f9001f01c..c0fe39d86 100644
--- a/inc/lang/ja/preview.txt
+++ b/inc/lang/ja/preview.txt
@@ -1,3 +1,3 @@
====== プレビュー ======
-編集中の文書のプレビューです。確認用なので**保存されていない**ことに注意してください。
+編集中の文書のプレビューです。**まだ保存されていませんのでご注意下さい。** \ No newline at end of file
diff --git a/inc/lang/ja/registermail.txt b/inc/lang/ja/registermail.txt
index ad5241a0b..5a2484087 100644
--- a/inc/lang/ja/registermail.txt
+++ b/inc/lang/ja/registermail.txt
@@ -1,10 +1,10 @@
新しいユーザーが登録されました。ユーザー情報は以下の通りです。
-ユーザー名 : @NEWUSER@
-フルネーム : @NEWNAME@
-メールアドレス : @NEWEMAIL@
+ユーザー名  : @NEWUSER@
+フルネーム  : @NEWNAME@
+メールアドレス: @NEWEMAIL@
-登録日 : @DATE@
-ブラウザ : @BROWSER@
-IPアドレス : @IPADDRESS@
-ホスト名 : @HOSTNAME@
+登録日    : @DATE@
+ブラウザ   : @BROWSER@
+IPアドレス  : @IPADDRESS@
+ホスト名   : @HOSTNAME@
diff --git a/inc/lang/ja/resetpwd.txt b/inc/lang/ja/resetpwd.txt
index a538f18f9..b28bc3763 100644
--- a/inc/lang/ja/resetpwd.txt
+++ b/inc/lang/ja/resetpwd.txt
@@ -1,3 +1,3 @@
====== 新しいパスワードを設定 ======
-このWikiでお使いのアカウント用の新しいパスワードを入力して下さい
+このWikiでお使いのアカウント用の新しいパスワードを入力して下さい。
diff --git a/inc/lang/ja/subscr_digest.txt b/inc/lang/ja/subscr_digest.txt
index 026a2fe89..7315b7622 100644
--- a/inc/lang/ja/subscr_digest.txt
+++ b/inc/lang/ja/subscr_digest.txt
@@ -1,6 +1,6 @@
こんにちは。
-@TITLE@ 内のページ @PAGE@ は変更されました。
+@TITLE@ 内のページ @PAGE@ に変更がありました。
変更点は以下の通りです:
--------------------------------------------------------
@@ -12,6 +12,5 @@
この通知を解除するには次のウィキへログインし
@DOKUWIKIURL@
-その後、
+その後、以下のページからページと名前空間の変更に対する購読を解除してください。
@SUBSCRIBE@
-ページと名前空間の変更に対する購読を解除してください。
diff --git a/inc/lang/ja/subscr_list.txt b/inc/lang/ja/subscr_list.txt
index dbe37c7f8..3b841ea4b 100644
--- a/inc/lang/ja/subscr_list.txt
+++ b/inc/lang/ja/subscr_list.txt
@@ -1,15 +1,13 @@
こんにちは。
-@TITLE@ の 名前空間 @PAGE@ にあるページが変更されました。
+@TITLE@ の 名前空間 @PAGE@ にあるページに変更がありました。
変更点は以下の通りです:
--------------------------------------------------------
@DIFF@
--------------------------------------------------------
-
この通知を解除するには次のウィキへログインし
@DOKUWIKIURL@
-その後、
+その後、以下のページからページと名前空間の変更に対する購読を解除してください。
@SUBSCRIBE@
-ページと名前空間の変更に対する購読を解除してください。
diff --git a/inc/lang/ja/subscr_single.txt b/inc/lang/ja/subscr_single.txt
index 4dac31e02..68190bfc4 100644
--- a/inc/lang/ja/subscr_single.txt
+++ b/inc/lang/ja/subscr_single.txt
@@ -1,20 +1,19 @@
こんにちは。
-@TITLE@ のウィキにあるページ @PAGE@ が変更されました。
+@TITLE@ のウィキにあるページ @PAGE@ に変更がありました。
変更点は以下の通りです:
--------------------------------------------------------
@DIFF@
--------------------------------------------------------
-日付 : @DATE@
-ユーザー : @USER@
-変更概要: @SUMMARY@
-古いリビジョン: @OLDPAGE@
-新しいリビジョン: @NEWPAGE@
+ユーザー       : @USER@
+編集の概要      : @SUMMARY@
+古いリビジョン    : @OLDPAGE@
+新しいリビジョン   : @NEWPAGE@
+新しいリビジョンの日付: @DATE@
この通知を解除するには次のウィキへログインし
@DOKUWIKIURL@
-その後、
+その後、以下のページからページと名前空間の変更に対する購読を解除してください。
@SUBSCRIBE@
-ページと名前空間の変更に対する購読を解除してください。
diff --git a/inc/lang/ja/updateprofile.txt b/inc/lang/ja/updateprofile.txt
index f3fb01e3c..21096970b 100644
--- a/inc/lang/ja/updateprofile.txt
+++ b/inc/lang/ja/updateprofile.txt
@@ -1,3 +1,3 @@
====== アカウント情報更新 ======
-変更したい項目のみ入力して下さい。ユーザー名は変更できません。
+変更したい項目のみ書き換えて下さい。ユーザー名は変更できません。
diff --git a/inc/lang/ja/uploadmail.txt b/inc/lang/ja/uploadmail.txt
index 8734c91df..11fa1a46d 100644
--- a/inc/lang/ja/uploadmail.txt
+++ b/inc/lang/ja/uploadmail.txt
@@ -1,10 +1,11 @@
お使いのDokuWikiにファイルがアップロードされました。詳細は以下の通りです。
-ファイル : @MEDIA@
-日付 : @DATE@
-ブラウザ : @BROWSER@
-IPアドレス : @IPADDRESS@
-ホスト名 : @HOSTNAME@
-サイズ : @SIZE@
-MIMEタイプ : @MIME@
-ユーザー名 : @USER@
+ファイル : @MEDIA@
+古い版  : @OLD@
+日付   : @DATE@
+ブラウザ : @BROWSER@
+IPアドレス: @IPADDRESS@
+ホスト名 : @HOSTNAME@
+サイズ  : @SIZE@
+MIMEタイプ: @MIME@
+ユーザー名: @USER@
diff --git a/inc/lang/nl/lang.php b/inc/lang/nl/lang.php
index 2cd368639..f799318c3 100644
--- a/inc/lang/nl/lang.php
+++ b/inc/lang/nl/lang.php
@@ -3,6 +3,7 @@
/**
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*
+ * @author PBU <pbu@xs4all.nl>
* @author Gerrit Uitslag <klapinklapin@gmail.com>
* @author Andy <astolker@icloud.com>
* @author Harriet Neitz <harrietneitz@gmail.com>
diff --git a/inc/lang/no/lang.php b/inc/lang/no/lang.php
index 2cb6839fa..0b4cd8629 100644
--- a/inc/lang/no/lang.php
+++ b/inc/lang/no/lang.php
@@ -3,8 +3,8 @@
/**
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*
- * @author Rut Kristin Aanestad <dark@met.no>
* @author Torgeir Blesvik <bletor@banenor.no>
+ * @author Rut Kristin Aanestad <dark@met.no>
* @author ThorPrestboen <thor.erling.prestboen@gmail.com>
* @author Christian McKenna <mckchr@banenor.no>
* @author Reidar Mosvold <Reidar.Mosvold@hit.no>
@@ -90,6 +90,7 @@ $lang['minoredit'] = 'Mindre endringer';
$lang['draftdate'] = 'Kladd autolagret';
$lang['nosecedit'] = 'Siden er i mellomtiden endret, seksjonsinfo har blitt foreldet - lastet full side istedet.';
$lang['searchcreatepage'] = 'Hvis du ikke fant det du søkte etter kan du lage eller endre siden %s.';
+$lang['js']['search_toggle_tools'] = 'Søkeverktøy';
$lang['js']['willexpire'] = 'Din redigeringslås for dette dokumentet kommer snart til å utløpe.\nFor å unngå versjonskonflikter bør du forhåndsvise dokumentet ditt for å forlenge redigeringslåsen.';
$lang['js']['notsavedyet'] = 'Ulagrede endringer vil gå tapt!
Vil du fortsette?';
diff --git a/inc/lang/pt-br/lang.php b/inc/lang/pt-br/lang.php
index da698a90b..c88049e1b 100644
--- a/inc/lang/pt-br/lang.php
+++ b/inc/lang/pt-br/lang.php
@@ -3,6 +3,7 @@
/**
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*
+ * @author Schopf <pschopf@gmail.com>
* @author Frederico Gonçalves Guimarães <frederico@teia.bio.br>
* @author Márcio Gomes Gonçalves <gomes@metha.com.br>
* @author Luis Fernando Enciso <lfenciso@certto.com.br>
diff --git a/inc/lang/pt/lang.php b/inc/lang/pt/lang.php
index 3ac1596f2..5c01a073e 100644
--- a/inc/lang/pt/lang.php
+++ b/inc/lang/pt/lang.php
@@ -3,8 +3,8 @@
/**
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*
+ * @author Schopf <pschopf@gmail.com>
* @author Mario AlexandTeixeira dos Santos <masterofclan@gmail.com>
- * @author Paulo Ricardo Schopf <pschopf@gmail.com>
* @author Maykon Oliveira <maykonoliveira850@gmail.com>
* @author José Vieira <jmsv63@gmail.com>
* @author José Carlos Monteiro <jose.c.monteiro@netcabo.pt>
diff --git a/inc/lang/ru/lang.php b/inc/lang/ru/lang.php
index 5e6277dfa..350e7d926 100644
--- a/inc/lang/ru/lang.php
+++ b/inc/lang/ru/lang.php
@@ -4,10 +4,10 @@
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*
* @author Aleksandr Selivanov <alexgearbox@yandex.ru>
+ * @author Yuriy Skalko <yuriy.skalko@gmail.com>
* @author Alexander Kh. <001.arx@gmail.com>
* @author Vyacheslav Strenadko <vyacheslav.strenadko@gmail.com>
* @author Wolterhon <hotmottot.1@gmail.com>
- * @author Yuriy Skalko <yuriy.skalko@gmail.com>
* @author Zhassulan <zyesmukanov@gmail.com>
* @author Yuri Pimenov <up@ftpsearch.lv>
* @author Igor Tarasov <tigr@mail15.com>
@@ -214,7 +214,7 @@ $lang['accessdenied'] = 'Вы не можете просмотреть
$lang['mediausage'] = 'Для ссылки на этот файл используйте следующий синтаксис:';
$lang['mediaview'] = 'Посмотреть исходный файл';
$lang['mediaroot'] = 'корень';
-$lang['mediaupload'] = 'Загрузка файла в текущее пространство имён. Для создания подпространства имён, добавьте его название перед именем файла через двоеточие. Поддерживается drag-and-drop.';
+$lang['mediaupload'] = 'Загрузка файла в текущее пространство имён. Для создания подпространства имён добавьте его название перед именем файла через двоеточие. Поддерживается drag-and-drop (перетащить-и-оставить).';
$lang['mediaextchange'] = 'Расширение изменилось с .%s на .%s!';
$lang['reference'] = 'Ссылки для';
$lang['ref_inuse'] = 'Этот файл не может быть удалён, так как он используется на следующих страницах:';
@@ -257,7 +257,7 @@ $lang['mail_newpage'] = 'страница добавлена:';
$lang['mail_changed'] = 'страница изменена:';
$lang['mail_subscribe_list'] = 'изменились страницы в пространстве имён:';
$lang['mail_new_user'] = 'новый пользователь:';
-$lang['mail_upload'] = 'файл закачан:';
+$lang['mail_upload'] = 'файл загружен:';
$lang['changes_type'] = 'Посмотреть изменения';
$lang['pages_changes'] = 'страниц';
$lang['media_changes'] = 'медиафайлов';
diff --git a/inc/lang/ru/uploadmail.txt b/inc/lang/ru/uploadmail.txt
index a92d85564..7c342a702 100644
--- a/inc/lang/ru/uploadmail.txt
+++ b/inc/lang/ru/uploadmail.txt
@@ -1,4 +1,4 @@
-В вашу вики был закачан файл. Подробная информация:
+В вашу вики был загружен файл. Подробная информация:
Файл: @MEDIA@
Старая версия: @OLD@
diff --git a/inc/lang/sk/lang.php b/inc/lang/sk/lang.php
index b15b94133..83c160b60 100644
--- a/inc/lang/sk/lang.php
+++ b/inc/lang/sk/lang.php
@@ -8,6 +8,7 @@
* auth.class language support
* installer strings
*
+ * @author Peter Mydliar <peto.mydliar@gmail.com>
* @author Martin Michalek <michalek.dev@gmail.com>
* @author Ondrej Vegh <ov@vsieti.sk> with help of the scholars from Zdruzena stredna skola polygraficka in Bratislava
* @author Michal Mesko <michal.mesko@gmail.com>
diff --git a/inc/lang/sr/admin.txt b/inc/lang/sr/admin.txt
index 8798fd4a6..284410b4b 100644
--- a/inc/lang/sr/admin.txt
+++ b/inc/lang/sr/admin.txt
@@ -1,3 +1,3 @@
====== Администрација ======
-Изпод се налази листа доступних администраторских опција у DokuWiki-ју.
+Испод се налази листа доступних администраторских опција у DokuWiki-ју.
diff --git a/inc/mail.php b/inc/mail.php
index 12a669dbe..429976d72 100644
--- a/inc/mail.php
+++ b/inc/mail.php
@@ -308,7 +308,7 @@ function mail_quotedprintable_encode($sText,$maxlen=74,$bEmulate_imap_8bit=true)
// encode x09,x20 at lineends
{
$iLength = strlen($sLine);
- $iLastChar = ord($sLine{$iLength-1});
+ $iLastChar = ord($sLine[$iLength-1]);
// !!!!!!!!
// imap_8_bit does not encode x20 at the very end of a text,
@@ -317,7 +317,7 @@ function mail_quotedprintable_encode($sText,$maxlen=74,$bEmulate_imap_8bit=true)
// or comment next line for RFC2045 conformance, if you like
if (!($bEmulate_imap_8bit && ($i==count($aLines)-1))){
if (($iLastChar==0x09)||($iLastChar==0x20)) {
- $sLine{$iLength-1}='=';
+ $sLine[$iLength-1]='=';
$sLine .= ($iLastChar==0x09)?'09':'20';
}
}
diff --git a/inc/media.php b/inc/media.php
index 41fbc0b8f..cc29bd16c 100644
--- a/inc/media.php
+++ b/inc/media.php
@@ -544,7 +544,7 @@ function media_upload_finish($fn_tmp, $fn, $id, $imime, $overwrite, $move = 'mov
// (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.)
chmod($fn, $conf['fmode']);
msg($lang['uploadsucc'],1);
- media_notify($id,$fn,$imime,$old);
+ media_notify($id,$fn,$imime,$old,$new);
// add a log entry to the media changelog
$filesize_new = filesize($fn);
$sizechange = $filesize_new - $filesize_old;
@@ -672,12 +672,12 @@ function media_contentcheck($file,$mime){
* @param bool|int $old_rev revision timestamp or false
* @return bool
*/
-function media_notify($id,$file,$mime,$old_rev=false){
+function media_notify($id,$file,$mime,$old_rev=false,$current_rev=false){
global $conf;
if(empty($conf['notify'])) return false; //notify enabled?
$subscription = new MediaSubscriptionSender();
- return $subscription->sendMediaDiff($conf['notify'], 'uploadmail', $id, $old_rev);
+ return $subscription->sendMediaDiff($conf['notify'], 'uploadmail', $id, $old_rev, $current_rev);
}
/**
@@ -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/pageutils.php b/inc/pageutils.php
index bc39a6fef..94d8b825a 100644
--- a/inc/pageutils.php
+++ b/inc/pageutils.php
@@ -464,7 +464,7 @@ function resolve_id($ns,$id,$clean=true){
// if the id starts with a dot we need to handle the
// relative stuff
- if($id && $id{0} == '.'){
+ if($id && $id[0] == '.'){
// normalize initial dots without a colon
$id = preg_replace('/^(\.+)(?=[^:\.])/','\1:',$id);
// prepend the current namespace
@@ -619,7 +619,7 @@ function resolve_pageid($ns,&$page,&$exists,$rev='',$date_at=false ){
function getCacheName($data,$ext=''){
global $conf;
$md5 = md5($data);
- $file = $conf['cachedir'].'/'.$md5{0}.'/'.$md5.$ext;
+ $file = $conf['cachedir'].'/'.$md5[0].'/'.$md5.$ext;
io_makeFileDir($file);
return $file;
}
diff --git a/inc/parser/handler.php b/inc/parser/handler.php
index 19c2aafe8..e5f7c4ed5 100644
--- a/inc/parser/handler.php
+++ b/inc/parser/handler.php
@@ -44,11 +44,17 @@ class Doku_Handler {
* @param mixed $args arguments for this call
* @param int $pos byte position in the original source file
*/
- protected function addCall($handler, $args, $pos) {
+ public function addCall($handler, $args, $pos) {
$call = array($handler,$args, $pos);
$this->callWriter->writeCall($call);
}
+ /** @deprecated 2019-10-31 use addCall() instead */
+ public function _addCall($handler, $args, $pos) {
+ dbg_deprecated('addCall');
+ $this->addCall($handler, $args, $pos);
+ }
+
/**
* Similar to addCall, but adds a plugin call
*
@@ -58,7 +64,7 @@ class Doku_Handler {
* @param int $pos byte position in the original source file
* @param string $match matched syntax
*/
- protected function addPluginCall($plugin, $args, $state, $pos, $match) {
+ public function addPluginCall($plugin, $args, $state, $pos, $match) {
$call = array('plugin',array($plugin, $args, $state, $match), $pos);
$this->callWriter->writeCall($call);
}
diff --git a/inc/parser/metadata.php b/inc/parser/metadata.php
index fa64ae2ec..849fffe8d 100644
--- a/inc/parser/metadata.php
+++ b/inc/parser/metadata.php
@@ -64,7 +64,7 @@ class Doku_Renderer_metadata extends Doku_Renderer
$this->headers = array();
// external pages are missing create date
- if (!$this->persistent['date']['created']) {
+ if (!isset($this->persistent['date']['created']) || !$this->persistent['date']['created']) {
$this->persistent['date']['created'] = filectime(wikiFN($ID));
}
if (!isset($this->persistent['user'])) {
diff --git a/inc/parser/parser.php b/inc/parser/parser.php
index d9fc5fb8f..aee82f01d 100644
--- a/inc/parser/parser.php
+++ b/inc/parser/parser.php
@@ -1,5 +1,7 @@
<?php
+use dokuwiki\Debug\PropertyDeprecationHelper;
+
/**
* Define various types of modes used by the parser - they are used to
* populate the list of modes another mode accepts
@@ -48,10 +50,50 @@ $PARSER_MODES = array(
* @deprecated 2018-05-04
*/
class Doku_Parser extends \dokuwiki\Parsing\Parser {
+ use PropertyDeprecationHelper {
+ __set as protected deprecationHelperMagicSet;
+ __get as protected deprecationHelperMagicGet;
+ }
/** @inheritdoc */
- public function __construct(Doku_Handler $handler) {
+ public function __construct(Doku_Handler $handler = null) {
dbg_deprecated(\dokuwiki\Parsing\Parser::class);
+ $this->deprecatePublicProperty('modes', __CLASS__);
+ $this->deprecatePublicProperty('connected', __CLASS__);
+
+ if ($handler === null) {
+ $handler = new Doku_Handler();
+ }
+
parent::__construct($handler);
}
+
+ public function __set($name, $value)
+ {
+
+ if ($name === 'Handler') {
+ $this->handler = $value;
+ return;
+ }
+
+ if ($name === 'Lexer') {
+ $this->lexer = $value;
+ return;
+ }
+
+ $this->deprecationHelperMagicSet($name, $value);
+ }
+
+ public function __get($name)
+ {
+ if ($name === 'Handler') {
+ return $this->handler;
+ }
+
+ if ($name === 'Lexer') {
+ return $this->lexer;
+ }
+
+ return $this->deprecationHelperMagicGet($name);
+ }
}
diff --git a/inc/parser/renderer.php b/inc/parser/renderer.php
index 9720cfd90..a03b84c8e 100644
--- a/inc/parser/renderer.php
+++ b/inc/parser/renderer.php
@@ -866,7 +866,7 @@ abstract class Doku_Renderer extends Plugin {
//use placeholders
$url = str_replace('{URL}', rawurlencode($reference), $url);
//wiki names will be cleaned next, otherwise urlencode unsafe chars
- $url = str_replace('{NAME}', ($url{0} === ':') ? $reference :
+ $url = str_replace('{NAME}', ($url[0] === ':') ? $reference :
preg_replace_callback('/[[\\\\\]^`{|}#%]/', function($match) {
return rawurlencode($match[0]);
}, $reference), $url);
@@ -889,7 +889,7 @@ abstract class Doku_Renderer extends Plugin {
$url = $url.rawurlencode($reference);
}
//handle as wiki links
- if($url{0} === ':') {
+ if($url[0] === ':') {
$urlparam = null;
$id = $url;
if (strpos($url, '?') !== false) {
diff --git a/inc/parser/xhtml.php b/inc/parser/xhtml.php
index b1931d6f0..169f4f9f4 100644
--- a/inc/parser/xhtml.php
+++ b/inc/parser/xhtml.php
@@ -680,7 +680,7 @@ class Doku_Renderer_xhtml extends Doku_Renderer {
$this->doc .= '</a></dt>'.DOKU_LF.'<dd>';
}
- if($text{0} == "\n") {
+ if($text[0] == "\n") {
$text = substr($text, 1);
}
if(substr($text, -1) == "\n") {
@@ -917,7 +917,7 @@ class Doku_Renderer_xhtml extends Doku_Renderer {
$link['pre'] = '';
$link['suf'] = '';
// highlight link to current page
- if($id == $INFO['id']) {
+ if(isset($INFO) && $id == $INFO['id']) {
$link['pre'] = '<span class="curid">';
$link['suf'] = '</span>';
}
diff --git a/inc/pluginutils.php b/inc/pluginutils.php
index f1ad82fe6..0514d1c6a 100644
--- a/inc/pluginutils.php
+++ b/inc/pluginutils.php
@@ -27,10 +27,13 @@ if(!defined('DOKU_PLUGIN_NAME_REGEX')) define('DOKU_PLUGIN_NAME_REGEX', '[a-zA-Z
* @param bool $all; true to retrieve all, false to retrieve only enabled plugins
* @return array with plugin names or plugin component names
*/
-function plugin_list($type='',$all=false) {
+function plugin_list($type='',$all=false)
+{
/** @var $plugin_controller PluginController */
global $plugin_controller;
- return $plugin_controller->getList($type,$all);
+ $plugins = $plugin_controller->getList($type,$all);
+ sort($plugins, SORT_NATURAL|SORT_FLAG_CASE);
+ return $plugins;
}
/**
@@ -44,7 +47,8 @@ function plugin_list($type='',$all=false) {
* @param $disabled bool true to load even disabled plugins
* @return PluginInterface|null the plugin object or null on failure
*/
-function plugin_load($type,$name,$new=false,$disabled=false) {
+function plugin_load($type,$name,$new=false,$disabled=false)
+{
/** @var $plugin_controller PluginController */
global $plugin_controller;
return $plugin_controller->load($type,$name,$new,$disabled);
@@ -56,7 +60,8 @@ function plugin_load($type,$name,$new=false,$disabled=false) {
* @param string $plugin name of plugin
* @return bool true disabled, false enabled
*/
-function plugin_isdisabled($plugin) {
+function plugin_isdisabled($plugin)
+{
/** @var $plugin_controller PluginController */
global $plugin_controller;
return $plugin_controller->isdisabled($plugin);
@@ -68,7 +73,8 @@ function plugin_isdisabled($plugin) {
* @param string $plugin name of plugin
* @return bool true saving succeed, false saving failed
*/
-function plugin_enable($plugin) {
+function plugin_enable($plugin)
+{
/** @var $plugin_controller PluginController */
global $plugin_controller;
return $plugin_controller->enable($plugin);
@@ -80,7 +86,8 @@ function plugin_enable($plugin) {
* @param string $plugin name of plugin
* @return bool true saving succeed, false saving failed
*/
-function plugin_disable($plugin) {
+function plugin_disable($plugin)
+{
/** @var $plugin_controller PluginController */
global $plugin_controller;
return $plugin_controller->disable($plugin);
@@ -93,7 +100,8 @@ function plugin_disable($plugin) {
* @return string name of directory
* @deprecated 2018-07-20
*/
-function plugin_directory($plugin) {
+function plugin_directory($plugin)
+{
dbg_deprecated('$plugin directly');
return $plugin;
}
@@ -103,7 +111,8 @@ function plugin_directory($plugin) {
*
* @return array with arrays of plugin configs
*/
-function plugin_getcascade() {
+function plugin_getcascade()
+{
/** @var $plugin_controller PluginController */
global $plugin_controller;
return $plugin_controller->getCascade();
@@ -116,7 +125,8 @@ function plugin_getcascade() {
*
* @return Doku_Plugin_Admin
*/
-function plugin_getRequestAdminPlugin(){
+function plugin_getRequestAdminPlugin()
+{
static $admin_plugin = false;
global $ACT,$INPUT,$INFO;
diff --git a/inc/template.php b/inc/template.php
index caec0b87b..798fea52e 100644
--- a/inc/template.php
+++ b/inc/template.php
@@ -263,7 +263,7 @@ function tpl_metaheaders($alt = true) {
$head['link'][] = array(
'rel' => 'alternate', 'type'=> 'application/rss+xml',
'title'=> $lang['currentns'],
- 'href' => DOKU_BASE.'feed.php?mode=list&ns='.$INFO['namespace']
+ 'href' => DOKU_BASE.'feed.php?mode=list&ns='.(isset($INFO) ? $INFO['namespace'] : '')
);
}
if(($ACT == 'show' || $ACT == 'search') && $INFO['writable']) {
@@ -332,31 +332,33 @@ function tpl_metaheaders($alt = true) {
// load stylesheets
$head['link'][] = array(
- 'rel' => 'stylesheet', 'type'=> 'text/css',
+ 'rel' => 'stylesheet',
'href'=> DOKU_BASE.'lib/exe/css.php?t='.rawurlencode($conf['template']).'&tseed='.$tseed
);
- $script = "var NS='".$INFO['namespace']."';";
+ $script = "var NS='".(isset($INFO)?$INFO['namespace']:'')."';";
if($conf['useacl'] && $INPUT->server->str('REMOTE_USER')) {
$script .= "var SIG='".toolbar_signature()."';";
}
jsinfo();
$script .= 'var JSINFO = ' . json_encode($JSINFO).';';
- $head['script'][] = array('type'=> 'text/javascript', '_data'=> $script);
+ $head['script'][] = array('_data'=> $script);
// load jquery
$jquery = getCdnUrls();
foreach($jquery as $src) {
$head['script'][] = array(
- 'type' => 'text/javascript', 'charset' => 'utf-8', '_data' => '', 'src' => $src
- );
+ 'charset' => 'utf-8',
+ '_data' => '',
+ 'src' => $src,
+ ) + ($conf['defer_js'] ? [ 'defer' => 'defer'] : []);
}
// load our javascript dispatcher
$head['script'][] = array(
- 'type'=> 'text/javascript', 'charset'=> 'utf-8', '_data'=> '',
- 'src' => DOKU_BASE.'lib/exe/js.php'.'?t='.rawurlencode($conf['template']).'&tseed='.$tseed
- );
+ 'charset'=> 'utf-8', '_data'=> '',
+ 'src' => DOKU_BASE.'lib/exe/js.php'.'?t='.rawurlencode($conf['template']).'&tseed='.$tseed,
+ ) + ($conf['defer_js'] ? [ 'defer' => 'defer'] : []);
// trigger event here
Event::createAndTrigger('TPL_METAHEADER_OUTPUT', $head, '_tpl_metaheaders_action', true);
@@ -957,6 +959,7 @@ function tpl_pagetitle($id = null, $ret = false) {
// page functions
case 'edit' :
+ case 'preview' :
$page_title = "✎ ".$name;
break;
@@ -1845,7 +1848,7 @@ function tpl_classes() {
'mode_'.$ACT,
'tpl_'.$conf['template'],
$INPUT->server->bool('REMOTE_USER') ? 'loggedIn' : '',
- $INFO['exists'] ? '' : 'notFound',
+ (isset($INFO) && $INFO['exists']) ? '' : 'notFound',
($ID == $conf['start']) ? 'home' : '',
);
return join(' ', $classes);