summaryrefslogtreecommitdiff
path: root/inc
diff options
context:
space:
mode:
Diffstat (limited to 'inc')
-rw-r--r--inc/PassHash.class.php68
-rw-r--r--inc/RemoteAPICore.php8
-rw-r--r--inc/Tar.class.php44
-rw-r--r--inc/actions.php22
-rw-r--r--inc/auth.php14
-rw-r--r--inc/changelog.php1
-rw-r--r--inc/common.php85
-rw-r--r--inc/html.php3
-rw-r--r--inc/indexer.php123
-rw-r--r--inc/lang/ar/subscr_single.txt2
-rw-r--r--inc/lang/bg/subscr_single.txt2
-rw-r--r--inc/lang/ca/lang.php129
-rw-r--r--inc/lang/ca/mailwrap.html13
-rw-r--r--inc/lang/ca/resetpwd.txt3
-rw-r--r--inc/lang/ca/subscr_digest.txt21
-rw-r--r--inc/lang/ca/subscr_form.txt3
-rw-r--r--inc/lang/ca/subscr_list.txt21
-rw-r--r--inc/lang/da/subscr_single.txt2
-rw-r--r--inc/lang/de-informal/subscr_single.txt2
-rw-r--r--inc/lang/de/subscr_single.txt2
-rw-r--r--inc/lang/en/subscr_single.txt2
-rw-r--r--inc/lang/eo/subscr_single.txt2
-rw-r--r--inc/lang/he/subscr_single.txt2
-rw-r--r--inc/lang/ia/subscr_single.txt2
-rw-r--r--inc/lang/it/subscr_single.txt2
-rw-r--r--inc/lang/ko/subscr_single.txt2
-rw-r--r--inc/lang/la/subscr_digest.txt2
-rw-r--r--inc/lang/la/subscr_single.txt2
-rw-r--r--inc/lang/no/subscr_digest.txt2
-rw-r--r--inc/lang/no/subscr_list.txt2
-rw-r--r--inc/lang/no/subscr_single.txt2
-rw-r--r--inc/lang/pt/subscr_single.txt2
-rw-r--r--inc/lang/sq/subscr_single.txt2
-rw-r--r--inc/lang/tr/lang.php55
-rw-r--r--inc/lang/tr/resetpwd.txt3
-rw-r--r--inc/lang/zh-tw/adminplugins.txt2
-rw-r--r--inc/lang/zh-tw/backlinks.txt2
-rw-r--r--inc/lang/zh-tw/diff.txt2
-rw-r--r--inc/lang/zh-tw/edit.txt2
-rw-r--r--inc/lang/zh-tw/editrev.txt2
-rw-r--r--inc/lang/zh-tw/index.txt2
-rw-r--r--inc/lang/zh-tw/install.html6
-rw-r--r--inc/lang/zh-tw/lang.php101
-rw-r--r--inc/lang/zh-tw/locked.txt2
-rw-r--r--inc/lang/zh-tw/login.txt3
-rw-r--r--inc/lang/zh-tw/mailtext.txt2
-rw-r--r--inc/lang/zh-tw/norev.txt2
-rw-r--r--inc/lang/zh-tw/preview.txt2
-rw-r--r--inc/lang/zh-tw/pwconfirm.txt8
-rw-r--r--inc/lang/zh-tw/read.txt2
-rw-r--r--inc/lang/zh-tw/register.txt3
-rw-r--r--inc/lang/zh-tw/registermail.txt4
-rw-r--r--inc/lang/zh-tw/resendpwd.txt2
-rw-r--r--inc/lang/zh-tw/revisions.txt2
-rw-r--r--inc/lang/zh-tw/stopwords.txt10
-rw-r--r--inc/lang/zh-tw/subscr_digest.txt6
-rw-r--r--inc/lang/zh-tw/subscr_form.txt2
-rw-r--r--inc/lang/zh-tw/subscr_list.txt6
-rw-r--r--inc/lang/zh-tw/subscr_single.txt6
-rw-r--r--inc/lang/zh-tw/updateprofile.txt2
-rw-r--r--inc/lang/zh-tw/uploadmail.txt4
-rw-r--r--inc/load.php1
-rw-r--r--inc/media.php23
-rw-r--r--inc/parser/xhtml.php1
-rw-r--r--inc/plugin.php4
-rw-r--r--inc/search.php11
-rw-r--r--inc/subscription.php993
67 files changed, 1227 insertions, 645 deletions
diff --git a/inc/PassHash.class.php b/inc/PassHash.class.php
index 13be479cc..080fb4778 100644
--- a/inc/PassHash.class.php
+++ b/inc/PassHash.class.php
@@ -4,7 +4,7 @@
*
* This class implements various mechanisms used to hash passwords
*
- * @author Andreas Gohr <andi@splitbrain.org>
+ * @author Andreas Gohr <andi@splitbrain.org>
* @license LGPL2
*/
class PassHash {
@@ -58,6 +58,12 @@ class PassHash {
} elseif(substr($hash, 0, 6) == '{SMD5}') {
$method = 'lsmd5';
$salt = substr(base64_decode(substr($hash, 6)), 16);
+ } elseif(preg_match('/^:B:(.+?):.{32}$/', $hash, $m)) {
+ $method = 'mediawiki';
+ $salt = $m[1];
+ } elseif(preg_match('/^\$6\$(.+?)\$/', $hash, $m)) {
+ $method = 'sha512';
+ $salt = $m[1];
} elseif($len == 32) {
$method = 'md5';
} elseif($len == 40) {
@@ -101,14 +107,18 @@ class PassHash {
* Initialize the passed variable with a salt if needed.
*
* If $salt is not null, the value is kept, but the lenght restriction is
- * applied.
+ * applied (unless, $cut is false).
*
* @param string &$salt The salt, pass null if you want one generated
- * @param int $len The length of the salt
+ * @param int $len The length of the salt
+ * @param bool $cut Apply length restriction to existing salt?
*/
- public function init_salt(&$salt, $len = 32) {
- if(is_null($salt)) $salt = $this->gen_salt($len);
- if(strlen($salt) > $len) $salt = substr($salt, 0, $len);
+ public function init_salt(&$salt, $len = 32, $cut = true) {
+ if(is_null($salt)) {
+ $salt = $this->gen_salt($len);
+ $cut = true; // for new hashes we alway apply length restriction
+ }
+ if(strlen($salt) > $len && $cut) $salt = substr($salt, 0, $len);
}
// Password hashing methods follow below
@@ -263,7 +273,7 @@ class PassHash {
*
* This method was used by old MySQL systems
*
- * @link http://www.php.net/mysql
+ * @link http://www.php.net/mysql
* @author <soren at byu dot edu>
* @param string $clear The clear text to hash
* @return string Hashed password
@@ -327,9 +337,9 @@ class PassHash {
* an exception.
*
* @link http://www.openwall.com/phpass/
- * @param string $clear The clear text to hash
- * @param string $salt The salt to use, null for random
- * @param string $magic The hash identifier (P or H)
+ * @param string $clear The clear text to hash
+ * @param string $salt The salt to use, null for random
+ * @param string $magic The hash identifier (P or H)
* @param int $compute The iteration count for new passwords
* @throws Exception
* @return string Hashed password
@@ -430,8 +440,8 @@ class PassHash {
* will break. When no salt is given, the iteration count can be set
* through the $compute variable.
*
- * @param string $clear The clear text to hash
- * @param string $salt The salt to use, null for random
+ * @param string $clear The clear text to hash
+ * @param string $salt The salt to use, null for random
* @param int $compute The iteration count (between 4 and 31)
* @throws Exception
* @return string Hashed password
@@ -450,4 +460,38 @@ class PassHash {
return crypt($clear, $salt);
}
+ /**
+ * Password hashing method SHA512
+ *
+ * This is only supported on PHP 5.3.2 or higher and will throw an exception if
+ * the needed crypt support is not available
+ *
+ * @param string $clear The clear text to hash
+ * @param string $salt The salt to use, null for random
+ * @return string Hashed password
+ * @throws Exception
+ */
+ public function hash_sha512($clear, $salt = null) {
+ if(!defined('CRYPT_SHA512') || CRYPT_SHA512 != 1) {
+ throw new Exception('This PHP installation has no SHA512 support');
+ }
+ $this->init_salt($salt, 8, false);
+ return crypt($clear, '$6$'.$salt.'$');
+ }
+
+ /**
+ * Password hashing method 'mediawiki'
+ *
+ * Uses salted MD5, this is referred to as Method B in MediaWiki docs. Unsalted md5
+ * method 'A' is not supported.
+ *
+ * @link http://www.mediawiki.org/wiki/Manual_talk:User_table#user_password_column
+ * @param string $clear The clear text to hash
+ * @param string $salt The salt to use, null for random
+ * @return string Hashed password
+ */
+ public function hash_mediawiki($clear, $salt = null) {
+ $this->init_salt($salt, 8, false);
+ return ':B:'.$salt.':'.md5($salt.'-'.md5($clear));
+ }
}
diff --git a/inc/RemoteAPICore.php b/inc/RemoteAPICore.php
index c04a14f5c..81b211ec8 100644
--- a/inc/RemoteAPICore.php
+++ b/inc/RemoteAPICore.php
@@ -3,7 +3,7 @@
/**
* Increased whenever the API is changed
*/
-define('DOKU_API_VERSION', 7);
+define('DOKU_API_VERSION', 8);
class RemoteAPICore {
@@ -344,6 +344,8 @@ class RemoteAPICore {
for($i=0; $i<$len; $i++) {
unset($data[$i]['meta']);
+ $data[$i]['perms'] = $data[$i]['perm'];
+ unset($data[$i]['perm']);
$data[$i]['lastModified'] = $this->api->toDate($data[$i]['mtime']);
}
return $data;
@@ -654,7 +656,9 @@ class RemoteAPICore {
if(count($revisions)>0 && $first==0) {
array_unshift($revisions, ''); // include current revision
- array_pop($revisions); // remove extra log entry
+ if ( count($revisions) > $conf['recent'] ){
+ array_pop($revisions); // remove extra log entry
+ }
}
if(count($revisions) > $conf['recent']) {
diff --git a/inc/Tar.class.php b/inc/Tar.class.php
index 59e14c705..20f397395 100644
--- a/inc/Tar.class.php
+++ b/inc/Tar.class.php
@@ -17,7 +17,7 @@
*
* $tar = new Tar();
* $tar->open('myfile.tgz');
- * $tar->extract(/tmp);
+ * $tar->extract('/tmp');
*
* To create a new TAR archive directly on the filesystem (low memory requirements), create() it,
* add*() files and close() it:
@@ -81,7 +81,7 @@ class Tar {
$this->fh = @fopen($this->file, 'rb');
}
- if(!$this->fh) throw(new TarIOException('Could not open file for reading: '.$this->file));
+ if(!$this->fh) throw new TarIOException('Could not open file for reading: '.$this->file);
$this->closed = false;
}
@@ -107,9 +107,9 @@ class Tar {
* Reopen the file with open() again if you want to do additional operations
*/
public function contents() {
- if($this->closed || !$this->file) throw(new TarIOException('Can not read from a closed archive'));
+ if($this->closed || !$this->file) throw new TarIOException('Can not read from a closed archive');
- $result = Array();
+ $result = array();
while($read = $this->readbytes(512)) {
$header = $this->parseHeader($read);
if(!is_array($header)) continue;
@@ -148,7 +148,7 @@ class Tar {
* @return array
*/
function extract($outdir, $strip = '', $exclude = '', $include = '') {
- if($this->closed || !$this->file) throw(new TarIOException('Can not read from a closed archive'));
+ if($this->closed || !$this->file) throw new TarIOException('Can not read from a closed archive');
$outdir = rtrim($outdir, '/');
io_mkdir_p($outdir);
@@ -207,7 +207,7 @@ class Tar {
// is this a file?
if(!$header['typeflag']) {
$fp = fopen($output, "wb");
- if(!$fp) throw(new TarIOException('Could not open file for writing: '.$output));
+ if(!$fp) throw new TarIOException('Could not open file for writing: '.$output);
$size = floor($header['size'] / 512);
for($i = 0; $i < $size; $i++) {
@@ -260,7 +260,7 @@ class Tar {
$this->fh = @fopen($this->file, 'wb');
}
- if(!$this->fh) throw(new TarIOException('Could not open file for writing: '.$this->file));
+ if(!$this->fh) throw new TarIOException('Could not open file for writing: '.$this->file);
}
$this->writeaccess = false;
$this->closed = false;
@@ -275,13 +275,13 @@ class Tar {
* @throws TarIOException
*/
public function addFile($file, $name = '') {
- if($this->closed) throw(new TarIOException('Archive has been closed, files can no longer be added'));
+ if($this->closed) throw new TarIOException('Archive has been closed, files can no longer be added');
if(!$name) $name = $file;
$name = $this->cleanPath($name);
$fp = fopen($file, 'rb');
- if(!$fp) throw(new TarIOException('Could not open file for reading: '.$file));
+ if(!$fp) throw new TarIOException('Could not open file for reading: '.$file);
// create file header and copy all stat info from the original file
clearstatcache(false, $file);
@@ -314,7 +314,7 @@ class Tar {
* @throws TarIOException
*/
public function addData($name, $data, $uid = 0, $gid = 0, $perm = 0666, $mtime = 0) {
- if($this->closed) throw(new TarIOException('Archive has been closed, files can no longer be added'));
+ if($this->closed) throw new TarIOException('Archive has been closed, files can no longer be added');
$name = $this->cleanPath($name);
$len = strlen($data);
@@ -401,7 +401,7 @@ class Tar {
if($comptype === Tar::COMPRESS_AUTO) $comptype = $this->filetype($file);
if(!file_put_contents($file, $this->getArchive($comptype, $complevel))) {
- throw(new TarIOException('Could not write to file: '.$file));
+ throw new TarIOException('Could not write to file: '.$file);
}
}
@@ -439,14 +439,14 @@ class Tar {
} else {
$written = @fwrite($this->fh, $data);
}
- if($written === false) throw(new TarIOException('Failed to write to archive stream'));
+ if($written === false) throw new TarIOException('Failed to write to archive stream');
return $written;
}
/**
* Skip forward in the open file pointer
*
- * This is basically a wrapper around seek() (and a workarounf for bzip2)
+ * This is basically a wrapper around seek() (and a workaround for bzip2)
*
* @param int $bytes seek to this position
*/
@@ -494,11 +494,11 @@ class Tar {
}
// values are needed in octal
- $uid = sprintf("%6s ", DecOct($uid));
- $gid = sprintf("%6s ", DecOct($gid));
- $perm = sprintf("%6s ", DecOct($perm));
- $size = sprintf("%11s ", DecOct($size));
- $mtime = sprintf("%11s", DecOct($mtime));
+ $uid = sprintf("%6s ", decoct($uid));
+ $gid = sprintf("%6s ", decoct($gid));
+ $perm = sprintf("%6s ", decoct($perm));
+ $size = sprintf("%11s ", decoct($size));
+ $mtime = sprintf("%11s", decoct($mtime));
$data_first = pack("a100a8a8a8a12A12", $name, $perm, $uid, $gid, $size, $mtime);
$data_last = pack("a1a100a6a2a32a32a8a8a155a12", $typeflag, '', 'ustar', '', '', '', '', '', $prefix, "");
@@ -511,7 +511,7 @@ class Tar {
$this->writebytes($data_first);
- $chks = pack("a8", sprintf("%6s ", DecOct($chks)));
+ $chks = pack("a8", sprintf("%6s ", decoct($chks)));
$this->writebytes($chks.$data_last);
}
@@ -598,11 +598,11 @@ class Tar {
*/
protected function compressioncheck($comptype) {
if($comptype === Tar::COMPRESS_GZIP && !function_exists('gzopen')) {
- throw(new TarIllegalCompressionException('No gzip support available'));
+ throw new TarIllegalCompressionException('No gzip support available');
}
if($comptype === Tar::COMPRESS_BZIP && !function_exists('bzopen')) {
- throw(new TarIllegalCompressionException('No bzip2 support available'));
+ throw new TarIllegalCompressionException('No bzip2 support available');
}
}
@@ -631,4 +631,4 @@ class TarIOException extends Exception {
}
class TarIllegalCompressionException extends Exception {
-} \ No newline at end of file
+}
diff --git a/inc/actions.php b/inc/actions.php
index f65b47451..4083b0454 100644
--- a/inc/actions.php
+++ b/inc/actions.php
@@ -711,21 +711,28 @@ function act_subscription($act){
$target = $params['target'];
$style = $params['style'];
- $data = $params['data'];
$action = $params['action'];
// Perform action.
- if (!subscription_set($_SERVER['REMOTE_USER'], $target, $style, $data)) {
+ $sub = new Subscription();
+ if($action == 'unsubscribe'){
+ $ok = $sub->remove($target, $_SERVER['REMOTE_USER'], $style);
+ }else{
+ $ok = $sub->add($target, $_SERVER['REMOTE_USER'], $style);
+ }
+
+ if($ok) {
+ msg(sprintf($lang["subscr_{$action}_success"], hsc($INFO['userinfo']['name']),
+ prettyprint_id($target)), 1);
+ act_redirect($ID, $act);
+ } else {
throw new Exception(sprintf($lang["subscr_{$action}_error"],
hsc($INFO['userinfo']['name']),
prettyprint_id($target)));
}
- msg(sprintf($lang["subscr_{$action}_success"], hsc($INFO['userinfo']['name']),
- prettyprint_id($target)), 1);
- act_redirect($ID, $act);
// Assure that we have valid data if act_redirect somehow fails.
- $INFO['subscribed'] = get_info_subscribed();
+ $INFO['subscribed'] = $sub->user_subscription();
return 'show';
}
@@ -777,8 +784,7 @@ function subscription_handle_post(&$params) {
$style = null;
}
- $data = in_array($style, array('list', 'digest')) ? time() : null;
- $params = compact('target', 'style', 'data', 'action');
+ $params = compact('target', 'style', 'action');
}
//Setup VIM: ex: et ts=2 :
diff --git a/inc/auth.php b/inc/auth.php
index c68a699fe..c4f1dcf2b 100644
--- a/inc/auth.php
+++ b/inc/auth.php
@@ -780,23 +780,19 @@ function register() {
return false;
}
- // create substitutions for use in notification email
- $substitutions = array(
- 'NEWUSER' => $login,
- 'NEWNAME' => $fullname,
- 'NEWEMAIL' => $email,
- );
+ // send notification about the new user
+ $subscription = new Subscription();
+ $subscription->send_register($login, $fullname, $email);
+ // are we done?
if(!$conf['autopasswd']) {
msg($lang['regsuccess2'], 1);
- notify('', 'register', '', $login, false, $substitutions);
return true;
}
- // autogenerated password? then send him the password
+ // autogenerated password? then send password to user
if(auth_sendPassword($login, $pass)) {
msg($lang['regsuccess'], 1);
- notify('', 'register', '', $login, false, $substitutions);
return true;
} else {
msg($lang['regmailfail'], -1);
diff --git a/inc/changelog.php b/inc/changelog.php
index 688aebfd6..9768fea51 100644
--- a/inc/changelog.php
+++ b/inc/changelog.php
@@ -258,6 +258,7 @@ function getRecentsSince($from,$to=null,$ns='',$flags=0){
} else {
$lines = @file($conf['changelog']);
}
+ if(!$lines) return $recent;
// we start searching at the end of the list
$lines = array_reverse($lines);
diff --git a/inc/common.php b/inc/common.php
index 3c40a47dc..bc49e76b2 100644
--- a/inc/common.php
+++ b/inc/common.php
@@ -107,9 +107,11 @@ function pageinfo() {
$info['isadmin'] = false;
$info['ismanager'] = false;
if(isset($_SERVER['REMOTE_USER'])) {
+ $sub = new Subscription();
+
$info['userinfo'] = $USERINFO;
$info['perm'] = auth_quickaclcheck($ID);
- $info['subscribed'] = get_info_subscribed();
+ $info['subscribed'] = $sub->user_subscription();
$info['client'] = $_SERVER['REMOTE_USER'];
if($info['perm'] == AUTH_ADMIN) {
@@ -320,15 +322,13 @@ function idfilter($id, $ue = true) {
if($conf['useslash'] && $conf['userewrite']) {
$id = strtr($id, ':', '/');
} elseif(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' &&
- $conf['userewrite'] &&
- strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') === false
+ $conf['userewrite']
) {
$id = strtr($id, ':', ';');
}
if($ue) {
$id = rawurlencode($id);
$id = str_replace('%3A', ':', $id); //keep as colon
- $id = str_replace('%3B', ';', $id); //keep as semicolon
$id = str_replace('%2F', '/', $id); //keep as slash
}
return $id;
@@ -1105,90 +1105,31 @@ function saveOldRevision($id) {
* @author Andreas Gohr <andi@splitbrain.org>
*/
function notify($id, $who, $rev = '', $summary = '', $minor = false, $replace = array()) {
- global $lang;
global $conf;
- global $INFO;
- global $DIFF_INLINESTYLES;
// decide if there is something to do, eg. whom to mail
if($who == 'admin') {
if(empty($conf['notify'])) return false; //notify enabled?
- $text = rawLocale('mailtext');
- $to = $conf['notify'];
- $bcc = '';
+ $tpl = 'mailtext';
+ $to = $conf['notify'];
} elseif($who == 'subscribers') {
- if(!$conf['subscribers']) return false; //subscribers enabled?
+ if(!actionOK('subscribe')) return false; //subscribers enabled?
if($conf['useacl'] && $_SERVER['REMOTE_USER'] && $minor) return false; //skip minors
$data = array('id' => $id, 'addresslist' => '', 'self' => false);
trigger_event(
'COMMON_NOTIFY_ADDRESSLIST', $data,
- 'subscription_addresslist'
+ array(new Subscription(), 'notifyaddresses')
);
- $bcc = $data['addresslist'];
- if(empty($bcc)) return false;
- $to = '';
- $text = rawLocale('subscr_single');
- } elseif($who == 'register') {
- if(empty($conf['registernotify'])) return false;
- $text = rawLocale('registermail');
- $to = $conf['registernotify'];
- $bcc = '';
+ $to = $data['addresslist'];
+ if(empty($to)) return false;
+ $tpl = 'subscr_single';
} else {
return false; //just to be safe
}
- // prepare replacements (keys not set in hrep will be taken from trep)
- $trep = array(
- 'NEWPAGE' => wl($id, '', true, '&'),
- 'PAGE' => $id,
- 'SUMMARY' => $summary
- );
- $trep = array_merge($trep, $replace);
- $hrep = array();
-
// prepare content
- if($who == 'register') {
- $subject = $lang['mail_new_user'].' '.$summary;
- } elseif($rev) {
- $subject = $lang['mail_changed'].' '.$id;
- $trep['OLDPAGE'] = wl($id, "rev=$rev", true, '&');
- $old_content = rawWiki($id, $rev);
- $new_content = rawWiki($id);
- $df = new Diff(explode("\n", $old_content),
- explode("\n", $new_content));
- $dformat = new UnifiedDiffFormatter();
- $tdiff = $dformat->format($df);
-
- $DIFF_INLINESTYLES = true;
- $hdf = new Diff(explode("\n", hsc($old_content)),
- explode("\n", hsc($new_content)));
- $dformat = new InlineDiffFormatter();
- $hdiff = $dformat->format($hdf);
- $hdiff = '<table>'.$hdiff.'</table>';
- $DIFF_INLINESTYLES = false;
- } else {
- $subject = $lang['mail_newpage'].' '.$id;
- $trep['OLDPAGE'] = '---';
- $tdiff = rawWiki($id);
- $hdiff = nl2br(hsc($tdiff));
- }
- $trep['DIFF'] = $tdiff;
- $hrep['DIFF'] = $hdiff;
-
- // send mail
- $mail = new Mailer();
- $mail->to($to);
- $mail->bcc($bcc);
- $mail->subject($subject);
- $mail->setBody($text, $trep, $hrep);
- if($who == 'subscribers') {
- $mail->setHeader(
- 'List-Unsubscribe',
- '<'.wl($id, array('do'=> 'subscribe'), true, '&').'>',
- false
- );
- }
- return $mail->send();
+ $subscription = new Subscription();
+ return $subscription->send_diff($to, $tpl, $id, $rev, $summary);
}
/**
diff --git a/inc/html.php b/inc/html.php
index f4e6af663..5c1c75cf6 100644
--- a/inc/html.php
+++ b/inc/html.php
@@ -1390,8 +1390,7 @@ function html_edit(){
$data = array('form' => $form,
'wr' => $wr,
'media_manager' => true,
- 'target' => ($INPUT->has('target') && $wr &&
- $RANGE !== '') ? $INPUT->str('target') : 'section',
+ 'target' => ($INPUT->has('target') && $wr) ? $INPUT->str('target') : 'section',
'intro_locale' => $include);
if ($data['target'] !== 'section') {
diff --git a/inc/indexer.php b/inc/indexer.php
index 7a62345bf..e518907d7 100644
--- a/inc/indexer.php
+++ b/inc/indexer.php
@@ -339,6 +339,109 @@ class Doku_Indexer {
}
/**
+ * 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);
+ if ($id === false) {
+ $this->unlock();
+ return 'page is not in index';
+ }
+
+ $new_id = array_search($newpage, $pages);
+ 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, can exist (then 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);
+ if ($oldid !== false) {
+ $newid = array_search($newvalue, $metavalues);
+ 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.
@@ -351,10 +454,26 @@ class Doku_Indexer {
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) {
- $this->unlock();
return false;
}
@@ -380,7 +499,6 @@ class Doku_Indexer {
}
// Save the reverse index
if (!$this->saveIndexKey('pageword', '', $pid, "")) {
- $this->unlock();
return false;
}
@@ -397,7 +515,6 @@ class Doku_Indexer {
$this->saveIndexKey($metaname.'_p', '', $pid, '');
}
- $this->unlock();
return true;
}
diff --git a/inc/lang/ar/subscr_single.txt b/inc/lang/ar/subscr_single.txt
index 5c62aeaeb..611688415 100644
--- a/inc/lang/ar/subscr_single.txt
+++ b/inc/lang/ar/subscr_single.txt
@@ -15,7 +15,7 @@
لإلغاء إشعارات الصفحة,لُج الويكي في
@DOKUWIKIURL@ ثم زُر
-@NEWPAGE@
+@SUBSCRIBE@
وألغ الاشتراك من تغييرات الصفحة و/أو النطاق.
--
diff --git a/inc/lang/bg/subscr_single.txt b/inc/lang/bg/subscr_single.txt
index 7b26f8e96..a74a21fb8 100644
--- a/inc/lang/bg/subscr_single.txt
+++ b/inc/lang/bg/subscr_single.txt
@@ -14,7 +14,7 @@
Нова версия: @NEWPAGE@
Ако желаете да прекратите уведомяването за страницата трябва да се впишете на адрес @DOKUWIKIURL@, да посетите
-@NEWPAGE@
+@SUBSCRIBE@
и да прекратите абонамента за промени по страницата или именното пространство.
--
diff --git a/inc/lang/ca/lang.php b/inc/lang/ca/lang.php
index 0fd88ec39..cb2b64686 100644
--- a/inc/lang/ca/lang.php
+++ b/inc/lang/ca/lang.php
@@ -5,6 +5,7 @@
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author Carles Bellver <carles.bellver@cent.uji.es>
* @author Carles Bellver <carles.bellver@gmail.com>
+ * @author daniel@6temes.cat
*/
$lang['encoding'] = 'utf-8';
$lang['direction'] = 'ltr';
@@ -27,7 +28,7 @@ $lang['btn_revs'] = 'Revisions anteriors';
$lang['btn_recent'] = 'Canvis recents';
$lang['btn_upload'] = 'Penja';
$lang['btn_cancel'] = 'Cancel·la';
-$lang['btn_index'] = 'Índex';
+$lang['btn_index'] = 'Mapa del lloc';
$lang['btn_secedit'] = 'Edita';
$lang['btn_login'] = 'Entra';
$lang['btn_logout'] = 'Surt';
@@ -38,14 +39,15 @@ $lang['btn_back'] = 'Enrere';
$lang['btn_backlink'] = 'Què hi enllaça';
$lang['btn_backtomedia'] = 'Torna a la selecció de fitxers';
$lang['btn_subscribe'] = 'Subscripció a canvis d\'aquesta pàgina';
-$lang['btn_unsubscribe'] = 'Cancel·la subscripció a pàgina';
$lang['btn_profile'] = 'Actualització del perfil';
$lang['btn_reset'] = 'Reinicia';
+$lang['btn_resendpwd'] = 'Estableix una nova contrasenya';
$lang['btn_draft'] = 'Edita esborrany';
$lang['btn_recover'] = 'Recupera esborrany';
$lang['btn_draftdel'] = 'Suprimeix esborrany';
$lang['btn_revert'] = 'Restaura';
$lang['btn_register'] = 'Registra\'m';
+$lang['btn_apply'] = 'Aplica';
$lang['loggedinas'] = 'Heu entrat com';
$lang['user'] = 'Nom d\'usuari';
$lang['pass'] = 'Contrasenya';
@@ -75,6 +77,7 @@ $lang['profnoempty'] = 'No es pot deixar en blanc el nom o l\'adreça
$lang['profchanged'] = 'El perfil d\'usuari s\'ha actualitzat correctament.';
$lang['pwdforget'] = 'Heu oblidat la contrasenya? Podeu obtenir-ne una de nova.';
$lang['resendna'] = 'Aquest wiki no permet tornar a enviar la contrasenya.';
+$lang['resendpwd'] = 'Estableix una nova contrasenya per';
$lang['resendpwdmissing'] = 'Heu d\'emplenar tots els camps.';
$lang['resendpwdnouser'] = 'No s\'ha pogut trobar aquest usuari a la base de dades.';
$lang['resendpwdbadauth'] = 'Aquest codi d\'autenticació no és vàlid. Assegureu-vos d\'utilitzar l\'enllaç de confirmació complet.';
@@ -87,10 +90,52 @@ $lang['searchmedia_in'] = 'Cerca en: %s';
$lang['txt_upload'] = 'Trieu el fitxer que voleu penjar';
$lang['txt_filename'] = 'Introduïu el nom wiki (opcional)';
$lang['txt_overwrt'] = 'Sobreescriu el fitxer actual';
+$lang['maxuploadsize'] = 'Puja com a màxim %s per arxiu.';
$lang['lockedby'] = 'Actualment blocat per:';
$lang['lockexpire'] = 'Venciment del blocatge:';
-$lang['js']['willexpire'] = 'El blocatge per a editar aquesta pàgina venç d\'aquí a un minut.\nUtilitzeu la visualització prèvia per reiniciar el rellotge i evitar conflictes.';
-$lang['js']['notsavedyet'] = "Heu fet canvis que es perdran si no els deseu.\nVoleu continuar?";
+$lang['js']['willexpire'] = 'El blocatge per a editar aquesta pàgina venç d\'aquí a un minut.\nUtilitzeu la visualització prèvia per reiniciar el rellotge i evitar conflictes.';
+$lang['js']['notsavedyet'] = 'Heu fet canvis que es perdran si no els deseu.
+Voleu continuar?';
+$lang['js']['searchmedia'] = 'Cerca fitxers';
+$lang['js']['keepopen'] = 'Manté la finestra oberta';
+$lang['js']['hidedetails'] = 'Oculta detalls';
+$lang['js']['mediatitle'] = 'Propietats de l\'enllaç';
+$lang['js']['mediadisplay'] = 'Tipus d\'enllaç';
+$lang['js']['mediaalign'] = 'Alineació';
+$lang['js']['mediasize'] = 'Mida de la imatge';
+$lang['js']['mediatarget'] = 'Destí de l\'enllaç';
+$lang['js']['mediaclose'] = 'Tanca';
+$lang['js']['mediainsert'] = 'Inserta';
+$lang['js']['mediadisplayimg'] = 'Mostra la imatge';
+$lang['js']['mediadisplaylnk'] = 'Mostra només l\'enllaç';
+$lang['js']['mediasmall'] = 'Versió petita';
+$lang['js']['mediamedium'] = 'Versió mitjana';
+$lang['js']['medialarge'] = 'Versió gran';
+$lang['js']['mediaoriginal'] = 'Versió original';
+$lang['js']['medialnk'] = 'Enllaç a la pàgina de detalls';
+$lang['js']['mediadirect'] = 'Enllaç directe a l\'original';
+$lang['js']['medianolnk'] = 'No hi ha enllaç';
+$lang['js']['medianolink'] = 'No enllacis la imatge';
+$lang['js']['medialeft'] = 'Alinea la imatge a l\'esquerra.';
+$lang['js']['mediaright'] = 'Alinea la imatge a la dreta.';
+$lang['js']['mediacenter'] = 'Alinea la imatge al mig.';
+$lang['js']['medianoalign'] = 'No facis servir alineació.';
+$lang['js']['nosmblinks'] = 'Els enllaços amb recursos compartits de Windows només funcionen amb el Microsoft Internet Explorer.
+Si voleu podeu copiar i enganxar l\'enllaç.';
+$lang['js']['linkwiz'] = 'Auxiliar d\'enllaços';
+$lang['js']['linkto'] = 'Enllaça a:';
+$lang['js']['del_confirm'] = 'Suprimiu aquesta entrada?';
+$lang['js']['restore_confirm'] = 'Vols realment restaurar aquesta versió?';
+$lang['js']['media_diff'] = 'Veure les diferències:';
+$lang['js']['media_diff_both'] = 'Un al costat de l\'altre';
+$lang['js']['media_diff_opacity'] = 'Resalta';
+$lang['js']['media_diff_portions'] = 'Llisca';
+$lang['js']['media_select'] = 'Escull els arxius';
+$lang['js']['media_upload_btn'] = 'Pujar';
+$lang['js']['media_done_btn'] = 'Fet';
+$lang['js']['media_drop'] = 'Arrossega aquí els arxius a pujar';
+$lang['js']['media_cancel'] = 'esborra';
+$lang['js']['media_overwrt'] = 'Sobreescriu els arxius existents';
$lang['rssfailed'] = 'S\'ha produït un error en recollir aquesta alimentació: ';
$lang['nothingfound'] = 'No s\'ha trobat res.';
$lang['mediaselect'] = 'Selecció de fitxers';
@@ -108,14 +153,7 @@ $lang['deletefail'] = 'No s\'ha pogut suprimir el fitxer "%s". Compro
$lang['mediainuse'] = 'No s\'ha pogut suprimir el fitxer "%s". Encara s\'està utilitzant.';
$lang['namespaces'] = 'Espais';
$lang['mediafiles'] = 'Fitxers disponibles en';
-$lang['js']['searchmedia'] = 'Cerca fitxers';
-$lang['js']['keepopen'] = 'Manté la finestra oberta';
-$lang['js']['hidedetails'] = 'Oculta detalls';
-$lang['js']['nosmblinks'] = 'Els enllaços amb recursos compartits de Windows només funcionen amb el Microsoft Internet Explorer.
-Si voleu podeu copiar i enganxar l\'enllaç.';
-$lang['js']['linkwiz'] = 'Auxiliar d\'enllaços';
-$lang['js']['linkto'] = 'Enllaça a:';
-$lang['js']['del_confirm'] = 'Suprimiu aquesta entrada?';
+$lang['accessdenied'] = 'No teniu permís per a veure aquesta pàgina.';
$lang['mediausage'] = 'Utilitzeu la sintaxi següent per referir-vos a aquest enllaç:';
$lang['mediaview'] = 'Mostra el fitxer original';
$lang['mediaroot'] = 'arrel';
@@ -131,6 +169,10 @@ $lang['current'] = 'actual';
$lang['yours'] = 'La vostra versió';
$lang['diff'] = 'Mostra diferències amb la versió actual';
$lang['diff2'] = 'Mostra diferències entre les revisions seleccionades';
+$lang['difflink'] = 'Enllaç a la visualització de la comparació';
+$lang['diff_type'] = 'Veieu les diferències:';
+$lang['diff_inline'] = 'En línia';
+$lang['diff_side'] = 'Un al costat de l\'altre';
$lang['line'] = 'Línia';
$lang['breadcrumb'] = 'Camí';
$lang['youarehere'] = 'Sou aquí';
@@ -143,10 +185,20 @@ $lang['external_edit'] = 'edició externa';
$lang['summary'] = 'Resum d\'edició';
$lang['noflash'] = 'Per a visualitzar aquest contingut necessiteu el <a href="http://www.adobe.com/products/flashplayer/">connector d\'Adobe Flash</a>.';
$lang['download'] = 'Baixa el fragment';
+$lang['tools'] = 'Eines';
+$lang['user_tools'] = 'Eines de l\'usuari';
+$lang['site_tools'] = 'Eines del lloc';
+$lang['page_tools'] = 'Eines de la pàgina';
+$lang['skip_to_content'] = 'salta al contingut';
+$lang['sidebar'] = 'Barra lateral';
$lang['mail_newpage'] = 'pàgina afegida:';
$lang['mail_changed'] = 'pàgina modificada:';
$lang['mail_new_user'] = 'nou usuari:';
$lang['mail_upload'] = 'fitxer penjat:';
+$lang['changes_type'] = 'Veure els canvis de';
+$lang['pages_changes'] = 'Pàgines';
+$lang['media_changes'] = 'Arxius gràfics';
+$lang['both_changes'] = 'Pàgines i arxius gràfics';
$lang['qb_bold'] = 'Negreta';
$lang['qb_italic'] = 'Cursiva';
$lang['qb_underl'] = 'Subratllat';
@@ -187,13 +239,27 @@ $lang['img_copyr'] = 'Copyright';
$lang['img_format'] = 'Format';
$lang['img_camera'] = 'Càmera';
$lang['img_keywords'] = 'Paraules clau';
-$lang['subscribe_success'] = 'S\'ha afegit %s a la llista de subscripcions de %s';
-$lang['subscribe_error'] = 'S\'ha produït un error en afegir %s a la llista de subscripcions de %s';
-$lang['subscribe_noaddress'] = 'No hi ha cap adreça de correu associada al vostre nom d\'usuari. No se us ha pogut afegir a la llista de subscripcions.';
-$lang['unsubscribe_success'] = '%s ha estat suprimit de la llista de subscripcions de %s';
-$lang['unsubscribe_error'] = 'S\'ha produït un error en suprimir %s de la llista de subscripcions de %s';
+$lang['img_width'] = 'Ample';
+$lang['img_height'] = 'Alçada';
+$lang['subscr_subscribe_success'] = 'S\'ha afegit %s a la llista de subscripcions per %s';
+$lang['subscr_subscribe_error'] = 'Hi ha hagut un error a l\'afegir %s a la llista per %s';
+$lang['subscr_subscribe_noaddress'] = 'No hi ha cap adreça associada pel vostre nom d\'usuari, no podeu ser afegit a la llista de subscripcions';
+$lang['subscr_unsubscribe_success'] = 'S\'ha esborrat %s de la llista de subscripcions per %s';
+$lang['subscr_unsubscribe_error'] = 'Hi ha hagut un error a l\'esborrar %s de la llista de subscripcions per %s';
+$lang['subscr_already_subscribed'] = '%s ja està subscrit a %s';
+$lang['subscr_not_subscribed'] = '%s no està subscrit a %s';
+$lang['subscr_m_not_subscribed'] = 'En aquests moments no esteu subscrit a l\'actual pàgina o espai';
+$lang['subscr_m_new_header'] = 'Afegeix subcripció';
+$lang['subscr_m_current_header'] = 'Subscripcions actuals';
+$lang['subscr_m_unsubscribe'] = 'Donar-se de baixa';
+$lang['subscr_m_subscribe'] = 'Donar-se d\'alta';
+$lang['subscr_m_receive'] = 'Rebre';
+$lang['subscr_style_every'] = 'Envia\'m un correu electrònic per a cada canvi';
+$lang['subscr_style_digest'] = 'Envia\'m un correu electrònic amb un resum dels canvis per a cada pàgina (cada %.2f dies)';
+$lang['subscr_style_list'] = 'llistat de pàgines canviades des de l\'últim correu electrònic (cada %.2f dies)';
$lang['authmodfailed'] = 'La configuració de l\'autenticació d\'usuaris és errònia. Informeu els administradors del wiki.';
$lang['authtempfail'] = 'L\'autenticació d\'usuaris no està disponible temporalment. Si aquesta situació persisteix, si us plau informeu els administradors del wiki.';
+$lang['authpwdexpire'] = 'La vostra contrasenya caducarà en %d dies, l\'hauríeu de canviar aviat.';
$lang['i_chooselang'] = 'Trieu l\'idioma';
$lang['i_installer'] = 'Instal·lador de DokuWiki';
$lang['i_wikiname'] = 'Nom del wiki';
@@ -208,13 +274,14 @@ $lang['i_confexists'] = '<code>%s</code> ja existeix';
$lang['i_writeerr'] = 'No es pot crear <code>%s</code>. Comproveu els permisos del directori i/o del fitxer i creeu el fitxer manualment.';
$lang['i_badhash'] = 'dokuwiki.php no reconegut o modificat (hash=<code>%s</code>)';
$lang['i_badval'] = '<code>%s</code> - valor il·legal o buit';
-$lang['i_success'] = 'La configuració s\'ha acabat amb èxit. Ara podeu suprimir el fitxer install.php. Aneu al vostre nou <a href="doku.php?id=wiki:welcome">DokuWiki</a>.';
-$lang['i_failure'] = 'S\'han produït alguns errors en escriure els fitxers de configuració. Potser caldrà que els arregleu manualment abans d\'utilitzar el vostre nou <a href="doku.php?id=wiki:welcome">DokuWiki</a>.';
+$lang['i_success'] = 'La configuració s\'ha acabat amb èxit. Ara podeu suprimir el fitxer install.php. Aneu al vostre nou <a href="doku.php">DokuWiki</a>.';
+$lang['i_failure'] = 'S\'han produït alguns errors en escriure els fitxers de configuració. Potser caldrà que els arregleu manualment abans d\'utilitzar el vostre nou <a href="doku.php">DokuWiki</a>.';
$lang['i_policy'] = 'Política ACL inicial';
$lang['i_pol0'] = 'Wiki obert (tothom pot llegir, escriure i penjar fitxers)';
$lang['i_pol1'] = 'Wiki públic (tothom pot llegir, els usuaris registrats poden escriure i penjar fitxers)';
$lang['i_pol2'] = 'Wiki tancat (només els usuaris registrats poden llegir, escriure i penjar fitxers)';
$lang['i_retry'] = 'Reintenta';
+$lang['i_license'] = 'Escolliu el tipus de llicència que voleu fer servir per al vostre contingut:';
$lang['recent_global'] = 'Esteu veient els canvis recents de l\'espai <strong>%s</strong>. També podeu veure els <a href="%s">canvis recents de tot el wiki</a>.';
$lang['years'] = 'fa %d anys';
$lang['months'] = 'fa %d mesos';
@@ -223,3 +290,27 @@ $lang['days'] = 'fa %d dies';
$lang['hours'] = 'fa %d hores';
$lang['minutes'] = 'fa %d minuts';
$lang['seconds'] = 'fa %d segons';
+$lang['wordblock'] = 'El vostre canvi no s\'ha guardat perquè conté text blocat (spam)';
+$lang['media_uploadtab'] = 'Puja';
+$lang['media_searchtab'] = 'Busca';
+$lang['media_file'] = 'Fitxer';
+$lang['media_viewtab'] = 'Mostra';
+$lang['media_edittab'] = 'Edita';
+$lang['media_historytab'] = 'Històric';
+$lang['media_list_thumbs'] = 'Miniatura';
+$lang['media_list_rows'] = 'Files';
+$lang['media_sort_name'] = 'Nom';
+$lang['media_sort_date'] = 'Data';
+$lang['media_namespaces'] = 'Escolliu l\'espai';
+$lang['media_files'] = 'Arxius a %s';
+$lang['media_upload'] = 'Puja a %s';
+$lang['media_search'] = 'Busca a %s';
+$lang['media_view'] = '%s';
+$lang['media_viewold'] = '%s a %s';
+$lang['media_edit'] = 'Edita %s';
+$lang['media_history'] = 'Històric de %s';
+$lang['media_meta_edited'] = 'metadata editada';
+$lang['media_perm_read'] = 'No teniu permisos suficients per a llegir arxius.';
+$lang['media_perm_upload'] = 'No teniu permisos suficients per a pujar arxius';
+$lang['media_update'] = 'Puja la nova versió';
+$lang['media_restore'] = 'Restaura aquesta versió';
diff --git a/inc/lang/ca/mailwrap.html b/inc/lang/ca/mailwrap.html
new file mode 100644
index 000000000..ed3bb6e9d
--- /dev/null
+++ b/inc/lang/ca/mailwrap.html
@@ -0,0 +1,13 @@
+<html>
+<head>
+<title>@TITLE@</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+</head>
+<body>
+
+@HTMLBODY@
+
+<br /><hr />
+<small>Aquest correu electrònic ha estat generat per DokuWiki a @DOKUWIKIURL@.</small>
+</body>
+</html> \ No newline at end of file
diff --git a/inc/lang/ca/resetpwd.txt b/inc/lang/ca/resetpwd.txt
new file mode 100644
index 000000000..565f1d5b0
--- /dev/null
+++ b/inc/lang/ca/resetpwd.txt
@@ -0,0 +1,3 @@
+===== Establiu una nova contrasenya =====
+
+Introdueixi una nova contrasenya pel seu compte a aquest wiki. \ No newline at end of file
diff --git a/inc/lang/ca/subscr_digest.txt b/inc/lang/ca/subscr_digest.txt
new file mode 100644
index 000000000..2b95f97a5
--- /dev/null
+++ b/inc/lang/ca/subscr_digest.txt
@@ -0,0 +1,21 @@
+Hola!
+
+La pàgina @PAGE@ al wiki @TITLE@ ha canviat.
+A continuació podeu veure els canvis:
+
+--------------------------------------------------------
+@DIFF@
+--------------------------------------------------------
+
+Versió anterior: @OLDPAGE@
+Nova versió: @NEWPAGE@
+
+Si voleu cancel·lar les notificacions per a la pàgina, accediu al wiki a
+@DOKUWIKIURL@, visiteu
+@SUBSCRIBE@
+i doneu-vos de baixa dels canvis de la pàgina o de l'espai.
+
+
+--
+Aquest mail ha estat generat per DokuWiki a
+@DOKUWIKIURL@ \ No newline at end of file
diff --git a/inc/lang/ca/subscr_form.txt b/inc/lang/ca/subscr_form.txt
new file mode 100644
index 000000000..d3679454f
--- /dev/null
+++ b/inc/lang/ca/subscr_form.txt
@@ -0,0 +1,3 @@
+===== Gestió de les Subscripcions =====
+
+Aquesta pàgina podeu gestiona les vostres subscripcions per a les pàgines i els espais actuals. \ No newline at end of file
diff --git a/inc/lang/ca/subscr_list.txt b/inc/lang/ca/subscr_list.txt
new file mode 100644
index 000000000..56bcad545
--- /dev/null
+++ b/inc/lang/ca/subscr_list.txt
@@ -0,0 +1,21 @@
+Hola!
+
+Alguna(es) pàgina(es) de l'espai @PAGE@ al wiki @TITLE@ han canviat.
+A continuació podeu veure els canvis:
+
+--------------------------------------------------------
+@DIFF@
+--------------------------------------------------------
+
+Versió anterior: @OLDPAGE@
+Nova versió: @NEWPAGE@
+
+Si voleu cancel·lar les notificacions per a la pàgina, accediu al wiki a
+@DOKUWIKIURL@, visiteu
+@SUBSCRIBE@
+i doneu-vos de baixa dels canvis de la pàgina o de l'espai.
+
+
+--
+Aquest mail ha estat generat per DokuWiki a
+@DOKUWIKIURL@ \ No newline at end of file
diff --git a/inc/lang/da/subscr_single.txt b/inc/lang/da/subscr_single.txt
index 64b14588c..cdc554513 100644
--- a/inc/lang/da/subscr_single.txt
+++ b/inc/lang/da/subscr_single.txt
@@ -15,7 +15,7 @@ Ny Revision: @NEWPAGE@
For at slå side notifikationer fra, skal du logge ind på
@DOKUWIKIURL@ og besøge
-@NEWPAGE@
+@SUBSCRIBE@
og slå abonnoment for side / navnerum ændringer fra.
--
diff --git a/inc/lang/de-informal/subscr_single.txt b/inc/lang/de-informal/subscr_single.txt
index 3c557bc17..6e3f58b9f 100644
--- a/inc/lang/de-informal/subscr_single.txt
+++ b/inc/lang/de-informal/subscr_single.txt
@@ -15,7 +15,7 @@ Neue Revision: @NEWPAGE@
Um das Abonnement für diese Seite aufzulösen, melde dich im Wiki an
@DOKUWIKIURL@, besuche dann
-@NEWPAGE@
+@SUBSCRIBE@
und klicke auf den Link 'Aboverwaltung'.
--
diff --git a/inc/lang/de/subscr_single.txt b/inc/lang/de/subscr_single.txt
index f3e1cd393..da9914e50 100644
--- a/inc/lang/de/subscr_single.txt
+++ b/inc/lang/de/subscr_single.txt
@@ -15,7 +15,7 @@ Neue Revision: @NEWPAGE@
Um das Abonnement für diese Seite aufzulösen, melden Sie sich im Wiki an
@DOKUWIKIURL@, besuchen dann
-@NEWPAGE@
+@SUBSCRIBE@
und klicken auf die Taste 'Aboverwaltung'.
--
diff --git a/inc/lang/en/subscr_single.txt b/inc/lang/en/subscr_single.txt
index 673c4c32a..0bc310e04 100644
--- a/inc/lang/en/subscr_single.txt
+++ b/inc/lang/en/subscr_single.txt
@@ -15,7 +15,7 @@ New Revision: @NEWPAGE@
To cancel the page notifications, log into the wiki at
@DOKUWIKIURL@ then visit
-@NEWPAGE@
+@SUBSCRIBE@
and unsubscribe page and/or namespace changes.
--
diff --git a/inc/lang/eo/subscr_single.txt b/inc/lang/eo/subscr_single.txt
index 431fd0251..e4847e8ab 100644
--- a/inc/lang/eo/subscr_single.txt
+++ b/inc/lang/eo/subscr_single.txt
@@ -15,7 +15,7 @@ Nova versio: @NEWPAGE@
Por nuligi la paĝinformojn, ensalutu la vikion ĉe
@DOKUWIKIURL@, poste iru al
-@NEWPAGE@
+@SUBSCRIBE@
kaj malabonu la paĝajn kaj/aŭ nomspacajn ŝanĝojn.
--
diff --git a/inc/lang/he/subscr_single.txt b/inc/lang/he/subscr_single.txt
index 123b186c8..78b551e2f 100644
--- a/inc/lang/he/subscr_single.txt
+++ b/inc/lang/he/subscr_single.txt
@@ -14,7 +14,7 @@
לביטול התרעות בנוגע לעמוד, יש להיכנס לאתר הוויקי בכתובת
@DOKUWIKIURL@ ואז לבקר בדף
-@NEWPAGE@
+@SUBSCRIBE@
ולבטל את המינוי לקבלת שינויים בדף ו/או במרחב השם.
--
diff --git a/inc/lang/ia/subscr_single.txt b/inc/lang/ia/subscr_single.txt
index 3d6ef7103..445df197c 100644
--- a/inc/lang/ia/subscr_single.txt
+++ b/inc/lang/ia/subscr_single.txt
@@ -15,7 +15,7 @@ Version nove: @NEWPAGE@
Pro cancellar le notificationes de paginas, aperi un session al wiki a
@DOKUWIKIURL@ postea visita
-@NEWPAGE@
+@SUBSCRIBE@
e cancella tu subscription al modificationes in paginas e/o spatios de nomines.
--
diff --git a/inc/lang/it/subscr_single.txt b/inc/lang/it/subscr_single.txt
index 8cde8ea0f..a8649a4ef 100644
--- a/inc/lang/it/subscr_single.txt
+++ b/inc/lang/it/subscr_single.txt
@@ -15,7 +15,7 @@ Nuova revisione: @NEWPAGE@
Per non ricevere più queste notifiche, collegati al
wiki all'indirizzo @DOKUWIKIURL@ e poi visita
-@NEWPAGE@
+@SUBSCRIBE@
e rimuovi la sottoscrizione alle modifiche della
pagina o categoria.
diff --git a/inc/lang/ko/subscr_single.txt b/inc/lang/ko/subscr_single.txt
index 6bd1885e6..2679db393 100644
--- a/inc/lang/ko/subscr_single.txt
+++ b/inc/lang/ko/subscr_single.txt
@@ -14,7 +14,7 @@
새 버전 : @NEWPAGE@
이 문서의 알림을 취소하려면, @DOKUWIKIURL@에 로그인한 뒤
-@NEWPAGE@ 문서를 방문하여 문서나 이름공간의 구독을 취소하세요.
+@SUBSCRIBE@ 문서를 방문하여 문서나 이름공간의 구독을 취소하세요.
--
@DOKUWIKIURL@의 DokuWiki가 자동으로 만들어낸 메일입니다. \ No newline at end of file
diff --git a/inc/lang/la/subscr_digest.txt b/inc/lang/la/subscr_digest.txt
index 629213359..32d378a7e 100644
--- a/inc/lang/la/subscr_digest.txt
+++ b/inc/lang/la/subscr_digest.txt
@@ -12,7 +12,7 @@ Noua recensio: @NEWPAGE@
Ut paginae adnotationes deleas, in uicem ineas in
@DOKUWIKIURL@, deinde uideas
-@NEWPAGE@
+@SUBSCRIBE@
et paginarum generum optiones mutes.
--
diff --git a/inc/lang/la/subscr_single.txt b/inc/lang/la/subscr_single.txt
index 7839791ea..14285014c 100644
--- a/inc/lang/la/subscr_single.txt
+++ b/inc/lang/la/subscr_single.txt
@@ -15,7 +15,7 @@ Noua recensio: @NEWPAGE@
Ut paginae adnotationes deleas, in uicem ineas in
@DOKUWIKIURL@, deinde uideas
-@NEWPAGE@
+@SUBSCRIBE@
et paginarum et\aut generum optiones mutasa.
--
diff --git a/inc/lang/no/subscr_digest.txt b/inc/lang/no/subscr_digest.txt
index 6afd0cc5c..670d39d32 100644
--- a/inc/lang/no/subscr_digest.txt
+++ b/inc/lang/no/subscr_digest.txt
@@ -12,7 +12,7 @@ Ny versjon: @NEWPAGE@
For å avslutte varslingen, logg inn på
@DOKUWIKIURL@ og gå til
-@NEWPAGE@
+@SUBSCRIBE@
og avslutt abonnementet på endringer av siden eller i navnerommet.
--
diff --git a/inc/lang/no/subscr_list.txt b/inc/lang/no/subscr_list.txt
index 72cd307cb..860d88d2a 100644
--- a/inc/lang/no/subscr_list.txt
+++ b/inc/lang/no/subscr_list.txt
@@ -9,7 +9,7 @@ Her er endringene:
For å avslutte varslinga, logg inn på
@DOKUWIKIURL@ og gå til
-@NEWPAGE@
+@SUBSCRIBE@
og avslutt abonnementet på endringer av sida eller i navnerommet.
--
diff --git a/inc/lang/no/subscr_single.txt b/inc/lang/no/subscr_single.txt
index 25296da58..b26b3a879 100644
--- a/inc/lang/no/subscr_single.txt
+++ b/inc/lang/no/subscr_single.txt
@@ -15,7 +15,7 @@ Ny versjon: @NEWPAGE@
For å avslutte varslingen, logg inn på
@DOKUWIKIURL@, gå til
-@NEWPAGE@
+@SUBSCRIBE@
og avslutt abonnementet på endringer av siden eller i navnerommet.
--
diff --git a/inc/lang/pt/subscr_single.txt b/inc/lang/pt/subscr_single.txt
index 1187b5911..469c6bfb1 100644
--- a/inc/lang/pt/subscr_single.txt
+++ b/inc/lang/pt/subscr_single.txt
@@ -15,7 +15,7 @@ Revisão Nova: @NEWPAGE@
Para cancelar as notificações de página, inicie sessão no wiki em
@DOKUWIKIURL@, visite
-@NEWPAGE@
+@SUBSCRIBE@
e des-subscreva às alterações de página e/ou espaço de nome.
--
diff --git a/inc/lang/sq/subscr_single.txt b/inc/lang/sq/subscr_single.txt
index 90520be4f..df28ee176 100644
--- a/inc/lang/sq/subscr_single.txt
+++ b/inc/lang/sq/subscr_single.txt
@@ -15,7 +15,7 @@ Rishikimi i ri: @NEWPAGE@
Për të fshirë lajmërimet e faqes, hyni në wiki tek
@DOKUWIKIURL@ dhe pastaj vizitoni
-@NEWPAGE@
+@SUBSCRIBE@
dhe fshini ndryshimet e faqes dhe/ose hapësirës së emrit.
--
diff --git a/inc/lang/tr/lang.php b/inc/lang/tr/lang.php
index a8d8c5ac9..5430905b1 100644
--- a/inc/lang/tr/lang.php
+++ b/inc/lang/tr/lang.php
@@ -8,6 +8,7 @@
* @author Cihan Kahveci <kahvecicihan@gmail.com>
* @author Yavuz Selim <yavuzselim@gmail.com>
* @author Caleb Maclennan <caleb@alerque.com>
+ * @author farukerdemoncel@gmail.com
*/
$lang['encoding'] = 'utf-8';
$lang['direction'] = 'ltr';
@@ -43,11 +44,14 @@ $lang['btn_backtomedia'] = 'Çokluortam dosyası seçimine dön';
$lang['btn_subscribe'] = 'Sayfa Değişikliklerini Bildir';
$lang['btn_profile'] = 'Kullanıcı Bilgilerini Güncelle';
$lang['btn_reset'] = 'Sıfırla';
+$lang['btn_resendpwd'] = 'Yeni şifre belirle';
$lang['btn_draft'] = 'Taslağı düzenle';
$lang['btn_recover'] = 'Taslağı geri yükle';
$lang['btn_draftdel'] = 'Taslağı sil';
$lang['btn_revert'] = 'Geri Yükle';
$lang['btn_register'] = 'Kayıt ol';
+$lang['btn_apply'] = 'Uygula';
+$lang['btn_media'] = 'Çokluortam Yöneticisi';
$lang['loggedinas'] = 'Giriş ismi';
$lang['user'] = 'Kullanıcı ismi';
$lang['pass'] = 'Parola';
@@ -77,6 +81,7 @@ $lang['profnoempty'] = 'Boş isim veya e-posta adresine izin verilmiyo
$lang['profchanged'] = 'Kullanıcı bilgileri başarıyla değiştirildi.';
$lang['pwdforget'] = 'Parolanızı mı unuttunuz? Yeni bir parola alın';
$lang['resendna'] = 'Bu wiki parolayı tekrar göndermeyi desteklememektedir.';
+$lang['resendpwd'] = 'İçin yeni şifre belirle';
$lang['resendpwdmissing'] = 'Üzgünüz, tüm alanları doldurmalısınız.';
$lang['resendpwdnouser'] = 'Üzgünüz, veritabanımızda bu kullanıcıyı bulamadık.';
$lang['resendpwdbadauth'] = 'Üzgünüz, bu doğrulama kodu doğru değil. Doğrulama linkini tam olarak kullandığınıza emin olun.';
@@ -91,7 +96,7 @@ $lang['txt_filename'] = 'Dosya adı (zorunlu değil)';
$lang['txt_overwrt'] = 'Mevcut dosyanın üstüne yaz';
$lang['lockedby'] = 'Şu an şunun tarafından kilitli:';
$lang['lockexpire'] = 'Kilitin açılma tarihi:';
-$lang['js']['willexpire'] = 'Bu sayfayı değiştirme kilidinin süresi yaklaşık bir dakika içinde geçecek.\nÇakışmaları önlemek için önizleme tuşunu kullanarak kilit sayacını sıfırla.';
+$lang['js']['willexpire'] = 'Bu sayfayı değiştirme kilidinin süresi yaklaşık bir dakika içinde geçecek.\nÇakışmaları önlemek için önizleme tuşunu kullanarak kilit sayacını sıfırla.';
$lang['js']['notsavedyet'] = 'Kaydedilmemiş değişiklikler kaybolacak.
Devam etmek istiyor musunuz?';
$lang['js']['searchmedia'] = 'Dosyalar için Ara';
@@ -122,6 +127,14 @@ $lang['js']['nosmblinks'] = 'Windows paylaşımı sadece Microsoft Internet
$lang['js']['linkwiz'] = 'Bağlantı sihirbazı';
$lang['js']['linkto'] = 'Bağlantı:';
$lang['js']['del_confirm'] = 'Bu girişi sil?';
+$lang['js']['restore_confirm'] = 'Bu sürüme geri dönmek istediğinizden emin misiniz?';
+$lang['js']['media_diff'] = 'Farkları gör:';
+$lang['js']['media_select'] = 'Dosyalar seç...';
+$lang['js']['media_upload_btn'] = 'Yükle';
+$lang['js']['media_done_btn'] = 'Bitti';
+$lang['js']['media_drop'] = 'Yüklemek istediğiniz dosyaları buraya bırakın';
+$lang['js']['media_cancel'] = 'kaldır';
+$lang['js']['media_overwrt'] = 'Var olan dosyaların üzerine yaz';
$lang['rssfailed'] = 'Bu beslemeyi çekerken hata oluştu: ';
$lang['nothingfound'] = 'Hiçbir şey yok.';
$lang['mediaselect'] = 'Çokluortam dosyası seçimi';
@@ -172,6 +185,9 @@ $lang['mail_newpage'] = 'sayfa eklenme:';
$lang['mail_changed'] = 'sayfa değiştirilme:';
$lang['mail_new_user'] = 'yeni kullanıcı';
$lang['mail_upload'] = 'dosya yüklendi:';
+$lang['pages_changes'] = 'Sayfalar';
+$lang['media_changes'] = 'Çokluortam dosyaları';
+$lang['both_changes'] = 'Sayfalar ve çoklu ortam dosyaları';
$lang['qb_bold'] = 'Kalın Yazı';
$lang['qb_italic'] = 'Eğik Yazı';
$lang['qb_underl'] = 'Altı Çizgili Yazı';
@@ -209,8 +225,14 @@ $lang['img_copyr'] = 'Telif Hakkı';
$lang['img_format'] = 'Biçim';
$lang['img_camera'] = 'Fotoğraf Makinası';
$lang['img_keywords'] = 'Anahtar Sözcükler';
+$lang['img_width'] = 'Genişlik';
+$lang['img_height'] = 'Yükseklik';
+$lang['img_manager'] = 'Ortam oynatıcısında göster';
+$lang['subscr_m_subscribe'] = 'Kayıt ol';
+$lang['subscr_m_receive'] = 'Al';
$lang['authmodfailed'] = 'Yanlış kullanıcı onaylama ayarı. Lütfen Wiki yöneticisine bildiriniz.';
$lang['authtempfail'] = 'Kullanıcı doğrulama geçici olarak yapılamıyor. Eğer bu durum devam ederse lütfen Wiki yöneticine haber veriniz.';
+$lang['authpwdexpire'] = 'Şifreniz %d gün sonra geçersiz hale gelecek, yakın bir zamanda değiştirmelisiniz.';
$lang['i_chooselang'] = 'Dili seçiniz';
$lang['i_installer'] = 'Dokuwiki Kurulum Sihirbazı';
$lang['i_wikiname'] = 'Wiki Adı';
@@ -225,11 +247,38 @@ $lang['i_confexists'] = '<code>%s</code> zaten var';
$lang['i_writeerr'] = '<code>%s</code> oluşturulamadı. Dosya/Klasör izin ayarlarını gözden geçirip dosyayı elle oluşturmalısınız.';
$lang['i_badhash'] = 'dokuwiki.php tanınamadı ya da değiştirilmiş (hash=<code>%s</code>)';
$lang['i_badval'] = '<code>%s</code> - Yanlış veya boş değer';
-$lang['i_success'] = 'Kurulum başarıyla tamamlandı. Şimdi install.php dosyasını silebilirsiniz. <a href="doku.php?id=wiki:welcome">Yeni DokuWikiniz</a>i kullanabilirsiniz.';
-$lang['i_failure'] = 'Ayar dosyalarını yazarken bazı hatalar oluştu. <a href="doku.php?id=wiki:welcome">Yeni DokuWikiniz</a>i kullanmadan önce bu hatalarınızı elle düzeltmeniz gerekebilir.';
+$lang['i_success'] = 'Kurulum başarıyla tamamlandı. Şimdi install.php dosyasını silebilirsiniz. <a href="doku.php">Yeni DokuWikiniz</a>i kullanabilirsiniz.';
+$lang['i_failure'] = 'Ayar dosyalarını yazarken bazı hatalar oluştu. <a href="doku.php">Yeni DokuWikiniz</a>i kullanmadan önce bu hatalarınızı elle düzeltmeniz gerekebilir.';
$lang['i_policy'] = 'İlk ACL ayarı';
$lang['i_pol0'] = 'Tamamen Açık Wiki (herkes okuyabilir, yazabilir ve dosya yükleyebilir)';
$lang['i_pol1'] = 'Açık Wiki (herkes okuyabilir, ancak sadece üye olanlar yazabilir ve dosya yükleyebilir)';
$lang['i_pol2'] = 'Kapalı Wiki (sadece üye olanlar okuyabilir, yazabilir ve dosya yükleyebilir)';
$lang['i_retry'] = 'Tekrar Dene';
+$lang['i_license'] = 'Lütfen içeriği hangi lisans altında yayınlamak istediğniizi belirtin:';
$lang['recent_global'] = '<b>%s</b> namespace\'i içerisinde yapılan değişiklikleri görüntülemektesiniz. Wiki\'deki tüm değişiklikleri de <a href="%s">bu adresten</a> görebilirsiniz. ';
+$lang['years'] = '%d yıl önce';
+$lang['months'] = '%d ay önce';
+$lang['weeks'] = '%d hafta önce';
+$lang['days'] = '%d gün önce';
+$lang['hours'] = '%d saat önce';
+$lang['minutes'] = '%d dakika önce';
+$lang['seconds'] = '%d saniye önce';
+$lang['wordblock'] = 'Değişikliğiniz kaydedilmedi çünkü istenmeyen mesaj içeriyor (spam).';
+$lang['media_uploadtab'] = 'Karşıya yükle';
+$lang['media_searchtab'] = 'Ara';
+$lang['media_file'] = 'Dosya';
+$lang['media_viewtab'] = 'Görünüm';
+$lang['media_edittab'] = 'Düzenle';
+$lang['media_historytab'] = 'Geçmiş';
+$lang['media_list_thumbs'] = 'Küçük resimler';
+$lang['media_list_rows'] = 'Satırlar';
+$lang['media_sort_name'] = 'İsim';
+$lang['media_sort_date'] = 'Tarih';
+$lang['media_files'] = '%s deki dosyalar';
+$lang['media_upload'] = '%s dizinine yükle';
+$lang['media_search'] = '%s dizininde ara';
+$lang['media_view'] = '%s';
+$lang['media_edit'] = 'Düzenle %s';
+$lang['media_history'] = 'Geçmiş %s';
+$lang['media_perm_upload'] = 'Üzgünüm, karşıya dosya yükleme yetkiniz yok.';
+$lang['media_restore'] = 'Bu sürümü eski haline getir';
diff --git a/inc/lang/tr/resetpwd.txt b/inc/lang/tr/resetpwd.txt
new file mode 100644
index 000000000..1ed758693
--- /dev/null
+++ b/inc/lang/tr/resetpwd.txt
@@ -0,0 +1,3 @@
+ ====== Yeni şifre belirle ======
+
+Lütfen bu wiki hesabınız için yeni bir şifre belirleyin. \ No newline at end of file
diff --git a/inc/lang/zh-tw/adminplugins.txt b/inc/lang/zh-tw/adminplugins.txt
index fb1999269..6d21ac2cd 100644
--- a/inc/lang/zh-tw/adminplugins.txt
+++ b/inc/lang/zh-tw/adminplugins.txt
@@ -1 +1 @@
-===== 外加插件 ===== \ No newline at end of file
+===== 附加元件 ===== \ No newline at end of file
diff --git a/inc/lang/zh-tw/backlinks.txt b/inc/lang/zh-tw/backlinks.txt
index 5b36728e7..6a8bf8896 100644
--- a/inc/lang/zh-tw/backlinks.txt
+++ b/inc/lang/zh-tw/backlinks.txt
@@ -1,4 +1,4 @@
====== 反向連結 ======
-這裡是引用、連結到目前頁面的頁面清單。
+這是引用、連結到目前頁面的頁面清單。
diff --git a/inc/lang/zh-tw/diff.txt b/inc/lang/zh-tw/diff.txt
index 17fad7ba0..e2c05001f 100644
--- a/inc/lang/zh-tw/diff.txt
+++ b/inc/lang/zh-tw/diff.txt
@@ -1,3 +1,3 @@
====== 差異處 ======
-這裏顯示二個版本的差異處。 \ No newline at end of file
+這裏顯示兩個版本的差異處。 \ No newline at end of file
diff --git a/inc/lang/zh-tw/edit.txt b/inc/lang/zh-tw/edit.txt
index bbe5bb8ed..f6b74794f 100644
--- a/inc/lang/zh-tw/edit.txt
+++ b/inc/lang/zh-tw/edit.txt
@@ -1 +1 @@
-編輯本頁並按下''儲存''即可。可在[[wiki:syntax|維基語法]]找到語法說明。請只在能讓本文品質「**更好**」時才編輯。如果只是要測試,請使用 [[playground:playground|遊樂場]]。
+編輯本頁後,請按下「儲存」按鈕。若要參看語法說明,請到[[wiki:syntax|語法]]頁。請只在能讓本文品質**更好**時才編輯。如果只是要測試,請移玉步至 [[playground:playground|遊樂場]]。 \ No newline at end of file
diff --git a/inc/lang/zh-tw/editrev.txt b/inc/lang/zh-tw/editrev.txt
index 96f9a7c6a..98a800ab1 100644
--- a/inc/lang/zh-tw/editrev.txt
+++ b/inc/lang/zh-tw/editrev.txt
@@ -1,2 +1,2 @@
-**您目前載入的是本份文件的舊版!** 您如果存檔,這些資料就會被存成另一份。
+**您目前載入的是本份文件的舊版!** 您如果存檔,這些舊版資料就會變成最新版本。
----
diff --git a/inc/lang/zh-tw/index.txt b/inc/lang/zh-tw/index.txt
index bba277041..1b89e0c5d 100644
--- a/inc/lang/zh-tw/index.txt
+++ b/inc/lang/zh-tw/index.txt
@@ -1,3 +1,3 @@
====== 站台地圖 ======
-這個站台地圖列出了所有允許的頁面,依 [[doku>namespaces|分類空間]] 排序。 \ No newline at end of file
+這個站台地圖列出了所有允許的頁面,依 [[doku>namespaces|分類名稱]] 排序。 \ No newline at end of file
diff --git a/inc/lang/zh-tw/install.html b/inc/lang/zh-tw/install.html
index 2a8b1aac3..9a0d1dcd9 100644
--- a/inc/lang/zh-tw/install.html
+++ b/inc/lang/zh-tw/install.html
@@ -1,8 +1,8 @@
<p>本頁面旨在幫助您完成第一次安装和設定 <a href="http://dokuwiki.org">Dokuwiki</a>。關於安裝工具的更多訊息請參閱 <a href="http://dokuwiki.org/installer">官方文檔頁面</a>。</p>
-<p>DokuWiki 使用普通檔案儲存維基頁面以及與頁面相關的訊息(例如:圖像,搜尋索引,修訂記錄等)。為了正常運作,DokuWiki <strong>必須</strong> 擁有針對那些路徑和檔案的寫入權限。本安裝工具無法設定目錄權限,這通常要透過命令行、FTP 或您主機上的控制台(如cPanel)進行。</p>
+<p>DokuWiki 使用普通檔案來儲存 wiki 頁面,以及與頁面相關的訊息(例如:圖像、搜尋索引、修訂記錄等)。為了正常運作,DokuWiki <strong>必須</strong> 擁有針對那些路徑和檔案的寫入權限。本安裝工具無法設定目錄權限,這通常要透過命令行、FTP 或您主機上的控制台(如cPanel)進行。</p>
-<p>本安裝工具將設定您的 DokuWiki 用於 <abbr title="訪問控制列表">ACL</abbr> 的設定檔,它能讓管理員登入並使用「管理」功能來安裝插件、管理用户、管理訪問權限和其他設定設定。它並不是 DokuWiki 正常運作所必須,但安裝之後將更方便管理。</p>
+<p>本安裝工具將設定您的 DokuWiki 用於 <abbr title="訪問控制列表">ACL</abbr> 的設定檔,它能讓管理員登入並使用「管理」功能來安裝附加元件、管理使用者、管理訪問權限和其他設定設定。它並不是 DokuWiki 正常運作所必須,但安裝之後將更方便管理。</p>
-<p>有經驗的用戶或有特殊需求的用戶請參閱更詳細的 <a href="http://dokuwiki.org/install">安裝指南</a>
+<p>有經驗的或有特殊需求的使用者,請參閱更詳細的 <a href="http://dokuwiki.org/install">安裝指南</a>
和 <a href="http://dokuwiki.org/config">設定</a>。</p> \ No newline at end of file
diff --git a/inc/lang/zh-tw/lang.php b/inc/lang/zh-tw/lang.php
index 9f380acb5..ddb35617e 100644
--- a/inc/lang/zh-tw/lang.php
+++ b/inc/lang/zh-tw/lang.php
@@ -11,6 +11,7 @@
* @author Danny Lin
* @author Shuo-Ting Jian <shoting@gmail.com>
* @author syaoranhinata@gmail.com
+ * @author Ichirou Uchiki <syaoranhinata@gmail.com>
*/
$lang['encoding'] = 'utf-8';
$lang['direction'] = 'ltr';
@@ -19,7 +20,7 @@ $lang['doublequoteclosing'] = '”';
$lang['singlequoteopening'] = '‘';
$lang['singlequoteclosing'] = '’';
$lang['apostrophe'] = '’';
-$lang['btn_edit'] = '編修本頁';
+$lang['btn_edit'] = '編輯本頁';
$lang['btn_source'] = '顯示原始碼';
$lang['btn_show'] = '顯示頁面';
$lang['btn_create'] = '建立此頁';
@@ -34,7 +35,7 @@ $lang['btn_recent'] = '最近更新';
$lang['btn_upload'] = '上傳';
$lang['btn_cancel'] = '取消';
$lang['btn_index'] = '站台地圖';
-$lang['btn_secedit'] = '改這段';
+$lang['btn_secedit'] = '編輯此段';
$lang['btn_login'] = '登入';
$lang['btn_logout'] = '登出';
$lang['btn_admin'] = '管理選單';
@@ -61,40 +62,40 @@ $lang['newpass'] = '新密碼';
$lang['oldpass'] = '目前密碼';
$lang['passchk'] = '確認密碼';
$lang['remember'] = '記住帳號密碼';
-$lang['fullname'] = '真實姓名';
+$lang['fullname'] = '姓名';
$lang['email'] = '電郵';
$lang['profile'] = '使用者個人資料';
$lang['badlogin'] = '很抱歉,您的使用者名稱或密碼可能有錯誤。';
$lang['minoredit'] = '小修改';
$lang['draftdate'] = '草稿已自動存檔於';
-$lang['nosecedit'] = '頁面在這之間已被修改,過時的區段資料已載入全頁取代。';
+$lang['nosecedit'] = '在您編輯期間,其他使用者修改過本頁面。區段資料已逾時,因此系統載入了全頁,以取代之。';
$lang['regmissing'] = '很抱歉,所有欄位都要填寫。';
-$lang['reguexists'] = '很抱歉,本帳號已被註冊。';
-$lang['regsuccess'] = '使用者已建立,密碼已寄發至該 email。';
-$lang['regsuccess2'] = '使用者已建立。';
+$lang['reguexists'] = '很抱歉,有人已使用了這個帳號。';
+$lang['regsuccess'] = '使用者帳號已建立,密碼已寄發至該電郵。';
+$lang['regsuccess2'] = '使用者帳號已建立。';
$lang['regmailfail'] = '寄出密碼信似乎發生錯誤,請跟管理員聯絡!';
-$lang['regbadmail'] = '您輸入的 email 似乎不對,如果您認為是正確的,請與管理員聯絡。';
+$lang['regbadmail'] = '您輸入的電郵似乎不對,如果您認為是正確的,請與管理員聯絡。';
$lang['regbadpass'] = '兩次輸入的密碼不一致,請再試一次。';
$lang['regpwmail'] = '您的 DokuWiki 帳號密碼';
$lang['reghere'] = '您還沒有帳號嗎?註冊一個吧。';
-$lang['profna'] = '本維基不開放修改個人資料。';
-$lang['profnochange'] = '未做任何變更。';
+$lang['profna'] = '在本 wiki 上,不能修改個人資料。';
+$lang['profnochange'] = '並未作任何變更。';
$lang['profnoempty'] = '帳號或電郵地址不可空白!';
-$lang['profchanged'] = '個人資料已成功更新。';
+$lang['profchanged'] = '個人資料已更新。';
$lang['pwdforget'] = '忘記密碼了?索取新密碼!';
-$lang['resendna'] = '本維基不開放重寄密碼。';
+$lang['resendna'] = '本 wiki 並不支援重寄密碼。';
$lang['resendpwd'] = '設定新密碼供';
$lang['resendpwdmissing'] = '抱歉,您必須填寫所有欄位。';
$lang['resendpwdnouser'] = '抱歉,資料庫內找不到這個使用者。';
$lang['resendpwdbadauth'] = '抱歉,認證碼無效。請確認您使用了完整的確認連結。';
$lang['resendpwdconfirm'] = '確認連結已通過郵件發送給您了。';
$lang['resendpwdsuccess'] = '您的新密碼已寄出。';
-$lang['license'] = '若未特別註明,此維基上的內容都是採用以下授權方式:';
+$lang['license'] = '若無特別註明,本 wiki 上的內容都是採用以下授權方式:';
$lang['licenseok'] = '注意:編輯此頁面表示您已同意以下的授權方式:';
$lang['searchmedia'] = '搜尋檔名:';
$lang['searchmedia_in'] = '在 %s 裏搜尋';
$lang['txt_upload'] = '請選擇要上傳的檔案';
-$lang['txt_filename'] = '請輸入要存在維基內的檔案名稱 (非必要)';
+$lang['txt_filename'] = '請輸入要上傳至本 wiki 的檔案名稱 (非必要)';
$lang['txt_overwrt'] = '是否要覆蓋原有檔案';
$lang['maxuploadsize'] = '每個上傳檔案不可大於 %s 。';
$lang['lockedby'] = '目前已被下列人員鎖定';
@@ -135,7 +136,7 @@ $lang['js']['media_diff'] = '檢視差異:';
$lang['js']['media_diff_both'] = '並排';
$lang['js']['media_diff_opacity'] = '重疊';
$lang['js']['media_diff_portions'] = '滑動';
-$lang['js']['media_select'] = '選擇檔案…';
+$lang['js']['media_select'] = '選擇檔案……';
$lang['js']['media_upload_btn'] = '上傳';
$lang['js']['media_done_btn'] = '完成';
$lang['js']['media_drop'] = '拖拉檔案到此上傳';
@@ -145,27 +146,27 @@ $lang['rssfailed'] = '擷取 RSS 饋送檔時發生錯誤:';
$lang['nothingfound'] = '沒找到任何結果。';
$lang['mediaselect'] = '媒體檔案';
$lang['fileupload'] = '上傳媒體檔案';
-$lang['uploadsucc'] = '上傳成功';
-$lang['uploadfail'] = '上傳失敗。似乎是權限錯誤?';
+$lang['uploadsucc'] = '已上傳';
+$lang['uploadfail'] = '無法上傳。是否因權限錯誤?';
$lang['uploadwrong'] = '拒絕上傳。這個副檔名被禁止了!';
$lang['uploadexist'] = '檔案已存在,未處理。';
$lang['uploadbadcontent'] = '上傳檔案的內容不符合 %s 檔的副檔名。';
-$lang['uploadspam'] = '這次的上傳被垃圾訊息黑名單阻檔了。';
-$lang['uploadxss'] = '這次的上傳因可能的惡意的內容而被阻檔。';
-$lang['uploadsize'] = '上傳的檔案太大了 (最大:%s)';
+$lang['uploadspam'] = '是次上傳被垃圾訊息黑名單阻檔了。';
+$lang['uploadxss'] = '因可能含有惡意內容,是次上傳已被阻檔。';
+$lang['uploadsize'] = '上傳的檔案太大了 (最大為:%s)';
$lang['deletesucc'] = '檔案 "%s" 已刪除。';
$lang['deletefail'] = '檔案 "%s" 無法刪除,請檢查權限定。';
-$lang['mediainuse'] = '檔案 "%s" 未刪除,因為它正被使用。';
-$lang['namespaces'] = '分類空間';
+$lang['mediainuse'] = '檔案 "%s" 仍在使用,並未刪除。';
+$lang['namespaces'] = '分類名稱';
$lang['mediafiles'] = '可用的檔案有';
$lang['accessdenied'] = '您不可以檢視此頁面。';
$lang['mediausage'] = '使用以下的語法來連結此檔案:';
$lang['mediaview'] = '檢視原始檔案';
$lang['mediaroot'] = 'root';
-$lang['mediaupload'] = '上傳檔案至目前的分類空間。要建立子分類空間,將其名稱加在「上傳並重命名為」檔案名的前面,並用英文冒號隔開。';
+$lang['mediaupload'] = '上傳檔案至目前分類名稱之下。要建立子分類名稱,請將其名稱加在「上傳並重命名為」檔案名的前面,並用英文冒號隔開。';
$lang['mediaextchange'] = '檔案類型已由 .%s 變更為 .%s !';
$lang['reference'] = '引用到本頁的,合計有';
-$lang['ref_inuse'] = '此檔案無法刪除,因為它正被以下頁面使用:';
+$lang['ref_inuse'] = '此檔案無法刪除,因以下頁面正在使用它:';
$lang['ref_hidden'] = '一些參考內容位於您沒有讀取權限的頁面中';
$lang['hits'] = '個符合';
$lang['quickhits'] = '符合的頁面名稱';
@@ -175,12 +176,12 @@ $lang['yours'] = '您的版本';
$lang['diff'] = '顯示與目前版本的差異';
$lang['diff2'] = '顯示選擇版本間的差異';
$lang['difflink'] = '連向這個比對檢視';
-$lang['diff_type'] = '檢視差異:';
+$lang['diff_type'] = '檢視差異:';
$lang['diff_inline'] = '行內';
$lang['diff_side'] = '並排';
$lang['line'] = '行';
$lang['breadcrumb'] = '足跡';
-$lang['youarehere'] = '您在這裡';
+$lang['youarehere'] = '您在這裏';
$lang['lastmod'] = '上一次變更';
$lang['by'] = '由';
$lang['deleted'] = '移除';
@@ -188,17 +189,17 @@ $lang['created'] = '建立';
$lang['restored'] = '恢復為舊版';
$lang['external_edit'] = '外部編輯';
$lang['summary'] = '編輯摘要';
-$lang['noflash'] = '顯示此內容需要 <a href="http://www.adobe.com/products/flashplayer/">Adobe Flash Plugin</a>';
+$lang['noflash'] = '顯示此內容需要 <a href="http://www.adobe.com/products/flashplayer/">Adobe Flash 附加元件</a>。';
$lang['download'] = '下載程式碼片段';
$lang['tools'] = '工具';
-$lang['user_tools'] = '用戶工具';
+$lang['user_tools'] = '使用者工具';
$lang['site_tools'] = '網站工具';
$lang['page_tools'] = '頁面工具';
$lang['skip_to_content'] = '跳至內容';
$lang['sidebar'] = '側欄';
$lang['mail_newpage'] = '增加的頁面:';
$lang['mail_changed'] = '變更的頁面:';
-$lang['mail_subscribe_list'] = '分類空間中更動的頁面:';
+$lang['mail_subscribe_list'] = '分類名稱中變更的頁面:';
$lang['mail_new_user'] = '新使用者:';
$lang['mail_upload'] = '已上傳檔案:';
$lang['changes_type'] = '檢視最近更新類型';
@@ -229,10 +230,10 @@ $lang['qb_media'] = '加入圖片或檔案';
$lang['qb_sig'] = '插入簽名';
$lang['qb_smileys'] = '表情符號';
$lang['qb_chars'] = '特殊字元';
-$lang['upperns'] = '前往父分類空間';
+$lang['upperns'] = '前往父分類名稱';
$lang['admin_register'] = '新增使用者';
$lang['metaedit'] = '編輯後設資料';
-$lang['metasaveerr'] = '後設資料寫入失敗';
+$lang['metasaveerr'] = '後設資料無法寫入';
$lang['metasaveok'] = '後設資料已儲存';
$lang['img_backto'] = '回上一頁';
$lang['img_title'] = '標題';
@@ -253,9 +254,9 @@ $lang['subscr_subscribe_error'] = '將 %s 加入至 %s 的訂閱列表時發生
$lang['subscr_subscribe_noaddress'] = '沒有與您登入相關的地址,無法將您加入訂閱列表';
$lang['subscr_unsubscribe_success'] = '已將 %s 移除自 %s 的訂閱列表';
$lang['subscr_unsubscribe_error'] = '將 %s 移除自 %s 的訂閱列表時發生錯誤';
-$lang['subscr_already_subscribed'] = '%s 已經被 %s 訂閱了';
-$lang['subscr_not_subscribed'] = '%s 尚未被 %s 訂閱';
-$lang['subscr_m_not_subscribed'] = '您尚未訂閱目前的頁面或分類空間。';
+$lang['subscr_already_subscribed'] = '%s 已經獲 %s 訂閱了';
+$lang['subscr_not_subscribed'] = '%s 尚未獲 %s 訂閱';
+$lang['subscr_m_not_subscribed'] = '您尚未訂閱目前的頁面或分類名稱。';
$lang['subscr_m_new_header'] = '加入訂閱';
$lang['subscr_m_current_header'] = '目前訂閱';
$lang['subscr_m_unsubscribe'] = '取消訂閱';
@@ -264,34 +265,34 @@ $lang['subscr_m_receive'] = '接收';
$lang['subscr_style_every'] = '每次更改都發送信件';
$lang['subscr_style_digest'] = '對每個頁面發送更改的摘要信件 (每 %.2f 天)';
$lang['subscr_style_list'] = '自上次發信以來更改的頁面的列表 (每 %.2f 天)';
-$lang['authmodfailed'] = '帳號認證的設定不正確,請通知該維基管理員。';
-$lang['authtempfail'] = '帳號認證目前暫不提供,若本狀況持續發生的話,請通知該維基管理員。';
+$lang['authmodfailed'] = '帳號認證的設定不正確,請通知該本 wiki 管理員。';
+$lang['authtempfail'] = '帳號認證目前暫不提供。若本狀況持續,請通知本 wiki 管理員。';
$lang['authpwdexpire'] = '您的密碼將在 %d 天內到期,請馬上更換新密碼。';
$lang['i_chooselang'] = '選擇您的語系';
$lang['i_installer'] = 'DokuWiki 安裝工具';
-$lang['i_wikiname'] = '維基名稱';
+$lang['i_wikiname'] = '本 wiki 的名稱';
$lang['i_enableacl'] = '啟用 ACL (建議)';
-$lang['i_superuser'] = '超級用戶';
+$lang['i_superuser'] = '超級使用者';
$lang['i_problems'] = '安裝程式發現如下的問題。您必須修正它們才能繼續。';
$lang['i_modified'] = '出於安全考量,本腳本只能用於安裝全新且未修改的 Dokuwiki。
您可以重新解壓下載的封包或查閱完整的<a href=\"http://dokuwiki.org/install\">Dokuwiki 安裝指南</a>';
$lang['i_funcna'] = 'PHP 函數 <code>%s</code> 無法使用。也許您的主機供應者基於某些理由停用了它?';
$lang['i_phpver'] = '您的 PHP 版本 <code>%s</code> 比需要的版本 <code>%s</code> 還低。您必須更新您的PHP。';
-$lang['i_permfail'] = '<code>%s</code> 無法被 DokuWiki 寫入。您必須修正該目錄的權限!';
+$lang['i_permfail'] = '<code>%s</code> 無法經由 DokuWiki 寫入。您必須修正該目錄的權限!';
$lang['i_confexists'] = '<code>%s</code> 已經存在';
$lang['i_writeerr'] = '無法建立 <code>%s</code>。您必須檢查目錄/檔案的權限並手動建立該檔案。';
-$lang['i_badhash'] = '無法辨識或被變更的 dokuwiki.php (hash=<code>%s</code>)';
-$lang['i_badval'] = '<code>%s</code> - 非法或空白的值';
-$lang['i_success'] = '設定已成功完成。您現在可以刪除 install.php 檔案。繼續到
-<a href="doku.php?id=wiki:welcome">您的新 DokuWiki</a>.';
-$lang['i_failure'] = '寫入設定檔時發生了一些錯誤。您必須在使用<a href="doku.php?id=wiki:welcome">您的新 Dokuwiki</a> 之前手動修正它們。';
+$lang['i_badhash'] = '無法辨識或已遭修改的 dokuwiki.php (hash=<code>%s</code>)';
+$lang['i_badval'] = '<code>%s</code> —— 非法或空白的值';
+$lang['i_success'] = '設定已完成。您現在可以刪除 install.php 檔案。繼續到
+<a href="doku.php">您的新 DokuWiki</a>.';
+$lang['i_failure'] = '寫入設定檔時發生了一些錯誤。您必須在使用<a href="doku.php">您的新 Dokuwiki</a> 之前手動修正它們。';
$lang['i_policy'] = '初步的 ACL 政策';
-$lang['i_pol0'] = '開放的維基 (任何人可讀取、寫入、上傳)';
-$lang['i_pol1'] = '公開的維基 (任何人可讀取,註冊使用者可寫入與上傳)';
-$lang['i_pol2'] = '封閉的維基 (只有註冊使用者可讀取、寫入、上傳)';
+$lang['i_pol0'] = '開放的 wiki (任何人可讀取、寫入、上傳)';
+$lang['i_pol1'] = '公開的 wiki (任何人可讀取,註冊使用者可寫入與上傳)';
+$lang['i_pol2'] = '封閉的 wiki (只有註冊使用者可讀取、寫入、上傳)';
$lang['i_retry'] = '重試';
$lang['i_license'] = '請選擇您想要的內容發布許可協議:';
-$lang['recent_global'] = '您正在閱讀分類空間: <b>%s</b> 中的變更。您亦可觀看整個維基的<a href="%s">最近更新</a>。';
+$lang['recent_global'] = '您正在閱讀分類名稱: <b>%s</b> 中的變更。您亦可觀看本 wiki <a href="%s">所有的最近更新</a>。';
$lang['years'] = '%d 年前';
$lang['months'] = '%d 個月前';
$lang['weeks'] = '%d 週前';
@@ -299,7 +300,7 @@ $lang['days'] = '%d 天前';
$lang['hours'] = '%d 個小時前';
$lang['minutes'] = '%d 分鐘前';
$lang['seconds'] = '%s 秒鐘前';
-$lang['wordblock'] = '您的更改沒有被儲存,因为它包含被阻擋的文字 (垃圾訊息)。';
+$lang['wordblock'] = '無法儲存您的更改,因為它含有受阻擋的文字 (垃圾訊息)。';
$lang['media_uploadtab'] = '上傳';
$lang['media_searchtab'] = '搜尋';
$lang['media_file'] = '檔案';
@@ -310,7 +311,7 @@ $lang['media_list_thumbs'] = '縮圖';
$lang['media_list_rows'] = '列表';
$lang['media_sort_name'] = '名稱';
$lang['media_sort_date'] = '日期';
-$lang['media_namespaces'] = '選擇分類空間';
+$lang['media_namespaces'] = '選擇分類名稱';
$lang['media_files'] = '在 %s 中的檔案';
$lang['media_upload'] = '上傳至 %s';
$lang['media_search'] = '在 %s 中搜尋';
diff --git a/inc/lang/zh-tw/locked.txt b/inc/lang/zh-tw/locked.txt
index e13fdc890..819e59e2f 100644
--- a/inc/lang/zh-tw/locked.txt
+++ b/inc/lang/zh-tw/locked.txt
@@ -1,3 +1,3 @@
====== 頁面鎖定 ======
-本頁目前正由其他使用者編修中,您必須等他完成編輯或等鎖定時間過去。
+其他使用者正在編輯本頁,您必須等他完成編輯或等鎖定時間過去。
diff --git a/inc/lang/zh-tw/login.txt b/inc/lang/zh-tw/login.txt
index 058cc5712..b82f08a20 100644
--- a/inc/lang/zh-tw/login.txt
+++ b/inc/lang/zh-tw/login.txt
@@ -1,5 +1,4 @@
====== 登入 ======
-您尚未登入,請輸入您的使用者名稱和密碼。 另外,瀏覽器需要打開 cookies 設定以進行登入。
-
+您尚未登入,請輸入您的使用者名稱和密碼。 另外,瀏覽器需要啟用 cookies 以登入本 wiki。
diff --git a/inc/lang/zh-tw/mailtext.txt b/inc/lang/zh-tw/mailtext.txt
index 3b6ece377..2a402c2fb 100644
--- a/inc/lang/zh-tw/mailtext.txt
+++ b/inc/lang/zh-tw/mailtext.txt
@@ -2,7 +2,7 @@
日期 : @DATE@
瀏覽器 : @BROWSER@
-IP位址 : @IPADDRESS@
+IP 位址 : @IPADDRESS@
主機名稱 : @HOSTNAME@
舊版本 : @OLDPAGE@
新版本 : @NEWPAGE@
diff --git a/inc/lang/zh-tw/norev.txt b/inc/lang/zh-tw/norev.txt
index bd1c7a623..2a32ba6a8 100644
--- a/inc/lang/zh-tw/norev.txt
+++ b/inc/lang/zh-tw/norev.txt
@@ -1,3 +1,3 @@
====== 無此版本 ======
-該版本的文件不存在。請用 「舊版」按鈕檢視該文件所有舊版本清單。 \ No newline at end of file
+該版本的文件不存在。請用「舊版」按鈕檢視該文件所有舊版本清單。 \ No newline at end of file
diff --git a/inc/lang/zh-tw/preview.txt b/inc/lang/zh-tw/preview.txt
index c68f94819..95d4b10e1 100644
--- a/inc/lang/zh-tw/preview.txt
+++ b/inc/lang/zh-tw/preview.txt
@@ -1,4 +1,4 @@
====== 預覽 ======
-以下是預覽該文件的狀態。請記住:**它還沒被儲存喔**!
+以下是該文件的預覽。請記住:**您還未儲存它**!
diff --git a/inc/lang/zh-tw/pwconfirm.txt b/inc/lang/zh-tw/pwconfirm.txt
index 994367980..fb8a5cb82 100644
--- a/inc/lang/zh-tw/pwconfirm.txt
+++ b/inc/lang/zh-tw/pwconfirm.txt
@@ -1,13 +1,13 @@
@FULLNAME@ 您好!
-有人為您在 @DOKUWIKIURL@ 註冊的使用者 @TITLE@ 請求新密碼
+感謝您在 @TITLE@ ( @DOKUWIKIURL@ ) 註冊了使用者帳號。我們收到請求,希望能允許此帳號使用新密碼。
-如果您沒有請求新密碼,請忽略這封郵件。
+如果您沒有發送此請求,請忽略這封郵件。
-要確認使用新密碼,請拜訪以下的連結。
+若您真的要使用新密碼,請拜訪以下的連結。
@CONFIRM@
--
本信件由以下 DokuWiki 站台產生:
-@DOKUWIKIURL@
+@DOKUWIKIURL@ \ No newline at end of file
diff --git a/inc/lang/zh-tw/read.txt b/inc/lang/zh-tw/read.txt
index a8305611f..4a472cd0d 100644
--- a/inc/lang/zh-tw/read.txt
+++ b/inc/lang/zh-tw/read.txt
@@ -1 +1 @@
-本頁是唯讀的,您可以看到原始碼,但不能更動它。您如果覺得這是誤判,請詢問管理員。 \ No newline at end of file
+本頁是唯讀的,您可以看到原始碼,但不能更動它。您如果覺得它不應被鎖上,請詢問管理員。 \ No newline at end of file
diff --git a/inc/lang/zh-tw/register.txt b/inc/lang/zh-tw/register.txt
index 0da401ccd..3ff68dee3 100644
--- a/inc/lang/zh-tw/register.txt
+++ b/inc/lang/zh-tw/register.txt
@@ -1,4 +1,3 @@
====== 註冊新使用者 ======
-填寫以下資料以註冊維基帳號,請確定您提供的是**合法的 email 地址**-如果這裡沒要求您填寫密碼,您的新密碼會自動產生並寄送到該地址。登錄名稱必須是合法的[[doku>pagename|頁面名稱]]。
-
+若要註冊本 wiki 的帳號,請填寫下列資料。請確定您提供的是**合法的電郵地址**。如果您不必填寫密碼,系統就會為您自動產生登入密碼,並寄送到該電郵地址。登錄名稱須符合正確[[doku>pagename|頁面名稱]]之條件。
diff --git a/inc/lang/zh-tw/registermail.txt b/inc/lang/zh-tw/registermail.txt
index 489e3f9d3..fb4951d60 100644
--- a/inc/lang/zh-tw/registermail.txt
+++ b/inc/lang/zh-tw/registermail.txt
@@ -2,11 +2,11 @@
帳號 : @NEWUSER@
姓名 : @NEWNAME@
-E-mail : @NEWEMAIL@
+電郵 : @NEWEMAIL@
日期 : @DATE@
瀏覽器 : @BROWSER@
-IP位址 : @IPADDRESS@
+IP 位址 : @IPADDRESS@
主機名稱 : @HOSTNAME@
--
diff --git a/inc/lang/zh-tw/resendpwd.txt b/inc/lang/zh-tw/resendpwd.txt
index 3dbfad750..46078a348 100644
--- a/inc/lang/zh-tw/resendpwd.txt
+++ b/inc/lang/zh-tw/resendpwd.txt
@@ -1,3 +1,3 @@
====== 寄送新密碼 ======
-請在以下欄位輸入您的帳號,新密碼將會寄送到您註冊時填寫的 email 地址。
+請在以下欄位輸入您的帳號,新密碼將會寄送到您註冊時填寫的電郵地址。 \ No newline at end of file
diff --git a/inc/lang/zh-tw/revisions.txt b/inc/lang/zh-tw/revisions.txt
index 479705b95..4818839ac 100644
--- a/inc/lang/zh-tw/revisions.txt
+++ b/inc/lang/zh-tw/revisions.txt
@@ -1,3 +1,3 @@
====== 舊版 ======
-以下是該文件的舊版本。如要恢復成某個舊版次,就點下它,然後按「編修本頁」,並存檔起來就可以了。
+以下是該文件的舊版本。如要恢復成某個舊版次,就點下它,然後按「編輯本頁」,並存檔起來就可以了。
diff --git a/inc/lang/zh-tw/stopwords.txt b/inc/lang/zh-tw/stopwords.txt
index 55b67ed16..e549250bd 100644
--- a/inc/lang/zh-tw/stopwords.txt
+++ b/inc/lang/zh-tw/stopwords.txt
@@ -1,9 +1,9 @@
-# 這檔是製作索引檔(index)時不要列入的關鍵字,格式為每字(詞)就使用一行。
-# 在修改時,請注意要用 UNIX 格式的換行符號(newline)處理,而非 DOS 的 CR-LR 喔
-# (如果在 MS Windows 環境使用的話,可使用 vim win32版 或 UltraEdit或其他類似編輯器修改)
+# 本清單列出製作索引檔 (index) 時不要列入的關鍵字,格式為每字 (詞) 佔一行。
+# 在修改本清單時,請注意要用 UNIX 格式的換行符號 (newline) 處理,而非 DOS 的 CR-LR 。
+# (如果在 MS Windows 環境使用的話,可使用 vim win32 版、 UltraEdit 或其他類似編輯器修改。)
#
-# 還有,不必把小於 3 個字元(英數字元)都包括進來。
-# 目前本清單的內容是以 http://www.ranks.nl/stopwords/ 為基礎而發展的。
+# 還有,不必把小於 3 個字元 (英數字元) 都包括進來。
+# 目前本清單的內容是以 http://www.ranks.nl/stopwords/ 為基礎,發展而成的。
about
are
and
diff --git a/inc/lang/zh-tw/subscr_digest.txt b/inc/lang/zh-tw/subscr_digest.txt
index a0f2be73e..a17a0551d 100644
--- a/inc/lang/zh-tw/subscr_digest.txt
+++ b/inc/lang/zh-tw/subscr_digest.txt
@@ -1,6 +1,6 @@
您好!
-維基 @TITLE@ 的頁面 @PAGE@ 已更改。
+本 wiki ( @TITLE@ ) 的頁面 @PAGE@ 已更改。
更改內容如下:
--------------------------------------------------------
@@ -10,9 +10,9 @@
舊版本:@OLDPAGE@
新版本:@NEWPAGE@
-要取消頁面提醒,請登入維基 @DOKUWIKIURL@
+要取消頁面提醒,請登入本 wiki @DOKUWIKIURL@
然後拜訪 @SUBSCRIBE@
-並取消訂閱頁面或分類空間的更改。
+並取消訂閱頁面或分類名稱的更改。
--
本信件由以下 DokuWiki 站台產生:
diff --git a/inc/lang/zh-tw/subscr_form.txt b/inc/lang/zh-tw/subscr_form.txt
index 394d4cbad..ba3f16113 100644
--- a/inc/lang/zh-tw/subscr_form.txt
+++ b/inc/lang/zh-tw/subscr_form.txt
@@ -1,3 +1,3 @@
====== 訂閱管理 ======
-這個頁面允許您管理在目前頁面和分類空間的訂閱。 \ No newline at end of file
+在此頁裏,您可以管理在目前頁面及分類名稱之訂閱。 \ No newline at end of file
diff --git a/inc/lang/zh-tw/subscr_list.txt b/inc/lang/zh-tw/subscr_list.txt
index 078eae63e..ea82dd35d 100644
--- a/inc/lang/zh-tw/subscr_list.txt
+++ b/inc/lang/zh-tw/subscr_list.txt
@@ -1,15 +1,15 @@
您好!
-維基 @TITLE@ 的 @PAGE@ 分類空間的頁面已更改。
+本 wiki ( @TITLE@ ) 的 @PAGE@ 分類名稱頁面已更改。
更改內容如下:
--------------------------------------------------------
@DIFF@
--------------------------------------------------------
-要取消頁面提醒,請登入維基 @DOKUWIKIURL@
+要取消頁面提醒,請登入本 wiki @DOKUWIKIURL@
然後拜訪 @SUBSCRIBE@
-並取消訂閱頁面或分類空間的更改。
+並取消訂閱頁面或分類名稱的更改。
--
本信件由以下 DokuWiki 站台產生:
diff --git a/inc/lang/zh-tw/subscr_single.txt b/inc/lang/zh-tw/subscr_single.txt
index 5128140d0..14d87bb73 100644
--- a/inc/lang/zh-tw/subscr_single.txt
+++ b/inc/lang/zh-tw/subscr_single.txt
@@ -1,6 +1,6 @@
您好!
-維基 @TITLE@ 的頁面 @PAGE@ 已更改。
+本 wiki ( @TITLE@ ) 的頁面 @PAGE@ 已更改。
更改內容如下:
--------------------------------------------------------
@@ -13,9 +13,9 @@
舊版本 : @OLDPAGE@
新版本 : @NEWPAGE@
-要取消頁面提醒,請登入維基 @DOKUWIKIURL@
+要取消頁面提醒,請登入本 wiki @DOKUWIKIURL@
然後拜訪 @NEWPAGE@
-並取消訂閱頁面或分類空間的更改。
+並取消訂閱頁面或分類名稱的更改。
--
本信件由以下 DokuWiki 站台產生:
diff --git a/inc/lang/zh-tw/updateprofile.txt b/inc/lang/zh-tw/updateprofile.txt
index 47f2ae1c4..a7a2ad803 100644
--- a/inc/lang/zh-tw/updateprofile.txt
+++ b/inc/lang/zh-tw/updateprofile.txt
@@ -1,3 +1,3 @@
====== 更新個人資料 ======
-您只需變更想更新的欄位就好,帳號名稱不能變更。 \ No newline at end of file
+您只需修改想更新的欄位就好,帳號名稱不能變更。 \ No newline at end of file
diff --git a/inc/lang/zh-tw/uploadmail.txt b/inc/lang/zh-tw/uploadmail.txt
index b4c1311ba..87bac7c9a 100644
--- a/inc/lang/zh-tw/uploadmail.txt
+++ b/inc/lang/zh-tw/uploadmail.txt
@@ -3,7 +3,7 @@
檔名 : @MEDIA@
日期 : @DATE@
瀏覽器 : @BROWSER@
-IP位址 : @IPADDRESS@
+IP 位址 : @IPADDRESS@
主機名稱 : @HOSTNAME@
大小 : @SIZE@
MIME類型 : @MIME@
@@ -11,4 +11,4 @@ MIME類型 : @MIME@
--
本信件由以下 DokuWiki 站台產生:
-@DOKUWIKIURL@
+@DOKUWIKIURL@ \ No newline at end of file
diff --git a/inc/load.php b/inc/load.php
index 49c307054..7fd9fc9d0 100644
--- a/inc/load.php
+++ b/inc/load.php
@@ -82,6 +82,7 @@ function load_autoload($name){
'Mailer' => DOKU_INC.'inc/Mailer.class.php',
'RemoteAPI' => DOKU_INC.'inc/remote.php',
'RemoteAPICore' => DOKU_INC.'inc/RemoteAPICore.php',
+ 'Subscription' => DOKU_INC.'inc/subscription.php',
'DokuWiki_Action_Plugin' => DOKU_PLUGIN.'action.php',
'DokuWiki_Admin_Plugin' => DOKU_PLUGIN.'admin.php',
diff --git a/inc/media.php b/inc/media.php
index 6335bf210..572b1177c 100644
--- a/inc/media.php
+++ b/inc/media.php
@@ -535,32 +535,13 @@ function media_contentcheck($file,$mime){
* Send a notify mail on uploads
*
* @author Andreas Gohr <andi@splitbrain.org>
- * @fixme this should embed thumbnails of images in HTML version
*/
function media_notify($id,$file,$mime,$old_rev=false){
- global $lang;
global $conf;
- global $INFO;
if(empty($conf['notify'])) return; //notify enabled?
- $text = rawLocale('uploadmail');
- $trep = array(
- 'MIME' => $mime,
- 'MEDIA' => ml($id,'',true,'&',true),
- 'SIZE' => filesize_h(filesize($file)),
- );
-
- if ($old_rev && $conf['mediarevisions']) {
- $trep['OLD'] = ml($id, "rev=$old_rev", true, '&', true);
- } else {
- $trep['OLD'] = '---';
- }
-
- $mail = new Mailer();
- $mail->to($conf['notify']);
- $mail->subject($lang['mail_upload'].' '.$id);
- $mail->setBody($text,$trep);
- return $mail->send();
+ $subscription = new Subscription();
+ return $subscription->send_media_diff($conf['notify'], 'uploadmail', $id, $old_rev, '');
}
/**
diff --git a/inc/parser/xhtml.php b/inc/parser/xhtml.php
index 68b92ca43..71f3aa4bf 100644
--- a/inc/parser/xhtml.php
+++ b/inc/parser/xhtml.php
@@ -803,6 +803,7 @@ class Doku_Renderer_xhtml extends Doku_Renderer {
$class = preg_replace('/[^_\-a-z0-9]+/i','_',$ext);
$link['class'] .= ' mediafile mf_'.$class;
$link['url'] = ml($src,array('id'=>$ID,'cache'=>$cache),true);
+ $link['title'] .= ' (' . filesize_h(filesize(mediaFN($src))).')';
}
if($hash) $link['url'] .= '#'.$hash;
diff --git a/inc/plugin.php b/inc/plugin.php
index e4bba989d..153e89407 100644
--- a/inc/plugin.php
+++ b/inc/plugin.php
@@ -81,8 +81,8 @@ class DokuWiki_Plugin {
* retrieve a language dependent file and pass to xhtml renderer for display
* plugin equivalent of p_locale_xhtml()
*
- * @param $id id of language dependent wiki page
- * @return string parsed contents of the wiki page in xhtml format
+ * @param string $id id of language dependent wiki page
+ * @return string parsed contents of the wiki page in xhtml format
*/
function locale_xhtml($id) {
return p_cached_output($this->localFN($id));
diff --git a/inc/search.php b/inc/search.php
index 1cecfd5ec..53bd240e8 100644
--- a/inc/search.php
+++ b/inc/search.php
@@ -247,11 +247,16 @@ function search_pagename(&$data,$base,$file,$type,$lvl,$opts){
* @author Andreas Gohr <andi@splitbrain.org>
*/
function search_allpages(&$data,$base,$file,$type,$lvl,$opts){
+ if(isset($opts['depth']) && $opts['depth']){
+ $parts = explode('/',ltrim($file,'/'));
+ if(($type == 'd' && count($parts) > $opts['depth'])
+ || ($type != 'd' && count($parts) > $opts['depth'] + 1)){
+ return false; // depth reached
+ }
+ }
+
//we do nothing with directories
if($type == 'd'){
- if(!$opts['depth']) return true; // recurse forever
- $parts = explode('/',ltrim($file,'/'));
- if(count($parts) == $opts['depth']) return false; // depth reached
return true;
}
diff --git a/inc/subscription.php b/inc/subscription.php
index 6b201c266..2989de032 100644
--- a/inc/subscription.php
+++ b/inc/subscription.php
@@ -1,414 +1,697 @@
<?php
/**
- * Utilities for handling (email) subscriptions
- *
- * The public interface of this file consists of the functions
- * - subscription_find
- * - subscription_send_digest
- * - subscription_send_list
- * - subscription_set
- * - get_info_subscribed
- * - subscription_addresslist
- * - subscription_lock
- * - subscription_unlock
+ * Class for handling (email) subscriptions
*
* @author Adrian Lang <lang@cosmocode.de>
+ * @author Andreas Gohr <andi@splitbrain.org>
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*/
+class Subscription {
-/**
- * Get the name of the metafile tracking subscriptions to target page or
- * namespace
- *
- * @author Adrian Lang <lang@cosmocode.de>
- *
- * @param string $id The target page or namespace, specified by id; Namespaces
- * are identified by appending a colon.
- * @return string
- */
-function subscription_filename($id) {
- $meta_fname = '.mlist';
- if ((substr($id, -1, 1) === ':')) {
- $meta_froot = getNS($id);
- $meta_fname = '/' . $meta_fname;
- } else {
- $meta_froot = $id;
+ /**
+ * Check if subscription system is enabled
+ *
+ * @return bool
+ */
+ public function isenabled() {
+ return actionOK('subscribe');
}
- return metaFN((string) $meta_froot, $meta_fname);
-}
-/**
- * Lock subscription info for an ID
- *
- * @author Adrian Lang <lang@cosmocode.de>
- * @param string $id The target page or namespace, specified by id; Namespaces
- * are identified by appending a colon.
- * @return string
- */
-function subscription_lock_filename ($id){
- global $conf;
- return $conf['lockdir'].'/_subscr_' . md5($id) . '.lock';
-}
+ /**
+ * Return the subscription meta file for the given ID
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ *
+ * @param string $id The target page or namespace, specified by id; Namespaces
+ * are identified by appending a colon.
+ * @return string
+ */
+ protected function file($id) {
+ $meta_fname = '.mlist';
+ if((substr($id, -1, 1) === ':')) {
+ $meta_froot = getNS($id);
+ $meta_fname = '/'.$meta_fname;
+ } else {
+ $meta_froot = $id;
+ }
+ return metaFN((string) $meta_froot, $meta_fname);
+ }
-/**
- * Creates a lock file for writing subscription data
- *
- * @todo add lock time parameter to io_lock() and use this instead
- * @param $id
- * @return bool
- */
-function subscription_lock($id) {
- global $conf;
- $lock = subscription_lock_filename($id);
+ /**
+ * Lock subscription info
+ *
+ * We don't use io_lock() her because we do not wait for the lock and use a larger stale time
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ * @param string $id The target page or namespace, specified by id; Namespaces
+ * are identified by appending a colon.
+ * @return bool true, if you got a succesful lock
+ */
+ protected function lock($id) {
+ global $conf;
+
+ $lock = $conf['lockdir'].'/_subscr_'.md5($id).'.lock';
+
+ if(is_dir($lock) && time() - @filemtime($lock) > 60 * 5) {
+ // looks like a stale lock - remove it
+ @rmdir($lock);
+ }
+
+ // try creating the lock directory
+ if(!@mkdir($lock, $conf['dmode'])) {
+ return false;
+ }
- if (is_dir($lock) && time()-@filemtime($lock) > 60*5) {
- // looks like a stale lock - remove it
- @rmdir($lock);
+ if($conf['dperm']) chmod($lock, $conf['dperm']);
+ return true;
}
- // try creating the lock directory
- if (!@mkdir($lock,$conf['dmode'])) {
- return false;
+ /**
+ * Unlock subscription info
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ * @param string $id The target page or namespace, specified by id; Namespaces
+ * are identified by appending a colon.
+ * @return bool
+ */
+ protected function unlock($id) {
+ global $conf;
+ $lock = $conf['lockdir'].'/_subscr_'.md5($id).'.lock';
+ return @rmdir($lock);
}
- if($conf['dperm']) chmod($lock, $conf['dperm']);
- return true;
-}
+ /**
+ * Construct a regular expression for parsing a subscription definition line
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ *
+ * @param string|array $user
+ * @param string|array $style
+ * @param string|array $data
+ * @return string complete regexp including delimiters
+ * @throws Exception when no data is passed
+ */
+ protected function buildregex($user = null, $style = null, $data = null) {
+ // always work with arrays
+ $user = (array) $user;
+ $style = (array) $style;
+ $data = (array) $data;
-/**
- * Unlock subscription info for an ID
- *
- * @author Adrian Lang <lang@cosmocode.de>
- * @param string $id The target page or namespace, specified by id; Namespaces
- * are identified by appending a colon.
- * @return bool
- */
-function subscription_unlock($id) {
- $lockf = subscription_lock_filename($id);
- return @rmdir($lockf);
-}
+ // clean
+ $user = array_filter(array_map('trim', $user));
+ $style = array_filter(array_map('trim', $style));
+ $data = array_filter(array_map('trim', $data));
-/**
- * Set subscription information
- *
- * Allows to set subscription information for permanent storage in meta files.
- * Subscriptions consist of a target object, a subscribing user, a subscribe
- * style and optional data.
- * A subscription may be deleted by specifying an empty subscribe style.
- * Only one subscription per target and user is allowed.
- * The function returns false on error, otherwise true. Note that no error is
- * returned if a subscription should be deleted but the user is not subscribed
- * and the subscription meta file exists.
- *
- * @author Adrian Lang <lang@cosmocode.de>
- *
- * @param string $user The subscriber or unsubscriber
- * @param string $page The target object (page or namespace), specified by
- * id; Namespaces are identified by a trailing colon.
- * @param string $style The subscribe style; DokuWiki currently implements
- * “every”, “digest”, and “list”.
- * @param string $data An optional data blob
- * @param bool $overwrite Whether an existing subscription may be overwritten
- * @return bool
- */
-function subscription_set($user, $page, $style, $data = null,
- $overwrite = false) {
- global $lang;
- if (is_null($style)) {
- // Delete subscription.
- $file = subscription_filename($page);
- if (!@file_exists($file)) {
- msg(sprintf($lang['subscr_not_subscribed'], $user,
- prettyprint_id($page)), -1);
- return false;
+ // user names are encoded
+ $user = array_map('auth_nameencode', $user);
+
+ // quote
+ $user = array_map('preg_quote_cb', $user);
+ $style = array_map('preg_quote_cb', $style);
+ $data = array_map('preg_quote_cb', $data);
+
+ // join
+ $user = join('|', $user);
+ $style = join('|', $style);
+ $data = join('|', $data);
+
+ // any data at all?
+ if($user.$style.$data === '') throw new Exception('no data passed');
+
+ // replace empty values, set which ones are optional
+ $sopt = '';
+ $dopt = '';
+ if($user === '') {
+ $user = '\S+';
+ }
+ if($style === '') {
+ $style = '\S+';
+ $sopt = '?';
+ }
+ if($data === '') {
+ $data = '\S+';
+ $dopt = '?';
}
- // io_deleteFromFile does not return false if no line matched.
- return io_deleteFromFile($file,
- subscription_regex(array('user' => auth_nameencode($user))),
- true);
+ // assemble
+ return "/^($user)(?:\\s+($style))$sopt(?:\\s+($data))$dopt$/";
}
- // Delete subscription if one exists and $overwrite is true. If $overwrite
- // is false, fail.
- $subs = subscription_find($page, array('user' => $user));
- if (count($subs) > 0 && isset($subs[$page])) {
- if (!$overwrite) {
- msg(sprintf($lang['subscr_already_subscribed'], $user,
- prettyprint_id($page)), -1);
- return false;
- }
- // Fail if deletion failed, else continue.
- if (!subscription_set($user, $page, null)) {
- return false;
+ /**
+ * Recursively search for matching subscriptions
+ *
+ * This function searches all relevant subscription files for a page or
+ * namespace.
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ *
+ * @param string $page The target object’s (namespace or page) id
+ * @param string|array $user
+ * @param string|array $style
+ * @param string|array $data
+ * @return array
+ */
+ public function subscribers($page, $user = null, $style = null, $data = null) {
+ if(!$this->isenabled()) return array();
+
+ // Construct list of files which may contain relevant subscriptions.
+ $files = array(':' => $this->file(':'));
+ do {
+ $files[$page] = $this->file($page);
+ $page = getNS(rtrim($page, ':')).':';
+ } while($page !== ':');
+
+ $re = $this->buildregex($user, $style, $data);
+
+ // Handle files.
+ $result = array();
+ foreach($files as $target => $file) {
+ if(!@file_exists($file)) continue;
+
+ $lines = file($file);
+ foreach($lines as $line) {
+ // fix old style subscription files
+ if(strpos($line, ' ') === false) $line = trim($line)." every\n";
+
+ // check for matching entries
+ if(!preg_match($re, $line, $m)) continue;
+
+ $u = rawurldecode($m[1]); // decode the user name
+ if(!isset($result[$target])) $result[$target] = array();
+ $result[$target][$u] = array($m[2], $m[3]); // add to result
+ }
}
+ return array_reverse($result);
}
- $file = subscription_filename($page);
- $content = auth_nameencode($user) . ' ' . $style;
- if (!is_null($data)) {
- $content .= ' ' . $data;
+ /**
+ * Adds a new subscription for the given page or namespace
+ *
+ * This will automatically overwrite any existent subscription for the given user on this
+ * *exact* page or namespace. It will *not* modify any subscription that may exist in higher namespaces.
+ *
+ * @param string $id The target page or namespace, specified by id; Namespaces
+ * are identified by appending a colon.
+ * @param string $user
+ * @param string $style
+ * @param string $data
+ * @throws Exception when user or style is empty
+ * @return bool
+ */
+ public function add($id, $user, $style, $data = '') {
+ if(!$this->isenabled()) return false;
+
+ // delete any existing subscription
+ $this->remove($id, $user);
+
+ $user = auth_nameencode(trim($user));
+ $style = trim($style);
+ $data = trim($data);
+
+ if(!$user) throw new Exception('no subscription user given');
+ if(!$style) throw new Exception('no subscription style given');
+ if(!$data) $data = time(); //always add current time for new subscriptions
+
+ $line = "$user $style $data\n";
+ $file = $this->file($id);
+ return io_saveFile($file, $line, true);
}
- return io_saveFile($file, $content . "\n", true);
-}
-/**
- * Recursively search for matching subscriptions
- *
- * This function searches all relevant subscription files for a page or
- * namespace.
- *
- * @author Adrian Lang <lang@cosmocode.de>
- * @see function subscription_regex for $pre documentation
- *
- * @param string $page The target object’s (namespace or page) id
- * @param array $pre A hash of predefined values
- * @return array
- */
-function subscription_find($page, $pre) {
- // Construct list of files which may contain relevant subscriptions.
- $filenames = array(':' => subscription_filename(':'));
- do {
- $filenames[$page] = subscription_filename($page);
- $page = getNS(rtrim($page, ':')) . ':';
- } while ($page !== ':');
-
- // Handle files.
- $matches = array();
- foreach ($filenames as $cur_page => $filename) {
- if (!@file_exists($filename)) {
- continue;
+ /**
+ * Removes a subscription for the given page or namespace
+ *
+ * This removes all subscriptions matching the given criteria on the given page or
+ * namespace. It will *not* modify any subscriptions that may exist in higher
+ * namespaces.
+ *
+ * @param string $id The target object’s (namespace or page) id
+ * @param string|array $user
+ * @param string|array $style
+ * @param string|array $data
+ * @return bool
+ */
+ public function remove($id, $user = null, $style = null, $data = null) {
+ if(!$this->isenabled()) return false;
+
+ $file = $this->file($id);
+ if(!file_exists($file)) return true;
+
+ $re = $this->buildregex($user, $style, $data);
+ return io_deleteFromFile($file, $re, true);
+ }
+
+ /**
+ * Get data for $INFO['subscribed']
+ *
+ * $INFO['subscribed'] is either false if no subscription for the current page
+ * and user is in effect. Else it contains an array of arrays with the fields
+ * “target”, “style”, and optionally “data”.
+ *
+ * @param string $id Page ID, defaults to global $ID
+ * @param string $user User, defaults to $_SERVER['REMOTE_USER']
+ * @return array
+ * @author Adrian Lang <lang@cosmocode.de>
+ */
+ function user_subscription($id = '', $user = '') {
+ if(!$this->isenabled()) return false;
+
+ global $ID;
+ if(!$id) $id = $ID;
+ if(!$user) $user = $_SERVER['REMOTE_USER'];
+
+ $subs = $this->subscribers($id, $user);
+ if(!count($subs)) return false;
+
+ $result = array();
+ foreach($subs as $target => $info) {
+ $result[] = array(
+ 'target' => $target,
+ 'style' => $info[$user][0],
+ 'data' => $info[$user][1]
+ );
}
- $subscriptions = file($filename);
- foreach ($subscriptions as $subscription) {
- if (strpos($subscription, ' ') === false) {
- // This is an old subscription file.
- $subscription = trim($subscription) . " every\n";
- }
- list($user, $rest) = explode(' ', $subscription, 2);
- $subscription = rawurldecode($user) . " " . $rest;
+ return $result;
+ }
- if (preg_match(subscription_regex($pre), $subscription,
- $line_matches) === 0) {
- continue;
- }
- $match = array_slice($line_matches, 1);
- if (!isset($matches[$cur_page])) {
- $matches[$cur_page] = array();
+ /**
+ * Send digest and list subscriptions
+ *
+ * This sends mails to all subscribers that have a subscription for namespaces above
+ * the given page if the needed $conf['subscribe_time'] has passed already.
+ *
+ * This function is called form lib/exe/indexer.php
+ *
+ * @param string $page
+ * @return int number of sent mails
+ */
+ public function send_bulk($page) {
+ if(!$this->isenabled()) return 0;
+
+ /** @var auth_basic $auth */
+ global $auth;
+ global $conf;
+ global $USERINFO;
+ $count = 0;
+
+ $subscriptions = $this->subscribers($page, null, array('digest', 'list'));
+
+ // remember current user info
+ $olduinfo = $USERINFO;
+ $olduser = $_SERVER['REMOTE_USER'];
+
+ foreach($subscriptions as $target => $users) {
+ if(!$this->lock($target)) continue;
+
+ foreach($users as $user => $info) {
+ list($style, $lastupdate) = $info;
+
+ $lastupdate = (int) $lastupdate;
+ if($lastupdate + $conf['subscribe_time'] > time()) {
+ // Less than the configured time period passed since last
+ // update.
+ continue;
+ }
+
+ // Work as the user to make sure ACLs apply correctly
+ $USERINFO = $auth->getUserData($user);
+ $_SERVER['REMOTE_USER'] = $user;
+ if($USERINFO === false) continue;
+ if(!$USERINFO['mail']) continue;
+
+ if(substr($target, -1, 1) === ':') {
+ // subscription target is a namespace, get all changes within
+ $changes = getRecentsSince($lastupdate, null, getNS($target));
+ } else {
+ // single page subscription, check ACL ourselves
+ if(auth_quickaclcheck($target) < AUTH_READ) continue;
+ $meta = p_get_metadata($target);
+ $changes = array($meta['last_change']);
+ }
+
+ // Filter out pages only changed in small and own edits
+ $change_ids = array();
+ foreach($changes as $rev) {
+ $n = 0;
+ while(!is_null($rev) && $rev['date'] >= $lastupdate &&
+ ($_SERVER['REMOTE_USER'] === $rev['user'] ||
+ $rev['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT)) {
+ $rev = getRevisions($rev['id'], $n++, 1);
+ $rev = (count($rev) > 0) ? $rev[0] : null;
+ }
+
+ if(!is_null($rev) && $rev['date'] >= $lastupdate) {
+ // Some change was not a minor one and not by myself
+ $change_ids[] = $rev['id'];
+ }
+ }
+
+ // send it
+ if($style === 'digest') {
+ foreach($change_ids as $change_id) {
+ $this->send_digest(
+ $USERINFO['mail'], $change_id,
+ $lastupdate
+ );
+ $count++;
+ }
+ } elseif($style === 'list') {
+ $this->send_list($USERINFO['mail'], $change_ids, $target);
+ $count++;
+ }
+ // TODO: Handle duplicate subscriptions.
+
+ // Update notification time.
+ $this->add($target, $user, $style, time());
}
- $matches[$cur_page][] = $match;
+ $this->unlock($target);
}
- }
- return array_reverse($matches);
-}
-/**
- * Get data for $INFO['subscribed']
- *
- * $INFO['subscribed'] is either false if no subscription for the current page
- * and user is in effect. Else it contains an array of arrays with the fields
- * “target”, “style”, and optionally “data”.
- *
- * @author Adrian Lang <lang@cosmocode.de>
- */
-function get_info_subscribed() {
- global $ID;
- global $conf;
- if (!$conf['subscribers']) {
- return false;
+ // restore current user info
+ $USERINFO = $olduinfo;
+ $_SERVER['REMOTE_USER'] = $olduser;
+ return $count;
}
- $subs = subscription_find($ID, array('user' => $_SERVER['REMOTE_USER']));
- if (count($subs) === 0) {
- return false;
+ /**
+ * Send the diff for some page change
+ *
+ * @param string $subscriber_mail The target mail address
+ * @param string $template Mail template ('subscr_digest', 'subscr_single', 'mailtext', ...)
+ * @param string $id Page for which the notification is
+ * @param int|null $rev Old revision if any
+ * @param string $summary Change summary if any
+ * @return bool true if successfully sent
+ */
+ public function send_diff($subscriber_mail, $template, $id, $rev = null, $summary = '') {
+ global $DIFF_INLINESTYLES;
+
+ // prepare replacements (keys not set in hrep will be taken from trep)
+ $trep = array(
+ 'PAGE' => $id,
+ 'NEWPAGE' => wl($id, '', true, '&'),
+ 'SUMMARY' => $summary,
+ 'SUBSCRIBE' => wl($id, array('do' => 'subscribe'), true, '&')
+ );
+ $hrep = array();
+
+ if($rev) {
+ $subject = 'changed';
+ $trep['OLDPAGE'] = wl($id, "rev=$rev", true, '&');
+
+ $old_content = rawWiki($id, $rev);
+ $new_content = rawWiki($id);
+
+ $df = new Diff(explode("\n", $old_content),
+ explode("\n", $new_content));
+ $dformat = new UnifiedDiffFormatter();
+ $tdiff = $dformat->format($df);
+
+ $DIFF_INLINESTYLES = true;
+ $df = new Diff(explode("\n", hsc($old_content)),
+ explode("\n", hsc($new_content)));
+ $dformat = new InlineDiffFormatter();
+ $hdiff = $dformat->format($df);
+ $hdiff = '<table>'.$hdiff.'</table>';
+ $DIFF_INLINESTYLES = false;
+ } else {
+ $subject = 'newpage';
+ $trep['OLDPAGE'] = '---';
+ $tdiff = rawWiki($id);
+ $hdiff = nl2br(hsc($tdiff));
+ }
+
+ $trep['DIFF'] = $tdiff;
+ $hrep['DIFF'] = $hdiff;
+
+ $headers = array('Message-Id' => $this->getMessageID($id));
+ if ($rev) {
+ $headers['In-Reply-To'] = $this->getMessageID($id, $rev);
+ }
+
+ return $this->send(
+ $subscriber_mail, $subject, $id,
+ $template, $trep, $hrep, $headers
+ );
}
- $_ret = array();
- foreach ($subs as $target => $subs_data) {
- $new = array('target' => $target,
- 'style' => $subs_data[0][0]);
- if (count($subs_data[0]) > 1) {
- $new['data'] = $subs_data[0][1];
+ /**
+ * Send the diff for some media change
+ *
+ * @fixme this should embed thumbnails of images in HTML version
+ * @param string $subscriber_mail The target mail address
+ * @param string $template Mail template ('uploadmail', ...)
+ * @param string $id Media file for which the notification is
+ * @param int|bool $rev Old revision if any
+ * @return bool true if successfully sent
+ */
+ public function send_media_diff($subscriber_mail, $template, $id, $rev = false) {
+ global $conf;
+
+ $file = mediaFN($id);
+ list($mime, $ext) = mimetype($id);
+
+ $trep = array(
+ 'MIME' => $mime,
+ 'MEDIA' => ml($id,'',true,'&',true),
+ 'SIZE' => filesize_h(filesize($file)),
+ );
+
+ if ($rev && $conf['mediarevisions']) {
+ $trep['OLD'] = ml($id, "rev=$rev", true, '&', true);
+ } else {
+ $trep['OLD'] = '---';
+ }
+
+ $headers = array('Message-Id' => $this->getMessageID($id, @filemtime($file)));
+ if ($rev) {
+ $headers['In-Reply-To'] = $this->getMessageID($id, $rev);
}
- $_ret[] = $new;
+
+ $this->send($subscriber_mail, 'upload', $id, $template, $trep, null, $headers);
+
}
- return $_ret;
-}
+ /**
+ * Send a notify mail on new registration
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ *
+ * @param string $login login name of the new user
+ * @param string $fullname full name of the new user
+ * @param string $email email address of the new user
+ * @return bool true if a mail was sent
+ */
+ public function send_register($login, $fullname, $email) {
+ global $conf;
+ if(empty($conf['registernotify'])) return false;
-/**
- * Construct a regular expression parsing a subscription definition line
- *
- * @author Adrian Lang <lang@cosmocode.de>
- *
- * @param array $pre A hash of predefined values; “user”, “style”, and
- * “data” may be set to limit the results to
- * subscriptions matching these parameters. If
- * “escaped” is true, these fields are inserted into the
- * regular expression without escaping.
- *
- * @return string complete regexp including delimiters
- */
-function subscription_regex($pre = array()) {
- if (!isset($pre['escaped']) || $pre['escaped'] === false) {
- $pre = array_map('preg_quote_cb', $pre);
+ $trep = array(
+ 'NEWUSER' => $login,
+ 'NEWNAME' => $fullname,
+ 'NEWEMAIL' => $email,
+ );
+
+ return $this->send(
+ $conf['registernotify'],
+ 'new_user',
+ $login,
+ 'registermail',
+ $trep
+ );
}
- foreach (array('user', 'style', 'data') as $key) {
- if (!isset($pre[$key])) {
- $pre[$key] = '(\S+)';
+
+ /**
+ * Send a digest mail
+ *
+ * Sends a digest mail showing a bunch of changes of a single page. Basically the same as send_diff()
+ * but determines the last known revision first
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ *
+ * @param string $subscriber_mail The target mail address
+ * @param array $id The ID
+ * @param int $lastupdate Time of the last notification
+ * @return bool
+ */
+ protected function send_digest($subscriber_mail, $id, $lastupdate) {
+ $n = 0;
+ do {
+ $rev = getRevisions($id, $n++, 1);
+ $rev = (count($rev) > 0) ? $rev[0] : null;
+ } while(!is_null($rev) && $rev > $lastupdate);
+
+ return $this->send_diff(
+ $subscriber_mail,
+ 'subscr_digest',
+ $id, $rev
+ );
+ }
+
+ /**
+ * Send a list mail
+ *
+ * Sends a list mail showing a list of changed pages.
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ *
+ * @param string $subscriber_mail The target mail address
+ * @param array $ids Array of ids
+ * @param string $ns_id The id of the namespace
+ * @return bool true if a mail was sent
+ */
+ protected function send_list($subscriber_mail, $ids, $ns_id) {
+ if(count($ids) === 0) return false;
+
+ $tlist = '';
+ $hlist = '<ul>';
+ foreach($ids as $id) {
+ $link = wl($id, array(), true);
+ $tlist .= '* '.$link.NL;
+ $hlist .= '<li><a href="'.$link.'">'.hsc($id).'</a></li>'.NL;
}
+ $hlist .= '</ul>';
+
+ $id = prettyprint_id($ns_id);
+ $trep = array(
+ 'DIFF' => rtrim($tlist),
+ 'PAGE' => $id,
+ 'SUBSCRIBE' => wl($id, array('do' => 'subscribe'), true, '&')
+ );
+ $hrep = array(
+ 'DIFF' => $hlist
+ );
+
+ return $this->send(
+ $subscriber_mail,
+ 'subscribe_list',
+ $ns_id,
+ 'subscr_list', $trep, $hrep
+ );
}
- return '/^' . $pre['user'] . '(?: ' . $pre['style'] .
- '(?: ' . $pre['data'] . ')?)?$/';
-}
-/**
- * Return a string with the email addresses of all the
- * users subscribed to a page
- *
- * This is the default action for COMMON_NOTIFY_ADDRESSLIST.
- *
- * @author Steven Danz <steven-danz@kc.rr.com>
- * @author Adrian Lang <lang@cosmocode.de>
- *
- * @todo this does NOT return a string but uses a reference to write back, either fix function or docs
- * @param array $data Containing $id (the page id), $self (whether the author
- * should be notified, $addresslist (current email address
- * list)
- * @return string
- */
-function subscription_addresslist(&$data){
- global $conf;
- /** @var auth_basic $auth */
- global $auth;
+ /**
+ * Helper function for sending a mail
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ *
+ * @param string $subscriber_mail The target mail address
+ * @param string $subject The lang id of the mail subject (without the
+ * prefix “mail_”)
+ * @param string $context The context of this mail, eg. page or namespace id
+ * @param string $template The name of the mail template
+ * @param array $trep Predefined parameters used to parse the
+ * template (in text format)
+ * @param array $hrep Predefined parameters used to parse the
+ * template (in HTML format), null to default to $trep
+ * @param array $headers Additional mail headers in the form 'name' => 'value'
+ * @return bool
+ */
+ protected function send($subscriber_mail, $subject, $context, $template, $trep, $hrep = null, $headers = array()) {
+ global $lang;
+ global $conf;
+
+ $text = rawLocale($template);
+ $subject = $lang['mail_'.$subject].' '.$context;
+ $mail = new Mailer();
+ $mail->bcc($subscriber_mail);
+ $mail->subject($subject);
+ $mail->setBody($text, $trep, $hrep);
+ if(in_array($template, array('subscr_list', 'subscr_digest'))){
+ $mail->from($conf['mailfromnobody']);
+ }
+ if(isset($trep['SUBSCRIBE'])) {
+ $mail->setHeader('List-Unsubscribe', '<'.$trep['SUBSCRIBE'].'>', false);
+ }
- $id = $data['id'];
- $self = $data['self'];
- $addresslist = $data['addresslist'];
+ foreach ($headers as $header => $value) {
+ $mail->setHeader($header, $value);
+ }
- if (!$conf['subscribers'] || $auth === null) {
- return '';
+ return $mail->send();
}
- $pres = array('style' => 'every', 'escaped' => true);
- if (!$self && isset($_SERVER['REMOTE_USER'])) {
- $pres['user'] = '((?!' . preg_quote_cb($_SERVER['REMOTE_USER']) .
- '(?: |$))\S+)';
+
+ /**
+ * Get a valid message id for a certain $id and revision (or the current revision)
+ * @param string $id The id of the page (or media file) the message id should be for
+ * @param string $rev The revision of the page, set to the current revision of the page $id if not set
+ * @return string
+ */
+ protected function getMessageID($id, $rev = NULL) {
+ static $listid = null;
+ if (is_null($listid)) {
+ $server = parse_url(DOKU_URL, PHP_URL_HOST);
+ $listid = join('.', array_reverse(explode('/', DOKU_BASE))).$server;
+ $listid = urlencode($listid);
+ $listid = strtolower(trim($listid, '.'));
+ }
+
+ if (is_null($rev)) {
+ $rev = @filemtime(wikiFN($id));
+ }
+
+ return "<$id?rev=$rev@$listid>";
}
- $subs = subscription_find($id, $pres);
- $emails = array();
- foreach ($subs as $by_targets) {
- foreach ($by_targets as $sub) {
- $info = $auth->getUserData($sub[0]);
- if ($info === false) continue;
- $level = auth_aclcheck($id, $sub[0], $info['grps']);
- if ($level >= AUTH_READ) {
- if (strcasecmp($info['mail'], $conf['notify']) != 0) {
- $emails[$sub[0]] = $info['mail'];
+
+ /**
+ * Default callback for COMMON_NOTIFY_ADDRESSLIST
+ *
+ * Aggregates all email addresses of user who have subscribed the given page with 'every' style
+ *
+ * @author Steven Danz <steven-danz@kc.rr.com>
+ * @author Adrian Lang <lang@cosmocode.de>
+ *
+ * @todo move the whole functionality into this class, trigger SUBSCRIPTION_NOTIFY_ADDRESSLIST instead,
+ * use an array for the addresses within it
+ *
+ * @param array &$data Containing $id (the page id), $self (whether the author
+ * should be notified, $addresslist (current email address
+ * list)
+ */
+ public function notifyaddresses(&$data) {
+ if(!$this->isenabled()) return;
+
+ /** @var auth_basic $auth */
+ global $auth;
+ global $conf;
+
+ $id = $data['id'];
+ $self = $data['self'];
+ $addresslist = $data['addresslist'];
+
+ $subscriptions = $this->subscribers($id, null, 'every');
+
+ $result = array();
+ foreach($subscriptions as $target => $users) {
+ foreach($users as $user => $info) {
+ $userinfo = $auth->getUserData($user);
+ if($userinfo === false) continue;
+ if(!$userinfo['mail']) continue;
+ if(!$self && $user == $_SERVER['REMOTE_USER']) continue; //skip our own changes
+
+ $level = auth_aclcheck($id, $user, $userinfo['grps']);
+ if($level >= AUTH_READ) {
+ if(strcasecmp($userinfo['mail'], $conf['notify']) != 0) { //skip user who get notified elsewhere
+ $result[$user] = $userinfo['mail'];
+ }
}
}
}
+ $data['addresslist'] = trim($addresslist.','.implode(',', $result), ',');
}
- $data['addresslist'] = trim($addresslist . ',' . implode(',', $emails), ',');
}
/**
- * Send a digest mail
+ * Compatibility wrapper around Subscription:notifyaddresses
*
- * Sends a digest mail showing a bunch of changes.
+ * for plugins emitting COMMON_NOTIFY_ADDRESSLIST themselves and relying on on this to
+ * be the default handler
*
- * @author Adrian Lang <lang@cosmocode.de>
+ * @param array $data event data for
*
- * @param string $subscriber_mail The target mail address
- * @param array $id The ID
- * @param int $lastupdate Time of the last notification
+ * @deprecated 2012-12-07
*/
-function subscription_send_digest($subscriber_mail, $id, $lastupdate) {
- $n = 0;
- do {
- $rev = getRevisions($id, $n++, 1);
- $rev = (count($rev) > 0) ? $rev[0] : null;
- } while (!is_null($rev) && $rev > $lastupdate);
-
- $replaces = array('NEWPAGE' => wl($id, '', true, '&'),
- 'SUBSCRIBE' => wl($id, array('do' => 'subscribe'), true, '&'));
- if (!is_null($rev)) {
- $subject = 'changed';
- $replaces['OLDPAGE'] = wl($id, "rev=$rev", true, '&');
- $df = new Diff(explode("\n", rawWiki($id, $rev)),
- explode("\n", rawWiki($id)));
- $dformat = new UnifiedDiffFormatter();
- $replaces['DIFF'] = $dformat->format($df);
- } else {
- $subject = 'newpage';
- $replaces['OLDPAGE'] = 'none';
- $replaces['DIFF'] = rawWiki($id);
- }
- subscription_send($subscriber_mail, $replaces, $subject, $id,
- 'subscr_digest');
-}
-
-/**
- * Send a list mail
- *
- * Sends a list mail showing a list of changed pages.
- *
- * @author Adrian Lang <lang@cosmocode.de>
- *
- * @param string $subscriber_mail The target mail address
- * @param array $ids Array of ids
- * @param string $ns_id The id of the namespace
- */
-function subscription_send_list($subscriber_mail, $ids, $ns_id) {
- if (count($ids) === 0) return;
- global $conf;
- $list = '';
- foreach ($ids as $id) {
- $list .= '* ' . wl($id, array(), true) . NL;
- }
- subscription_send($subscriber_mail,
- array('DIFF' => rtrim($list),
- 'SUBSCRIBE' => wl($ns_id . $conf['start'],
- array('do' => 'subscribe'),
- true, '&')),
- 'subscribe_list',
- prettyprint_id($ns_id),
- 'subscr_list');
-}
-
-/**
- * Helper function for sending a mail
- *
- * @author Adrian Lang <lang@cosmocode.de>
- *
- * @param string $subscriber_mail The target mail address
- * @param array $replaces Predefined parameters used to parse the
- * template
- * @param string $subject The lang id of the mail subject (without the
- * prefix “mail_”)
- * @param string $id The page or namespace id
- * @param string $template The name of the mail template
- * @return bool
- */
-function subscription_send($subscriber_mail, $replaces, $subject, $id, $template) {
- global $lang;
- global $conf;
-
- $text = rawLocale($template);
- $trep = array_merge($replaces, array('PAGE' => $id));
- $hrep = $trep;
- $hrep['DIFF'] = nl2br(htmlspecialchars($hrep['DIFF']));
-
- $subject = $lang['mail_' . $subject] . ' ' . $id;
- $mail = new Mailer();
- $mail->bcc($subscriber_mail);
- $mail->subject($subject);
- $mail->setBody($text,$trep,$hrep);
- $mail->from($conf['mailfromnobody']);
- $mail->setHeader(
- 'List-Unsubscribe',
- '<'.wl($id,array('do'=>'subscribe'),true,'&').'>',
- false
- );
- return $mail->send();
-}
+function subscription_addresslist(&$data) {
+ $sub = new Subscription();
+ $sub->notifyaddresses($data);
+} \ No newline at end of file