diff options
Diffstat (limited to 'inc')
36 files changed, 918 insertions, 514 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/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 5c1c75cf6..3124f4b1d 100644 --- a/inc/html.php +++ b/inc/html.php @@ -859,7 +859,7 @@ function html_list_index($item){ $base = ':'.$item['id']; $base = substr($base,strrpos($base,':')+1); if($item['type']=='d'){ - $ret .= '<a href="'.wl($ID,'idx='.rawurlencode($item['id'])).'" class="idx_dir"><strong>'; + $ret .= '<a href="'.wl($ID,'idx='.rawurlencode($item['id'])).'" title="' . $item['id'] . '" class="idx_dir"><strong>'; $ret .= $base; $ret .= '</strong></a>'; }else{ 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/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/el/lang.php b/inc/lang/el/lang.php index b6cdc38c1..55b70074f 100644 --- a/inc/lang/el/lang.php +++ b/inc/lang/el/lang.php @@ -8,6 +8,7 @@ * @author Konstantinos Koryllos <koryllos@gmail.com> * @author George Petsagourakis <petsagouris@gmail.com> * @author Petros Vidalis <pvidalis@gmail.com> + * @author Vasileios Karavasilis vasileioskaravasilis@gmail.com */ $lang['encoding'] = 'utf-8'; $lang['direction'] = 'ltr'; @@ -43,6 +44,7 @@ $lang['btn_backtomedia'] = 'Επιστροφή στην επιλογή α $lang['btn_subscribe'] = 'Εγγραφή σε λήψη ενημερώσεων σελίδας'; $lang['btn_profile'] = 'Επεξεργασία προφίλ'; $lang['btn_reset'] = 'Ακύρωση'; +$lang['btn_resendpwd'] = 'Εισαγωγή νέου κωδικού'; $lang['btn_draft'] = 'Επεξεργασία αυτόματα αποθηκευμένης σελίδας'; $lang['btn_recover'] = 'Επαναφορά αυτόματα αποθηκευμένης σελίδας'; $lang['btn_draftdel'] = 'Διαγραφή αυτόματα αποθηκευμένης σελίδας'; @@ -79,6 +81,7 @@ $lang['profnoempty'] = 'Δεν επιτρέπεται κενό όνο $lang['profchanged'] = 'Το προφίλ χρήστη τροποποιήθηκε επιτυχώς.'; $lang['pwdforget'] = 'Ξεχάσατε το κωδικό σας; Αποκτήστε νέο.'; $lang['resendna'] = 'Αυτό το wiki δεν υποστηρίζει την εκ\' νέου αποστολή κωδικών.'; +$lang['resendpwd'] = 'Εισαγωγή νέου ωδικού για'; $lang['resendpwdmissing'] = 'Πρέπει να συμπληρώσετε όλα τα πεδία.'; $lang['resendpwdnouser'] = 'Αυτός ο χρήστης δεν υπάρχει στα αρχεία μας.'; $lang['resendpwdbadauth'] = 'Αυτός ο κωδικός ενεργοποίησης δεν είναι έγκυρος.'; @@ -91,6 +94,7 @@ $lang['searchmedia_in'] = 'Αναζήτηση σε %s'; $lang['txt_upload'] = 'Επιλέξτε αρχείο για φόρτωση'; $lang['txt_filename'] = 'Επιλέξτε νέο όνομα αρχείου (προαιρετικό)'; $lang['txt_overwrt'] = 'Αντικατάσταση υπάρχοντος αρχείου'; +$lang['maxuploadsize'] = 'Μέγιστο μέγεθος αρχείου: %s.'; $lang['lockedby'] = 'Προσωρινά κλειδωμένο από'; $lang['lockexpire'] = 'Το κλείδωμα λήγει στις'; $lang['js']['willexpire'] = 'Το κλείδωμά σας για την επεξεργασία αυτής της σελίδας θα λήξει σε ένα λεπτό.\n Για να το ανανεώσετε χρησιμοποιήστε την Προεπισκόπηση.'; @@ -185,6 +189,12 @@ $lang['external_edit'] = 'εξωτερική τροποποίηση'; $lang['summary'] = 'Επεξεργασία σύνοψης'; $lang['noflash'] = 'Το <a href="http://www.adobe.com/products/flashplayer/">Adobe Flash Plugin</a> απαιτείται για την προβολή αυτού του στοιχείου.'; $lang['download'] = 'Λήψη Κώδικα'; +$lang['tools'] = 'Εργαλεία'; +$lang['user_tools'] = 'Εργαλεία Χρήστη'; +$lang['site_tools'] = 'Εργαλεία ιστότοπου'; +$lang['page_tools'] = 'Εργαλεία ιστοσελίδας'; +$lang['skip_to_content'] = 'παράληψη περιεχομένων'; +$lang['sidebar'] = 'Sidebar'; $lang['mail_newpage'] = 'σελίδα προστέθηκε:'; $lang['mail_changed'] = 'σελίδα τροποποιήθηκε:'; $lang['mail_subscribe_list'] = 'σελίδες που άλλαξαν στον φάκελο:'; @@ -255,6 +265,7 @@ $lang['subscr_style_digest'] = 'συνοπτικό email αλλαγών της $lang['subscr_style_list'] = 'λίστα σελίδων με αλλαγές μετά από το τελευταίο email (κάθε %.2f μέρες)'; $lang['authmodfailed'] = 'Κακή ρύθμιση λίστας χρηστών. Παρακαλούμε ενημερώστε τον διαχειριστή του wiki.'; $lang['authtempfail'] = 'Η συνδεση χρηστών είναι απενεργοποιημένη αυτή την στιγμή. Αν αυτό διαρκέσει για πολύ, παρακαλούμε ενημερώστε τον διαχειριστή του wiki.'; +$lang['authpwdexpire'] = 'Ο κωδικός πρόσβασης θα λήξει σε %s ημέρες. Προτείνουμε να τον αλλάξετε σύντομα.'; $lang['i_chooselang'] = 'Επιλογή γλώσσας'; $lang['i_installer'] = 'Οδηγός εγκατάστασης DokuWiki'; $lang['i_wikiname'] = 'Ονομασία wiki'; @@ -270,8 +281,8 @@ $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_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'] = 'Ανοιχτό Wiki (όλοι μπορούν να διαβάσουν ή να δημιουργήσουν/τροποποιήσουν σελίδες και να μεταφορτώσουν αρχεία)'; $lang['i_pol1'] = 'Δημόσιο Wiki (όλοι μπορούν να διαβάσουν σελίδες αλλά μόνο οι εγγεγραμμένοι χρήστες μπορούν να δημιουργήσουν/τροποποιήσουν σελίδες και να μεταφορτώσουν αρχεία)'; @@ -289,16 +300,20 @@ $lang['seconds'] = 'πριν %d δευτερόλεπτα'; $lang['wordblock'] = 'Η αλλαγή σας δεν αποθηκεύτηκε γιατί περιείχε spam.'; $lang['media_uploadtab'] = 'Φόρτωση'; $lang['media_searchtab'] = 'Αναζήτηση'; +$lang['media_file'] = 'Αρχείο'; $lang['media_viewtab'] = 'Εμφάνιση'; $lang['media_edittab'] = 'Επεξεργασία'; $lang['media_historytab'] = 'Ιστορικό'; -$lang['media_thumbsview'] = 'Προεπισκόπιση'; -$lang['media_listview'] = 'Λίστα'; -$lang['media_sort'] = 'Ταξινόμιση'; +$lang['media_list_thumbs'] = 'Μικρογραφίες'; +$lang['media_list_rows'] = 'Γραμμές'; $lang['media_sort_name'] = 'ανά όνομα'; $lang['media_sort_date'] = 'ανά ημερομηνία'; +$lang['media_namespaces'] = 'Επιλογή namespace'; +$lang['media_files'] = 'Αρχεία στο %s φάκελο'; $lang['media_upload'] = 'Φόρτωση στο <strong>%s</strong> φάκελο.'; $lang['media_search'] = 'Αναζήτηση στο <strong>%s</strong> φάκελο.'; +$lang['media_view'] = '%s'; +$lang['media_viewold'] = '%s στα %s'; $lang['media_edit'] = 'Επεξεργασία'; $lang['media_history'] = 'Αυτές είναι οι παλαιότερες αναθεωρήσεις του αρχείου.'; $lang['media_meta_edited'] = 'τα μεταδεδομένα επεξεργάστηκαν'; diff --git a/inc/lang/el/mailwrap.html b/inc/lang/el/mailwrap.html new file mode 100644 index 000000000..b2e655789 --- /dev/null +++ b/inc/lang/el/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>Το email έχει δημιουργηθεί από το DokuWiki στις @DOKUWIKIURL@.</small> +</body> +</html>
\ No newline at end of file diff --git a/inc/lang/el/resetpwd.txt b/inc/lang/el/resetpwd.txt new file mode 100644 index 000000000..0d26d05fa --- /dev/null +++ b/inc/lang/el/resetpwd.txt @@ -0,0 +1,3 @@ +====== Εισάγετε νέο κωδικό πρόσβασης ====== + +Παρακαλούμε, εισάγετε έναν νέο κωδικό πρόσβασης για τον λογαριασμό σας.
\ No newline at end of file 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/gl/lang.php b/inc/lang/gl/lang.php index a33aba72e..7cc06a833 100644 --- a/inc/lang/gl/lang.php +++ b/inc/lang/gl/lang.php @@ -5,7 +5,7 @@ * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) * @author Medúlio <medulio@ciberirmandade.org> * @author Oscar M. Lage <r0sk10@gmail.com> - * @author Leandro Regueiro <leandro.regueiro@gmail.com> + * @author Rodrigo Rega <rodrigorega@gmail.com> */ $lang['encoding'] = 'utf-8'; $lang['direction'] = 'ltr'; @@ -41,7 +41,7 @@ $lang['btn_backtomedia'] = 'Volver á Selección de Arquivos-Media'; $lang['btn_subscribe'] = 'Avísame dos trocos na páxina'; $lang['btn_profile'] = 'Actualizar Perfil'; $lang['btn_reset'] = 'Reiniciar'; -$lang['btn_resendpwd'] = 'Definir novo contrasinal'; +$lang['btn_resendpwd'] = 'Establecer novo contrasinal'; $lang['btn_draft'] = 'Editar borrador'; $lang['btn_recover'] = 'Recuperar borrador'; $lang['btn_draftdel'] = 'Eliminar borrador'; @@ -78,7 +78,7 @@ $lang['profnoempty'] = 'Non se permite un nome ou un enderezo de corre $lang['profchanged'] = 'Perfil de usuario actualizado correctamente.'; $lang['pwdforget'] = 'Esqueceches o teu contrasinal? Consegue un novo'; $lang['resendna'] = 'Este wiki non permite o reenvío de contrasinais.'; -$lang['resendpwd'] = 'Definir novo contrasinal para'; +$lang['resendpwd'] = 'Establecer novo contrasinal para'; $lang['resendpwdmissing'] = 'Sentímolo, tes que cubrir todos os campos.'; $lang['resendpwdnouser'] = 'Sentímolo, non atopamos este usuario no noso banco de datos.'; $lang['resendpwdbadauth'] = 'Sentímolo, mais este código de autorización non é válido. Asegúrate de que usaches a ligazón completa de confirmación.'; @@ -91,7 +91,7 @@ $lang['searchmedia_in'] = 'Procurar en %s'; $lang['txt_upload'] = 'Escolle o arquivo para subir'; $lang['txt_filename'] = 'Subir como (opcional)'; $lang['txt_overwrt'] = 'Sobrescribir arquivo existente'; -$lang['maxuploadsize'] = 'Envío máx. de %s por ficheiro.'; +$lang['maxuploadsize'] = 'Subida máxima %s por arquivo.'; $lang['lockedby'] = 'Bloqueado actualmente por'; $lang['lockexpire'] = 'O bloqueo remata o'; $lang['js']['willexpire'] = 'O teu bloqueo para editares esta páxina vai caducar nun minuto.\nPara de evitar conflitos, emprega o botón de previsualización para reiniciares o contador do tempo de bloqueo.'; @@ -190,7 +190,7 @@ $lang['tools'] = 'Ferramentas'; $lang['user_tools'] = 'Ferramentas de usuario'; $lang['site_tools'] = 'Ferramentas do sitio'; $lang['page_tools'] = 'Ferramentas de páxina'; -$lang['skip_to_content'] = 'saltar ao contido'; +$lang['skip_to_content'] = 'Pasar ao contido'; $lang['sidebar'] = 'Barra lateral'; $lang['mail_newpage'] = 'páxina engadida:'; $lang['mail_changed'] = 'páxina mudada:'; @@ -262,7 +262,7 @@ $lang['subscr_style_digest'] = 'correo-e con resumo de trocos para cada páxin $lang['subscr_style_list'] = 'lista de páxinas mudadas dende o último correo-e'; $lang['authmodfailed'] = 'Configuración de autenticación de usuario incorrecta. Por favor, informa ao Administrador do teu Wiki.'; $lang['authtempfail'] = 'A autenticación de usuario non está dispoñible de xeito temporal. De persistir esta situación, por favor, informa ao Administrador do teu Wiki.'; -$lang['authpwdexpire'] = 'O seu contrasinal vai caducar en %d días, razón pola cal debería cambialo axiña.'; +$lang['authpwdexpire'] = 'A túa contrasinal expirará en %d días, deberías cambiala pronto.'; $lang['i_chooselang'] = 'Escolle o teu idioma'; $lang['i_installer'] = 'Instalador do DokuWiki'; $lang['i_wikiname'] = 'Nome do Wiki'; diff --git a/inc/lang/gl/mailwrap.html b/inc/lang/gl/mailwrap.html index 05ef4175a..19927c117 100644 --- a/inc/lang/gl/mailwrap.html +++ b/inc/lang/gl/mailwrap.html @@ -8,6 +8,6 @@ @HTMLBODY@ <br /><hr /> -<small>Esta mensaxe foi xerada por DokuWiki en @DOKUWIKIURL@.</small> +<small>Este correo era xerado por DokuWiki en @DOKUWIKIURL@.</small> </body> </html>
\ No newline at end of file diff --git a/inc/lang/gl/resetpwd.txt b/inc/lang/gl/resetpwd.txt index 7b662d8f5..d3d64e90d 100644 --- a/inc/lang/gl/resetpwd.txt +++ b/inc/lang/gl/resetpwd.txt @@ -1,3 +1,3 @@ -====== Definir novo contrasinal ====== +====== Establecer novo contrasinal ====== -Introduza un novo contrasinal para a súa conta neste wiki.
\ No newline at end of file +Por favor introduzca un novo contrasinal para a súa conta neste wiki.
\ No newline at end of file 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/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/plugin.php b/inc/plugin.php index 153e89407..649fc1f26 100644 --- a/inc/plugin.php +++ b/inc/plugin.php @@ -189,7 +189,7 @@ class DokuWiki_Plugin { * * @return object helper plugin object */ - function loadHelper($name, $msg){ + function loadHelper($name, $msg = true){ if (!plugin_isdisabled($name)){ $obj = plugin_load('helper',$name); }else{ 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 diff --git a/inc/template.php b/inc/template.php index 4af35cc2b..2c083c964 100644 --- a/inc/template.php +++ b/inc/template.php @@ -703,6 +703,7 @@ function tpl_get_action($type) { } break; case 'media': + $params['ns'] = getNS($ID); break; default: return '[unknown %s type]'; @@ -839,7 +840,9 @@ function tpl_youarehere($sep = ' » ') { echo '<span class="bchead">'.$lang['youarehere'].': </span>'; // always print the startpage + echo '<span class="home">'; tpl_pagelink(':'.$conf['start']); + echo '</span>'; // print intermediate namespace links $part = ''; |