diff options
Diffstat (limited to 'www/wiki/extensions/Translate/tag/TranslatablePage.php')
-rw-r--r-- | www/wiki/extensions/Translate/tag/TranslatablePage.php | 231 |
1 files changed, 128 insertions, 103 deletions
diff --git a/www/wiki/extensions/Translate/tag/TranslatablePage.php b/www/wiki/extensions/Translate/tag/TranslatablePage.php index c0236b98..51a32db9 100644 --- a/www/wiki/extensions/Translate/tag/TranslatablePage.php +++ b/www/wiki/extensions/Translate/tag/TranslatablePage.php @@ -4,9 +4,11 @@ * * @file * @author Niklas Laxström - * @license GPL-2.0+ + * @license GPL-2.0-or-later */ +use Wikimedia\Rdbms\Database; + /** * Class to parse translatable wiki pages. * @@ -61,8 +63,6 @@ class TranslatablePage { $this->title = $title; } - // Public constructors // - /** * Constructs a translatable page from given text. * Some functions will fail unless you set revision @@ -71,7 +71,7 @@ class TranslatablePage { * @param Title $title * @param string $text * - * @return TranslatablePage + * @return self */ public static function newFromText( Title $title, $text ) { $obj = new self( $title ); @@ -87,9 +87,9 @@ class TranslatablePage { * behavior will happen. * * @param Title $title - * @param integer $revision Revision number + * @param int $revision Revision number * @throws MWException - * @return TranslatablePage + * @return self */ public static function newFromRevision( Title $title, $revision ) { $rev = Revision::newFromTitle( $title, $revision ); @@ -109,7 +109,7 @@ class TranslatablePage { * The text of last marked revision is loaded when neded. * * @param Title $title - * @return TranslatablePage + * @return self */ public static function newFromTitle( Title $title ) { $obj = new self( $title ); @@ -118,8 +118,6 @@ class TranslatablePage { return $obj; } - // Getters // - /** * Returns the title for this translatable page. * @return Title @@ -159,7 +157,7 @@ class TranslatablePage { /** * Revision is null if object was constructed using newFromText. - * @return null or integer + * @return null|int */ public function getRevision() { return $this->revision; @@ -167,7 +165,7 @@ class TranslatablePage { /** * Manually set a revision number to use loading page text. - * @param integer $revision + * @param int $revision */ public function setRevision( $revision ) { $this->revision = $revision; @@ -175,8 +173,6 @@ class TranslatablePage { $this->init = false; } - // Public functions // - /** * Returns the source language of this translatable page. In other words * the language in which the page without language code is written. @@ -216,7 +212,7 @@ class TranslatablePage { /** * Check whether title is marked for translation * @return bool - * @return 2014.06 + * @since 2014.06 */ public function hasPageDisplayTitle() { // Cached value @@ -256,7 +252,7 @@ class TranslatablePage { return null; } - return $group->getMessage( "$page/$section", $code ); + return $group->getMessage( "$page/$section", $code, $group::READ_NORMAL ); } /** @@ -272,10 +268,10 @@ class TranslatablePage { $text = $this->getText(); - $nowiki = array(); + $nowiki = []; $text = self::armourNowiki( $nowiki, $text ); - $sections = array(); + $sections = []; // Add section to allow translating the page name $displaytitle = new TPSection; @@ -283,11 +279,11 @@ class TranslatablePage { $displaytitle->text = $this->getTitle()->getPrefixedText(); $sections[TranslateUtils::getPlaceholder()] = $displaytitle; - $tagPlaceHolders = array(); + $tagPlaceHolders = []; while ( true ) { - $re = '~(<translate>)\s*(.*?)(</translate>)~s'; - $matches = array(); + $re = '~(<translate>)(.*?)(</translate>)~s'; + $matches = []; $ok = preg_match_all( $re, $text, $matches, PREG_OFFSET_CAPTURE ); if ( $ok === 0 ) { @@ -311,15 +307,16 @@ class TranslatablePage { $sectiontext = substr( $contents, $start, $len ); if ( strpos( $sectiontext, '<translate>' ) !== false ) { - throw new TPException( array( 'pt-parse-nested', $sectiontext ) ); + throw new TPException( [ 'pt-parse-nested', $sectiontext ] ); } $sectiontext = self::unArmourNowiki( $nowiki, $sectiontext ); - $ret = $this->sectionise( $sections, $sectiontext ); + $parse = self::sectionise( $sectiontext ); + $sections += $parse['sections']; $tagPlaceHolders[$ph] = - self::index_replace( $contents, $ret, $start, $end ); + self::index_replace( $contents, $parse['template'], $start, $end ); } $prettyTemplate = $text; @@ -328,9 +325,9 @@ class TranslatablePage { } if ( strpos( $text, '<translate>' ) !== false ) { - throw new TPException( array( 'pt-parse-open', $prettyTemplate ) ); + throw new TPException( [ 'pt-parse-open', $prettyTemplate ] ); } elseif ( strpos( $text, '</translate>' ) !== false ) { - throw new TPException( array( 'pt-parse-close', $prettyTemplate ) ); + throw new TPException( [ 'pt-parse-close', $prettyTemplate ] ); } foreach ( $tagPlaceHolders as $ph => $value ) { @@ -339,7 +336,7 @@ class TranslatablePage { if ( count( $sections ) === 1 ) { // Don't return display title for pages which have no sections - $sections = array(); + $sections = []; } $text = self::unArmourNowiki( $nowiki, $text ); @@ -354,10 +351,28 @@ class TranslatablePage { return $parse; } - // Inner functionality // + /** + * Remove all opening and closing translate tags following the same whitespace rules + * as the regular parsing. The difference is that this doesn't try to parse the page, + * so it can handle unbalanced tags. + * + * @param string $text Wikitext + * @return string Wikitext without translate tags. + */ + public static function cleanupTags( $text ) { + $nowiki = []; + $text = self::armourNowiki( $nowiki, $text ); + $text = preg_replace( '~<translate>\n?~s', '', $text ); + $text = preg_replace( '~\n?</translate>~s', '', $text ); + // Mirroring what TPSection::getTextForTrans does + $text = preg_replace( '~<tvar\|([^>]+)>(.*?)</>~u', '\2', $text ); + + $text = self::unArmourNowiki( $nowiki, $text ); + return $text; + } /** - * @param array $holders + * @param array &$holders * @param string $text * @return string */ @@ -374,7 +389,7 @@ class TranslatablePage { } /** - * @param $holders + * @param array $holders * @param string $text * @return mixed */ @@ -400,27 +415,36 @@ class TranslatablePage { /** * Splits the content marked with \<translate> tags into sections, which * are separated with with two or more newlines. Extra whitespace is captured - * in the template and not included in the sections. - * @param array $sections Array of placeholder => TPSection. + * in the template and is not included in the sections. + * * @param string $text Contents of one pair of \<translate> tags. - * @return string Template with placeholders for sections, which itself are added to $sections. + * @return array Contains a template and array of unparsed sections. */ - protected function sectionise( &$sections, $text ) { + public static function sectionise( $text ) { $flags = PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE; - $parts = preg_split( '~(\s*\n\n\s*|\s*$)~', $text, -1, $flags ); + $parts = preg_split( '~(^\s*|\s*\n\n\s*|\s*$)~', $text, -1, $flags ); + + $inline = preg_match( '~\n~', $text ) === 0; $template = ''; + $sections = []; + foreach ( $parts as $_ ) { if ( trim( $_ ) === '' ) { $template .= $_; } else { $ph = TranslateUtils::getPlaceholder(); - $sections[$ph] = $this->shakeSection( $_ ); + $tpsection = self::shakeSection( $_ ); + $tpsection->setIsInline( $inline ); + $sections[$ph] = $tpsection; $template .= $ph; } } - return $template; + return [ + 'template' => $template, + 'sections' => $sections, + ]; } /** @@ -435,13 +459,13 @@ class TranslatablePage { * @throws TPException * @return TPSection */ - protected function shakeSection( $content ) { + public static function shakeSection( $content ) { $re = '~<!--T:(.*?)-->~'; - $matches = array(); + $matches = []; $count = preg_match_all( $re, $content, $matches, PREG_SET_ORDER ); if ( $count > 1 ) { - throw new TPException( array( 'pt-shake-multiple', $content ) ); + throw new TPException( [ 'pt-shake-multiple', $content ] ); } $section = new TPSection; @@ -452,15 +476,15 @@ class TranslatablePage { // Currently handle only these two standard places. // Is this too strict? - $rer1 = '~^<!--T:(.*?)-->\n~'; // Normal sections + $rer1 = '~^<!--T:(.*?)-->( |\n)~'; // Normal sections $rer2 = '~\s*<!--T:(.*?)-->$~m'; // Sections with title $content = preg_replace( $rer1, '', $content ); $content = preg_replace( $rer2, '', $content ); if ( preg_match( $re, $content ) === 1 ) { - throw new TPException( array( 'pt-shake-position', $content ) ); + throw new TPException( [ 'pt-shake-position', $content ] ); } elseif ( trim( $content ) === '' ) { - throw new TPException( array( 'pt-shake-empty', $id ) ); + throw new TPException( [ 'pt-shake-empty', $id ] ); } } } else { @@ -473,14 +497,12 @@ class TranslatablePage { return $section; } - // Tag methods // - - protected static $tagCache = array(); + protected static $tagCache = []; /** * Adds a tag which indicates that this page is * suitable for translation. - * @param integer $revision + * @param int $revision * @param null|string $value */ public function addMarkedTag( $revision, $value = null ) { @@ -490,7 +512,7 @@ class TranslatablePage { /** * Adds a tag which indicates that this page source is * ready for marking for translation. - * @param integer $revision + * @param int $revision */ public function addReadyTag( $revision ) { $this->addTag( 'tp:tag', $revision ); @@ -499,7 +521,7 @@ class TranslatablePage { /** * @param string $tag Tag name * @param int $revision Revision ID to add tag for - * @param mixed $value Optional. Value to be stored as serialized with | as separator + * @param mixed|null $value Optional. Value to be stored as serialized with | as separator * @throws MWException */ protected function addTag( $tag, $revision, $value = null ) { @@ -511,11 +533,11 @@ class TranslatablePage { throw new MWException( 'Got object, expected id' ); } - $conds = array( + $conds = [ 'rt_page' => $aid, 'rt_type' => RevTag::getType( $tag ), 'rt_revision' => $revision - ); + ]; $dbw->delete( 'revtag', $conds, __METHOD__ ); if ( $value !== null ) { @@ -529,7 +551,7 @@ class TranslatablePage { /** * Returns the latest revision which has marked tag, if any. - * @return integer|bool false + * @return int|bool false */ public function getMarkedTag() { return $this->getTag( 'tp:mark' ); @@ -551,25 +573,25 @@ class TranslatablePage { $aid = $this->getTitle()->getArticleID(); $dbw = wfGetDB( DB_MASTER ); - $conds = array( + $conds = [ 'rt_page' => $aid, - 'rt_type' => array( + 'rt_type' => [ RevTag::getType( 'tp:mark' ), RevTag::getType( 'tp:tag' ), - ), - ); + ], + ]; $dbw->delete( 'revtag', $conds, __METHOD__ ); - $dbw->delete( 'translate_sections', array( 'trs_page' => $aid ), __METHOD__ ); + $dbw->delete( 'translate_sections', [ 'trs_page' => $aid ], __METHOD__ ); unset( self::$tagCache[$aid] ); } /** - * @param $tag + * @param string $tag * @param int $dbt * @return int|bool False if tag is not found, else revision id */ - protected function getTag( $tag, $dbt = DB_SLAVE ) { + protected function getTag( $tag, $dbt = DB_REPLICA ) { if ( !$this->getTitle()->exists() ) { return false; } @@ -583,12 +605,12 @@ class TranslatablePage { $db = wfGetDB( $dbt ); - $conds = array( + $conds = [ 'rt_page' => $aid, 'rt_type' => RevTag::getType( $tag ), - ); + ]; - $options = array( 'ORDER BY' => 'rt_revision DESC' ); + $options = [ 'ORDER BY' => 'rt_revision DESC' ]; $value = $db->selectField( 'revtag', 'rt_revision', $conds, __METHOD__, $options ); return $value === false ? $value : (int)$value; @@ -600,12 +622,12 @@ class TranslatablePage { * @return string Relative url */ public function getTranslationUrl( $code = false ) { - $params = array( + $params = [ 'group' => $this->getMessageGroupId(), 'action' => 'page', 'filter' => '', 'language' => $code, - ); + ]; $translate = SpecialPage::getTitleFor( 'Translate' ); @@ -615,12 +637,12 @@ class TranslatablePage { public function getMarkedRevs() { $db = TranslateUtils::getSafeReadDB(); - $fields = array( 'rt_revision', 'rt_value' ); - $conds = array( + $fields = [ 'rt_revision', 'rt_value' ]; + $conds = [ 'rt_page' => $this->getTitle()->getArticleID(), 'rt_type' => RevTag::getType( 'tp:mark' ), - ); - $options = array( 'ORDER BY' => 'rt_revision DESC' ); + ]; + $options = [ 'ORDER BY' => 'rt_revision DESC' ]; return $db->select( 'revtag', $fields, $conds, __METHOD__, $options ); } @@ -636,16 +658,16 @@ class TranslatablePage { $likePattern = $dbr->buildLike( $prefix, $dbr->anyString() ); $res = $dbr->select( 'page', - array( 'page_namespace', 'page_title' ), - array( + [ 'page_namespace', 'page_title' ], + [ 'page_namespace' => $this->getTitle()->getNamespace(), "page_title $likePattern" - ), + ], __METHOD__ ); $titles = TitleArray::newFromResult( $res ); - $filtered = array(); + $filtered = []; // Make sure we only get translation subpages while ignoring others $codes = Language::fetchLanguageNames(); @@ -670,10 +692,10 @@ class TranslatablePage { protected function getSections() { $dbr = TranslateUtils::getSafeReadDB(); - $conds = array( 'trs_page' => $this->getTitle()->getArticleID() ); + $conds = [ 'trs_page' => $this->getTitle()->getArticleID() ]; $res = $dbr->select( 'translate_sections', 'trs_key', $conds, __METHOD__ ); - $sections = array(); + $sections = []; foreach ( $res as $row ) { $sections[] = $row->trs_key; } @@ -701,11 +723,11 @@ class TranslatablePage { $like = $dbw->buildLike( "$base/", $dbw->anyString() ); } - $fields = array( 'page_namespace', 'page_title' ); - $conds = array( + $fields = [ 'page_namespace', 'page_title' ]; + $conds = [ 'page_namespace' => NS_TRANSLATIONS, 'page_title ' . $like - ); + ]; $res = $dbw->select( 'page', $fields, $conds, __METHOD__ ); // Only include pages which belong to this translatable page. @@ -713,7 +735,7 @@ class TranslatablePage { // translatable. Then when querying for Foo, we also get units // belonging to Foo/bar. $sections = array_flip( $this->getSections() ); - $units = array(); + $units = []; foreach ( $res as $row ) { $title = Title::newFromRow( $row ); @@ -746,12 +768,12 @@ class TranslatablePage { // Calculate percentages for the available translations $group = $this->getMessageGroup(); if ( !$group instanceof WikiPageMessageGroup ) { - return array(); + return []; } $titles = $this->getTranslationPages(); $temp = MessageGroupStats::forGroup( $this->getMessageGroupId() ); - $stats = array(); + $stats = []; foreach ( $titles as $t ) { $handle = new MessageHandle( $t ); @@ -779,18 +801,18 @@ class TranslatablePage { $db = TranslateUtils::getSafeReadDB(); $fields = 'rt_value'; - $conds = array( + $conds = [ 'rt_page' => $title->getArticleID(), 'rt_type' => RevTag::getType( 'tp:transver' ), - ); - $options = array( 'ORDER BY' => 'rt_revision DESC' ); + ]; + $options = [ 'ORDER BY' => 'rt_revision DESC' ]; return $db->selectField( 'revtag', $fields, $conds, __METHOD__, $options ); } /** * @param Title $title - * @return bool|TranslatablePage + * @return bool|self */ public static function isTranslationPage( Title $title ) { $handle = new MessageHandle( $title ); @@ -815,7 +837,7 @@ class TranslatablePage { return false; } - $page = TranslatablePage::newFromTitle( $newtitle ); + $page = self::newFromTitle( $newtitle ); if ( $page->getMarkedTag() === false ) { return false; @@ -833,39 +855,42 @@ class TranslatablePage { * @return bool */ public static function isSourcePage( Title $title ) { - static $cache = null; - - $cacheObj = wfGetCache( CACHE_ANYTHING ); - $cacheKey = wfMemcKey( 'pagetranslation', 'sourcepages' ); - - if ( $cache === null ) { - $cache = $cacheObj->get( $cacheKey ); - } - if ( !is_array( $cache ) ) { - $cache = self::getTranslatablePages(); - $cacheObj->set( $cacheKey, $cache, 60 * 5 ); - } + $cache = ObjectCache::getMainWANInstance(); + $pcTTL = $cache::TTL_PROC_LONG; + + $translatablePageIds = $cache->getWithSetCallback( + $cache->makeKey( 'pagetranslation', 'sourcepages' ), + $cache::TTL_MINUTE * 5, + function ( $oldValue, &$ttl, array &$setOpts ) { + $dbr = wfGetDB( DB_REPLICA ); + $setOpts += Database::getCacheSetOptions( $dbr ); + + return self::getTranslatablePages(); + }, + [ 'pcTTL' => $pcTTL, 'pcGroup' => __CLASS__ . ':30' ] + ); - return in_array( $title->getArticleID(), $cache ); + return in_array( $title->getArticleID(), $translatablePageIds ); } /** * Get a list of page ids where the latest revision is either tagged or marked + * @return array */ public static function getTranslatablePages() { $dbr = TranslateUtils::getSafeReadDB(); - $tables = array( 'revtag', 'page' ); + $tables = [ 'revtag', 'page' ]; $fields = 'rt_page'; - $conds = array( + $conds = [ 'rt_page = page_id', 'rt_revision = page_latest', - 'rt_type' => array( RevTag::getType( 'tp:mark' ), RevTag::getType( 'tp:tag' ) ), - ); - $options = array( 'GROUP BY' => 'rt_page' ); + 'rt_type' => [ RevTag::getType( 'tp:mark' ), RevTag::getType( 'tp:tag' ) ], + ]; + $options = [ 'GROUP BY' => 'rt_page' ]; $res = $dbr->select( $tables, $fields, $conds, __METHOD__, $options ); - $results = array(); + $results = []; foreach ( $res as $row ) { $results[] = $row->rt_page; } |