User:Zayoo/Extension:Advanced Meta
***THIS PAGE IS NO LONGER VALID***
Changes has been updated to Extension:Advanced Meta and this page no longer updates.
This is a suggested version of Extension:Advanced Meta.
You can find an updated version of installation instructions, including a script to drop in the required MySQL table into your database, here.
[edit] Installation
To basically install the plugin, create a folder called MWAdvancedMeta in /extensions/ in your MediaWiki main folder, then upload the source code below in two files called MWAdvancedMeta.php and MWAdvancedMeta.i18n.php in the new folder. In LocalSettings.php add this line
require_once( "$IP/extensions/MWAdvancedMeta/MWAdvancedMeta.php" );
Add the page Mediawiki:Globalkeywords into your own wiki and write the global keywords there, splitted in commas (,).
There are some settings you can change in LocalSettings.php. Possible settings are namespaces that are indexed by default (default setting: NS_MAIN and NS_PROJECT), and users/usergroups that are allowed to edit the meta settings (default: sysop and bureaucrat).
To set these settings, add this line first
$mwaMeta = MWAdvancedMeta::setup();
Then add one or more of these lines
$mwaMeta->setAllowedUsers(array('Mary', 'Anne')); $mwaMeta->setAllowedUsergroups(array()); $mwaMeta->setIndexedPages(array(NS_MAIN, NS_PROJECT, NS_IMAGE, NS_TALK));
Namespaces that are not in the 'indexedpages' variable will get a "noindex,follow" by default. There is currently no setting to also prevent them from being followed, but I might add that sooner or later depending on if it's being requested (feel free to contact me).
Last, a table should be added to the mediawiki database. The following SQL should take care of that (note: change 'mw' to your wiki prefix as needed for the AL code and modify the table name referenced in the "write" function as well....):
CREATE TABLE IF NOT EXISTS `mw_ext_meta` ( `pageid` INT(8) NOT NULL, `rindex` tinyint(1) NOT NULL, `rfollow` tinyint(1) NOT NULL, `titlealias` VARCHAR(255), `keywords` text, `description` text, PRIMARY KEY (`pageid`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
[edit] Code: MWAdvancedMeta.php
<?php /* SQL to create required table: CREATE TABLE IF NOT EXISTS `mw_ext_meta` ( -- replace "mw" with your prefix `pageid` int(8) NOT NULL, `rindex` tinyint(1) NOT NULL, `rfollow` tinyint(1) NOT NULL, `titlealias` varchar(255), `keywords` varchar(500), `description` varchar(2000), PRIMARY KEY (`pageid`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; */ /** * MediaWiki Advanced Meta extension * Add meta data to individual pages or entire namespaces * @version 1.4.0 * @author Stephan Muller <mail@litso.com> (Main author) * @author Bart van Heukelom <b@rtvh.nl> (Objectification) * @author Zayoo <zayoo@126.com> (Revise & TitleAlias, refer to Extension:Add HTML Meta and Title & Extension:TitleAlias) */ $wgExtensionCredits['parserhook'][] = array( 'name' => 'Advanced Meta', 'author' => 'Stephan Muller, Bart van Heukelom, Zayoo', 'descriptionmsg' => 'ameta-desc', 'url' => 'http://www.mediawiki.org/wiki/Extension:Advanced_Meta', 'version' => '1.4.0' ); $wgExtensionMessagesFiles['Advanced Meta'] = dirname(__FILE__) . '/MWAdvancedMeta.i18n.php'; MWAdvancedMeta::setup(); class MWAdvancedMeta { private static $instance = null; /** * Initialise the advanced meta plugin. * @return MWAdvancedMeta the plugin object, which you can use to set settings. */ public static function setup() { // check for wiki if (!defined('MEDIAWIKI')) { throw new Exception('This is an extension to the MediaWiki software and cannot be used standalone.'); } // create plugin if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } private $indexedPages = array(NS_MAIN, NS_PROJECT); private $allowedUsers = array(); private $allowedUsergroups = array('sysop', 'bureaucrat'); private $savedMeta = null; public function __construct() { /********* * Hooks * *********/ global $wgHooks; //Inserts HTML for meta input fields into the edit page. $wgHooks['ParserBeforeTidy'][] = $this; //Before the updated text and properties of an article are saved to the database //the new meta info is saved too $wgHooks['ArticleSave'][] = $this; //If a new article is created the meta is temporarily saved as article ID '0' //Move it to the newly created article now $wgHooks['ArticleInsertComplete'][] = $this; //Insert meta into article $wgHooks['OutputPageBeforeHTML'][] = $this; //Title Alias $wgHooks['BeforePageDisplay'][] = $this; } /** * All namespaces get a robots setting of "noindex, nofollow" by default. * Use this method to specify namespaces with interesting content, pages in which should be indexed. * Default is NS_MAIN and NS_PROJECT. * @param $indexedPages Array of namespace names. */ public function setIndexedPages(array $indexedPages) { $this->indexedPages = $indexedPages; } /** * Specify keywords that should be added to every page. * @param $keywords The keywords in an array. */ public function setGlobalKeywords($keywords) { $this->globalKeywords = $keywords; } /** * SEO can be a delicate issue. Define here who is allowed to edit the meta tags * allow users individually, by username. Warning: Case sensitive! */ public function setAllowedUsers($users) { $this->allowedUsers = $users; } /** * allow users by mediawiki's own usergroups * or the special groups 'loggedin' and 'all' ('all' allowing even anonymous edits) */ public function setAllowedUsergroups($usergroups) { $this->allowedUsergroups = $usergroups; } /********* * Hooks * *********/ /** * Hook 1: in /includes/parser/Parser.php * Loops through the different sections of the page being parsed * and adds html for the meta input forms into the article edit pages * * @param object $parser The parser object * @param string $text * * @global indexedpages defined in the global config section above * @global alloweusers defined in the global config section above * * @return true * */ public function onParserBeforeTidy(&$parser, &$text) { $fname = 'MWAdvancedMeta::onParserBeforeTidy'; global $wgTitle, $wgUser, $wgRequest; // only run this hook for edit forms if(!$wgRequest->getVal('action') == ('edit' || 'submit')) return false; // can this user edit meta for this page? if(!$this->canEditMeta()) return false; // handle submit (preview) if ($wgRequest->wasPosted()) { // it's probably a preview, so show the newly submitted meta too, otherwise we'll lose 'em $meta = array( 'rindex' => (isset($_POST['wpIndex'])) ? '1' : '0', 'rfollow' => (isset($_POST['wpFollow'])) ? '1' : '0', 'titlealias' => $_POST['wpTitleAlias'], 'keywords' => $_POST['wpKeywords'], 'description' => $_POST['wpDescription'] ); } else { // else just get the meta from the db $meta = $this->getMetaByArticleID($wgTitle->getArticleID()); // no meta or creating a new article? make default values if(empty($meta) || $wgTitle->getArticleID() == '0') { $meta = array( 'rindex' => '1', 'rfollow' => '1', 'titlealias' => '', 'keywords' => '', 'description' => '' ); } } // prepare checkboxes $checkindex = ($meta['rindex'] == '1') ? 'checked="checked"' : '' ; $checkfollow = ($meta['rfollow'] == '1') ? 'checked="checked"' : '' ; // some mediawiki preference variables $cols = $wgUser->getIntOption('cols'); $ew = $wgUser->getOption('editwidth'); $hidden = ""; //don't know when this is exactly used. If I find out, I'll do something with it :) $addedkeywords = wfMsg( 'globalkeywords' )=='<globalkeywords>' ? '(none)' : wfMsg( 'globalkeywords' ); // define replacements $replaceFrom[] = '<div id="editpage-copywarn">'; $replaceWith[] = "<h2>" . wfMsg( 'ameta-metasettings' ) . "</h2> <strong>Robots:</strong> <label><input tabindex='2' id='wpIndex' type='checkbox' name='wpIndex' value='1' {$checkindex} accesskey='/'> Index</label> <label><input tabindex='3' id='wpFollow' type='checkbox' name='wpFollow' value='1' {$checkfollow} > Follow</label> <br /><strong>" . wfMsg( 'ameta-titlealias' ) . "</strong><br /> <input type='text' name='wpTitleAlias' id='wpTitleAlias' value='{$meta['titlealias']}' size='64'> <br /><strong>Keywords:</strong> <small>" . wfMsg( 'ameta-keywordsadd' ) . "<a href='javascript:;' title='" . wfMsg( 'ameta-keywordsmodify' ) . "'>" . htmlspecialchars(str_replace("$1",$wgTitle,$addedkeywords))."</a> </small><br /> <textarea tabindex='4' name='wpKeywords' id='wpKeywords' rows='1' cols='{$cols}'{$ew} {$hidden}>{$meta['keywords']}</textarea> <br /><strong>Description:</strong><br /> <textarea tabindex='4' name='wpDescription' id='wpDescription' rows='2' cols='{$cols}'{$ew} {$hidden}>{$meta['description']}</textarea> <div id=\"editpage-copywarn\">"; // apply replacements $text = str_replace($replaceFrom, $replaceWith, $text); return true; } /** * Hook 2: Called during function doEdit() in /includes/Article.php * Adds the new meta information to the database when an article is saved * * @param object $article The entire article and it's properties * @param object $user The user updating the article * @return true * * @global indexedpages, array of namespaces that should be indexed * */ public function onArticleSave(&$article, &$user) { $fname = 'MWAdvancedMeta::onArticleSave'; $id = $article->mTitle->getArticleID(); //can this user edit meta for this page? if (!$this->canEditMeta()) return true; //return false gives edit conflicts // get meta from the posted forms $metaData = array( 'rindex' => isset($_POST['wpIndex']) ? '1' : '0', 'rfollow' => isset($_POST['wpFollow']) ? '1' : '0', 'titlealias' => htmlspecialchars($_POST['wpTitleAlias']), 'keywords' => htmlspecialchars($_POST['wpKeywords']), 'description' => htmlspecialchars($_POST['wpDescription']) ); if ($id == 0) { // if this is an insert, we need to store the meta until we know the page id $this->savedMeta = $metaData; } else { // write new metadata to the database $this->writeMeta($id, $metaData); } // empty cache for the page $dbw = wfGetDB( DB_MASTER ); $timestamp = $dbw->timestamp(); $dbw->update( 'page', array( 'page_touched' => $timestamp ), array( 'page_id' => $id ), $fname ); return true; } /** * Hook 3: Called during function doEdit() in /includes/Article.php * Move the new meta information from a temporary id='0' to the new article's id * * @param object $article: the article (object) saved * @param object $user: the user (object) who saved the article * @param string $text: the new article content * @param string $summary: the article summary (comment) * @param bool $minoredit: minor edit flag * @param bool $watchthis: not used as of 1.8 (automatically set to "null") * @param bool $sectionanchor: not used as of 1.8 (automatically set to "null") * @param unknown $flags: bitfield, see source code for details; passed to Article::doedit() * @param object $revision: The newly inserted revision object (as of 1.11.0) * @return true * * @global indexedpages, array of namespaces that should be indexed * */ function onArticleInsertComplete(&$article, &$user, $text, $summary, $minoredit, $watchthis, $sectionanchor, &$flags, $revision) { $fname = 'MWAdvancedMeta::onArticleInsertComplete'; // if we have saved metadata, insert it if($this->savedMeta !== null) { // write new metadata to the database $this->writeMeta($article->getID(), $this->savedMeta); $this->savedMeta = null; } return true; } /** * Hook 4: Called during function view() in /includes/OutputPage.php * Adds the proper meta tags to the article when a page is viewed * * @param object $out The outputted page * @param string $text The file description * @return true */ function onOutputPageBeforeHTML(&$out, &$text) { global $wgTitle; $fname = 'MWAdvancedMeta::onOutputPageBeforeHTML'; $addedkeywords = wfMsg( 'globalkeywords' )=='<globalkeywords>' ? '' : wfMsg( 'globalkeywords' , $wgTitle); // pages that aren't in the array indexedpages won't be indexed, but links will be followed if (!in_array($wgTitle->getnamespace(), $this->indexedPages)) { $out->setRobotPolicy('noindex, follow'); } else { $out->setRobotPolicy('index, follow'); } // for indexed namespaces, get the info from the db $meta = $this->getMetaByArticleID($wgTitle->getArticleID()); // nothing in db? use global keywords other than exit now if (empty($meta)) { $out->mKeywords = array_merge(explode(',',$addedkeywords), $out->mKeywords); //Global keywords should be shown even no meta data return false; } // set the keywords // only overwrite keywords if any were provided (else the ones generated by mediawiki are used) if (!empty($meta['keywords'])) { $out->mKeywords = array_merge(explode(',',$addedkeywords), explode(',', $meta['keywords'])); } else { $out->mKeywords = array_merge(explode(',',$addedkeywords), $out->mKeywords); } // set the description if(!empty($meta['description'])) $out->addMeta("description", $meta['description']); // set robots information $index = ($meta['rindex'] == '1') ? 'index' : 'noindex'; $follow = ($meta['rfollow'] == '1') ? 'follow' : 'nofollow'; $out->setRobotPolicy($index.', '.$follow); return true; } /** * Hook 5: Called during function output() in /includes/OutputPage.php * Allows last minute changes to the output page, e.g. adding of CSS or Javascript by extensions * * @param object $out The OutputPage object * @param string $sk Skin object that will be used to generate the page * @return true */ function onBeforePageDisplay(&$out, &$text) { global $wgTitle; $fname = 'MWAdvancedMeta::onBeforePageDisplay'; $meta = $this->getMetaByArticleID($wgTitle->getArticleID()); if (empty($meta)) return true; if (!empty($meta['titlealias'])) $out->mHTMLtitle=wfMsg( 'pagetitle', $meta['titlealias'] ); return true; } /************* * Functions * *************/ private function getMetaByArticleID($id) { $fname = 'MWAdvancedMeta::getMetaByArticleID'; // get metadata from database $dbr = wfGetDB( DB_MASTER ); $data = $dbr->select( 'ext_meta', array( 'rindex', 'rfollow', 'titlealias', 'keywords', 'description' ), array( 'pageid' => $id ), $fname ); $meta = $dbr->fetchRow( $data ); $dbr->freeResult($data); return $meta; } private function writeMeta($id, $metaData) { $fname = 'MWAdvancedMeta::writeMeta'; $dbw = wfGetDB( DB_MASTER ); if($metaData['rindex']=='1'&&$metaData['rfollow']=='1'&&empty($metaData['titlealias'])&&empty($metaData['keywords'])&&empty($metaData['description'])) { $dbw->delete( 'ext_meta', array( 'pageid' => $id ), __METHOD__ ); //delete meta with normal robot policies and without titlealias, keywords & description to save space } else { $dbw->replace( 'ext_meta', array( 'pageid' => $id), array_merge($metaData, array('pageid' => $id)), $fname ); } } private function canEditMeta() { global $wgUser, $wgTitle; $fname = 'MWAdvancedMeta::canEditMeta'; $ns = $wgTitle->getNamespace(); // redirect pages don't need metadata // TODO: make work in MediaWiki < 1.13 // if ($wgTitle->isRedirect()) { // return false; // } // does the user have permission? return (in_array($wgUser->getName(), $this->allowedUsers) || in_array('all', $this->allowedUsergroups) || (in_array('loggedin', $this->allowedUsergroups) && $wgUser->isLoggedIn()) || count(array_intersect($wgUser->getGroups(), $this->allowedUsergroups)) !== 0 ); } }
[edit] Code: MWAdvancedMeta.i18n.php
<?php /* * Public Domain */ $messages = array(); $messages['en']=array( 'ameta-desc'=>'Allows per page meta settings (robots, keywords, description) and change title', 'ameta-metasettings'=>'Meta settings', 'ameta-titlealias'=>'Title Alias:', 'ameta-keywordsadd'=>'the following keywords are added to all pages: ', 'ameta-keywordsmodify'=>'can be modified at Mediawiki:Globalkeywords', ); $messages['de']=array( 'ameta-desc'=>'Ermöglicht das Festlegen der Meta-Elemente „robots“, „keywords“ und „description“ sowie das Anpassen des Seitentitels', 'ameta-metasettings'=>'Meta-Einstellungen', 'ameta-titlealias'=>'Seitentitelalias:', 'ameta-keywordsadd'=>'Die folgenden „keywords“ werden allen Seiten hinzugefügt: ', 'ameta-keywordsmodify'=>'Sie können auf der Seite Mediawiki:Globalkeywords angepasst werden.', ); $messages['zh-hans']=array( 'ameta-desc'=>'允许为每个页面设置机器人政策、关键词和描述,以及修改标题', 'ameta-metasettings'=>'Meta设置', 'ameta-titlealias'=>'修改标题:', 'ameta-keywordsadd'=>'以下内容自动作为关键词添加: ', 'ameta-keywordsmodify'=>'可在 Mediawiki:Globalkeywords 修改全局关键词', ); $messages['zh-hant']=array( 'ameta-desc'=>'允許為每個頁面設置機器人政策、關鍵字和描述,以及修改標題', 'ameta-metasettings'=>'Meta設置', 'ameta-titlealias'=>'修改標題:', 'ameta-keywordsadd'=>'以下內容自動作為關鍵字添加: ', 'ameta-keywordsmodify'=>'可在 Mediawiki:Globalkeywords 修改全域關鍵字', );