Index: branches/iwtransclusion/phase3v3/maintenance/archives/patch-globaltemplatelinks.sql |
— | — | @@ -17,15 +17,19 @@ |
18 | 18 | -- Needed for display purposes |
19 | 19 | gtl_from_title varchar(255) binary NOT NULL, |
20 | 20 | |
21 | | - -- The wiki ID of the wiki that hosts the transcluded page |
22 | | - gtl_to_wiki varchar(64) NOT NULL, |
| 21 | + -- The interwiki prefix of the wiki that hosts the transcluded page |
| 22 | + gtl_to_prefix varchar(32) NOT NULL, |
23 | 23 | |
24 | 24 | -- The namespace of the transcluded page on that wiki |
25 | 25 | gtl_to_namespace int NOT NULL, |
26 | 26 | |
| 27 | + -- The namespace name of transcluded page |
| 28 | + -- Needed for display purposes, since the local namespace ID doesn't necessarily match a distant one |
| 29 | + gtl_to_namespacetext varchar(255) NOT NULL, |
| 30 | + |
27 | 31 | -- The title of the transcluded page on that wiki |
28 | 32 | gtl_to_title varchar(255) binary NOT NULL |
29 | 33 | ) /*$wgDBTableOptions*/; |
30 | 34 | |
31 | | -CREATE UNIQUE INDEX /*i*/gtl_to_from ON /*_*/globaltemplatelinks (gtl_to_wiki, gtl_to_namespace, gtl_to_title, gtl_from_wiki, gtl_from_page); |
32 | | -CREATE UNIQUE INDEX /*i*/gtl_from_to ON /*_*/globaltemplatelinks (gtl_from_wiki, gtl_from_page, gtl_to_wiki, gtl_to_namespace, gtl_to_title); |
| 35 | +CREATE UNIQUE INDEX /*i*/gtl_to_from ON /*_*/globaltemplatelinks (gtl_to_prefix, gtl_to_namespace, gtl_to_title, gtl_from_wiki, gtl_from_page); |
| 36 | +CREATE UNIQUE INDEX /*i*/gtl_from_to ON /*_*/globaltemplatelinks (gtl_from_wiki, gtl_from_page, gtl_to_prefix, gtl_to_namespace, gtl_to_title); |
Index: branches/iwtransclusion/phase3v3/maintenance/language/messages.inc |
— | — | @@ -639,6 +639,9 @@ |
640 | 640 | 'templatesused', |
641 | 641 | 'templatesusedpreview', |
642 | 642 | 'templatesusedsection', |
| 643 | + 'distanttemplatesused', |
| 644 | + 'distanttemplatesusedpreview', |
| 645 | + 'distanttemplatesusedsection', |
643 | 646 | 'template-protected', |
644 | 647 | 'template-semiprotected', |
645 | 648 | 'hiddencategories', |
Index: branches/iwtransclusion/phase3v3/includes/Article.php |
— | — | @@ -1627,6 +1627,594 @@ |
1628 | 1628 | } |
1629 | 1629 | |
1630 | 1630 | /** |
| 1631 | + * Back-end article deletion |
| 1632 | + * Deletes the article with database consistency, writes logs, purges caches |
| 1633 | + * |
| 1634 | + * @param $reason string delete reason for deletion log |
| 1635 | + * @param suppress bitfield |
| 1636 | + * Revision::DELETED_TEXT |
| 1637 | + * Revision::DELETED_COMMENT |
| 1638 | + * Revision::DELETED_USER |
| 1639 | + * Revision::DELETED_RESTRICTED |
| 1640 | + * @param $id int article ID |
| 1641 | + * @param $commit boolean defaults to true, triggers transaction end |
| 1642 | + * @return boolean true if successful |
| 1643 | + */ |
| 1644 | + public function doDeleteArticle( $reason, $suppress = false, $id = 0, $commit = true ) { |
| 1645 | + global $wgDeferredUpdateList, $wgUseTrackbacks, $wgEnableInterwikiTemplatesTracking, $wgGlobalDatabase; |
| 1646 | + |
| 1647 | + wfDebug( __METHOD__ . "\n" ); |
| 1648 | + |
| 1649 | + if ( ! wfRunHooks( 'ArticleDelete', array( &$this, &$wgUser, &$reason, &$error ) ) ) { |
| 1650 | + return false; |
| 1651 | + } |
| 1652 | + $dbw = wfGetDB( DB_MASTER ); |
| 1653 | + $t = $this->mTitle->getDBkey(); |
| 1654 | + $id = $id ? $id : $this->mTitle->getArticleID( Title::GAID_FOR_UPDATE ); |
| 1655 | + |
| 1656 | + if ( $t === '' || $id == 0 ) { |
| 1657 | + return false; |
| 1658 | + } |
| 1659 | + |
| 1660 | + $u = new SiteStatsUpdate( 0, 1, - (int)$this->isCountable( $this->getRawText() ), -1 ); |
| 1661 | + array_push( $wgDeferredUpdateList, $u ); |
| 1662 | + |
| 1663 | + // Bitfields to further suppress the content |
| 1664 | + if ( $suppress ) { |
| 1665 | + $bitfield = 0; |
| 1666 | + // This should be 15... |
| 1667 | + $bitfield |= Revision::DELETED_TEXT; |
| 1668 | + $bitfield |= Revision::DELETED_COMMENT; |
| 1669 | + $bitfield |= Revision::DELETED_USER; |
| 1670 | + $bitfield |= Revision::DELETED_RESTRICTED; |
| 1671 | + } else { |
| 1672 | + $bitfield = 'rev_deleted'; |
| 1673 | + } |
| 1674 | + |
| 1675 | + $dbw->begin(); |
| 1676 | + // For now, shunt the revision data into the archive table. |
| 1677 | + // Text is *not* removed from the text table; bulk storage |
| 1678 | + // is left intact to avoid breaking block-compression or |
| 1679 | + // immutable storage schemes. |
| 1680 | + // |
| 1681 | + // For backwards compatibility, note that some older archive |
| 1682 | + // table entries will have ar_text and ar_flags fields still. |
| 1683 | + // |
| 1684 | + // In the future, we may keep revisions and mark them with |
| 1685 | + // the rev_deleted field, which is reserved for this purpose. |
| 1686 | + $dbw->insertSelect( 'archive', array( 'page', 'revision' ), |
| 1687 | + array( |
| 1688 | + 'ar_namespace' => 'page_namespace', |
| 1689 | + 'ar_title' => 'page_title', |
| 1690 | + 'ar_comment' => 'rev_comment', |
| 1691 | + 'ar_user' => 'rev_user', |
| 1692 | + 'ar_user_text' => 'rev_user_text', |
| 1693 | + 'ar_timestamp' => 'rev_timestamp', |
| 1694 | + 'ar_minor_edit' => 'rev_minor_edit', |
| 1695 | + 'ar_rev_id' => 'rev_id', |
| 1696 | + 'ar_text_id' => 'rev_text_id', |
| 1697 | + 'ar_text' => '\'\'', // Be explicit to appease |
| 1698 | + 'ar_flags' => '\'\'', // MySQL's "strict mode"... |
| 1699 | + 'ar_len' => 'rev_len', |
| 1700 | + 'ar_page_id' => 'page_id', |
| 1701 | + 'ar_deleted' => $bitfield |
| 1702 | + ), array( |
| 1703 | + 'page_id' => $id, |
| 1704 | + 'page_id = rev_page' |
| 1705 | + ), __METHOD__ |
| 1706 | + ); |
| 1707 | + |
| 1708 | + # Delete restrictions for it |
| 1709 | + $dbw->delete( 'page_restrictions', array ( 'pr_page' => $id ), __METHOD__ ); |
| 1710 | + |
| 1711 | + # Now that it's safely backed up, delete it |
| 1712 | + $dbw->delete( 'page', array( 'page_id' => $id ), __METHOD__ ); |
| 1713 | + $ok = ( $dbw->affectedRows() > 0 ); // getArticleId() uses slave, could be laggy |
| 1714 | + |
| 1715 | + if ( !$ok ) { |
| 1716 | + $dbw->rollback(); |
| 1717 | + return false; |
| 1718 | + } |
| 1719 | + |
| 1720 | + # Fix category table counts |
| 1721 | + $cats = array(); |
| 1722 | + $res = $dbw->select( 'categorylinks', 'cl_to', array( 'cl_from' => $id ), __METHOD__ ); |
| 1723 | + |
| 1724 | + foreach ( $res as $row ) { |
| 1725 | + $cats [] = $row->cl_to; |
| 1726 | + } |
| 1727 | + |
| 1728 | + $this->updateCategoryCounts( array(), $cats ); |
| 1729 | + |
| 1730 | + # If using cascading deletes, we can skip some explicit deletes |
| 1731 | + if ( !$dbw->cascadingDeletes() ) { |
| 1732 | + $dbw->delete( 'revision', array( 'rev_page' => $id ), __METHOD__ ); |
| 1733 | + |
| 1734 | + if ( $wgUseTrackbacks ) |
| 1735 | + $dbw->delete( 'trackbacks', array( 'tb_page' => $id ), __METHOD__ ); |
| 1736 | + |
| 1737 | + # Delete outgoing links |
| 1738 | + $dbw->delete( 'pagelinks', array( 'pl_from' => $id ) ); |
| 1739 | + $dbw->delete( 'imagelinks', array( 'il_from' => $id ) ); |
| 1740 | + $dbw->delete( 'categorylinks', array( 'cl_from' => $id ) ); |
| 1741 | + $dbw->delete( 'templatelinks', array( 'tl_from' => $id ) ); |
| 1742 | + $dbw->delete( 'externallinks', array( 'el_from' => $id ) ); |
| 1743 | + $dbw->delete( 'langlinks', array( 'll_from' => $id ) ); |
| 1744 | + $dbw->delete( 'iwlinks', array( 'iwl_from' => $id ) ); |
| 1745 | + $dbw->delete( 'redirect', array( 'rd_from' => $id ) ); |
| 1746 | + |
| 1747 | + if ( $wgEnableInterwikiTemplatesTracking && $wgGlobalDatabase ) { |
| 1748 | + $dbw2 = wfGetDB( DB_MASTER, array(), $wgGlobalDatabase ); |
| 1749 | + $dbw2->delete( 'globaltemplatelinks', |
| 1750 | + array( 'gtl_from_wiki' => wfWikiID( ), |
| 1751 | + 'gtl_from_page' => $id ) |
| 1752 | + ); |
| 1753 | + } |
| 1754 | + } |
| 1755 | + |
| 1756 | + # If using cleanup triggers, we can skip some manual deletes |
| 1757 | + if ( !$dbw->cleanupTriggers() ) { |
| 1758 | + # Clean up recentchanges entries... |
| 1759 | + $dbw->delete( 'recentchanges', |
| 1760 | + array( 'rc_type != ' . RC_LOG, |
| 1761 | + 'rc_namespace' => $this->mTitle->getNamespace(), |
| 1762 | + 'rc_title' => $this->mTitle->getDBkey() ), |
| 1763 | + __METHOD__ ); |
| 1764 | + $dbw->delete( 'recentchanges', |
| 1765 | + array( 'rc_type != ' . RC_LOG, 'rc_cur_id' => $id ), |
| 1766 | + __METHOD__ ); |
| 1767 | + } |
| 1768 | + |
| 1769 | + # Clear caches |
| 1770 | + Article::onArticleDelete( $this->mTitle ); |
| 1771 | + |
| 1772 | + # Clear the cached article id so the interface doesn't act like we exist |
| 1773 | + $this->mTitle->resetArticleID( 0 ); |
| 1774 | + |
| 1775 | + # Log the deletion, if the page was suppressed, log it at Oversight instead |
| 1776 | + $logtype = $suppress ? 'suppress' : 'delete'; |
| 1777 | + $log = new LogPage( $logtype ); |
| 1778 | + |
| 1779 | + # Make sure logging got through |
| 1780 | + $log->addEntry( 'delete', $this->mTitle, $reason, array() ); |
| 1781 | + |
| 1782 | + if ( $commit ) { |
| 1783 | + $dbw->commit(); |
| 1784 | + } |
| 1785 | + |
| 1786 | + wfRunHooks( 'ArticleDeleteComplete', array( &$this, &$wgUser, $reason, $id ) ); |
| 1787 | + return true; |
| 1788 | + } |
| 1789 | + |
| 1790 | + /** |
| 1791 | + * Roll back the most recent consecutive set of edits to a page |
| 1792 | + * from the same user; fails if there are no eligible edits to |
| 1793 | + * roll back to, e.g. user is the sole contributor. This function |
| 1794 | + * performs permissions checks on $wgUser, then calls commitRollback() |
| 1795 | + * to do the dirty work |
| 1796 | + * |
| 1797 | + * @param $fromP String: Name of the user whose edits to rollback. |
| 1798 | + * @param $summary String: Custom summary. Set to default summary if empty. |
| 1799 | + * @param $token String: Rollback token. |
| 1800 | + * @param $bot Boolean: If true, mark all reverted edits as bot. |
| 1801 | + * |
| 1802 | + * @param $resultDetails Array: contains result-specific array of additional values |
| 1803 | + * 'alreadyrolled' : 'current' (rev) |
| 1804 | + * success : 'summary' (str), 'current' (rev), 'target' (rev) |
| 1805 | + * |
| 1806 | + * @return array of errors, each error formatted as |
| 1807 | + * array(messagekey, param1, param2, ...). |
| 1808 | + * On success, the array is empty. This array can also be passed to |
| 1809 | + * OutputPage::showPermissionsErrorPage(). |
| 1810 | + */ |
| 1811 | + public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails ) { |
| 1812 | + global $wgUser; |
| 1813 | + |
| 1814 | + $resultDetails = null; |
| 1815 | + |
| 1816 | + # Check permissions |
| 1817 | + $editErrors = $this->mTitle->getUserPermissionsErrors( 'edit', $wgUser ); |
| 1818 | + $rollbackErrors = $this->mTitle->getUserPermissionsErrors( 'rollback', $wgUser ); |
| 1819 | + $errors = array_merge( $editErrors, wfArrayDiff2( $rollbackErrors, $editErrors ) ); |
| 1820 | + |
| 1821 | + if ( !$wgUser->matchEditToken( $token, array( $this->mTitle->getPrefixedText(), $fromP ) ) ) { |
| 1822 | + $errors[] = array( 'sessionfailure' ); |
| 1823 | + } |
| 1824 | + |
| 1825 | + if ( $wgUser->pingLimiter( 'rollback' ) || $wgUser->pingLimiter() ) { |
| 1826 | + $errors[] = array( 'actionthrottledtext' ); |
| 1827 | + } |
| 1828 | + |
| 1829 | + # If there were errors, bail out now |
| 1830 | + if ( !empty( $errors ) ) { |
| 1831 | + return $errors; |
| 1832 | + } |
| 1833 | + |
| 1834 | + return $this->commitRollback( $fromP, $summary, $bot, $resultDetails ); |
| 1835 | + } |
| 1836 | + |
| 1837 | + /** |
| 1838 | + * Backend implementation of doRollback(), please refer there for parameter |
| 1839 | + * and return value documentation |
| 1840 | + * |
| 1841 | + * NOTE: This function does NOT check ANY permissions, it just commits the |
| 1842 | + * rollback to the DB Therefore, you should only call this function direct- |
| 1843 | + * ly if you want to use custom permissions checks. If you don't, use |
| 1844 | + * doRollback() instead. |
| 1845 | + */ |
| 1846 | + public function commitRollback( $fromP, $summary, $bot, &$resultDetails ) { |
| 1847 | + global $wgUseRCPatrol, $wgUser, $wgLang; |
| 1848 | + |
| 1849 | + $dbw = wfGetDB( DB_MASTER ); |
| 1850 | + |
| 1851 | + if ( wfReadOnly() ) { |
| 1852 | + return array( array( 'readonlytext' ) ); |
| 1853 | + } |
| 1854 | + |
| 1855 | + # Get the last editor |
| 1856 | + $current = Revision::newFromTitle( $this->mTitle ); |
| 1857 | + if ( is_null( $current ) ) { |
| 1858 | + # Something wrong... no page? |
| 1859 | + return array( array( 'notanarticle' ) ); |
| 1860 | + } |
| 1861 | + |
| 1862 | + $from = str_replace( '_', ' ', $fromP ); |
| 1863 | + # User name given should match up with the top revision. |
| 1864 | + # If the user was deleted then $from should be empty. |
| 1865 | + if ( $from != $current->getUserText() ) { |
| 1866 | + $resultDetails = array( 'current' => $current ); |
| 1867 | + return array( array( 'alreadyrolled', |
| 1868 | + htmlspecialchars( $this->mTitle->getPrefixedText() ), |
| 1869 | + htmlspecialchars( $fromP ), |
| 1870 | + htmlspecialchars( $current->getUserText() ) |
| 1871 | + ) ); |
| 1872 | + } |
| 1873 | + |
| 1874 | + # Get the last edit not by this guy... |
| 1875 | + # Note: these may not be public values |
| 1876 | + $user = intval( $current->getRawUser() ); |
| 1877 | + $user_text = $dbw->addQuotes( $current->getRawUserText() ); |
| 1878 | + $s = $dbw->selectRow( 'revision', |
| 1879 | + array( 'rev_id', 'rev_timestamp', 'rev_deleted' ), |
| 1880 | + array( 'rev_page' => $current->getPage(), |
| 1881 | + "rev_user != {$user} OR rev_user_text != {$user_text}" |
| 1882 | + ), __METHOD__, |
| 1883 | + array( 'USE INDEX' => 'page_timestamp', |
| 1884 | + 'ORDER BY' => 'rev_timestamp DESC' ) |
| 1885 | + ); |
| 1886 | + if ( $s === false ) { |
| 1887 | + # No one else ever edited this page |
| 1888 | + return array( array( 'cantrollback' ) ); |
| 1889 | + } else if ( $s->rev_deleted & Revision::DELETED_TEXT || $s->rev_deleted & Revision::DELETED_USER ) { |
| 1890 | + # Only admins can see this text |
| 1891 | + return array( array( 'notvisiblerev' ) ); |
| 1892 | + } |
| 1893 | + |
| 1894 | + $set = array(); |
| 1895 | + if ( $bot && $wgUser->isAllowed( 'markbotedits' ) ) { |
| 1896 | + # Mark all reverted edits as bot |
| 1897 | + $set['rc_bot'] = 1; |
| 1898 | + } |
| 1899 | + |
| 1900 | + if ( $wgUseRCPatrol ) { |
| 1901 | + # Mark all reverted edits as patrolled |
| 1902 | + $set['rc_patrolled'] = 1; |
| 1903 | + } |
| 1904 | + |
| 1905 | + if ( count( $set ) ) { |
| 1906 | + $dbw->update( 'recentchanges', $set, |
| 1907 | + array( /* WHERE */ |
| 1908 | + 'rc_cur_id' => $current->getPage(), |
| 1909 | + 'rc_user_text' => $current->getUserText(), |
| 1910 | + "rc_timestamp > '{$s->rev_timestamp}'", |
| 1911 | + ), __METHOD__ |
| 1912 | + ); |
| 1913 | + } |
| 1914 | + |
| 1915 | + # Generate the edit summary if necessary |
| 1916 | + $target = Revision::newFromId( $s->rev_id ); |
| 1917 | + if ( empty( $summary ) ) { |
| 1918 | + if ( $from == '' ) { // no public user name |
| 1919 | + $summary = wfMsgForContent( 'revertpage-nouser' ); |
| 1920 | + } else { |
| 1921 | + $summary = wfMsgForContent( 'revertpage' ); |
| 1922 | + } |
| 1923 | + } |
| 1924 | + |
| 1925 | + # Allow the custom summary to use the same args as the default message |
| 1926 | + $args = array( |
| 1927 | + $target->getUserText(), $from, $s->rev_id, |
| 1928 | + $wgLang->timeanddate( wfTimestamp( TS_MW, $s->rev_timestamp ), true ), |
| 1929 | + $current->getId(), $wgLang->timeanddate( $current->getTimestamp() ) |
| 1930 | + ); |
| 1931 | + $summary = wfMsgReplaceArgs( $summary, $args ); |
| 1932 | + |
| 1933 | + # Save |
| 1934 | + $flags = EDIT_UPDATE; |
| 1935 | + |
| 1936 | + if ( $wgUser->isAllowed( 'minoredit' ) ) { |
| 1937 | + $flags |= EDIT_MINOR; |
| 1938 | + } |
| 1939 | + |
| 1940 | + if ( $bot && ( $wgUser->isAllowedAny( 'markbotedits', 'bot' ) ) ) { |
| 1941 | + $flags |= EDIT_FORCE_BOT; |
| 1942 | + } |
| 1943 | + |
| 1944 | + # Actually store the edit |
| 1945 | + $status = $this->doEdit( $target->getText(), $summary, $flags, $target->getId() ); |
| 1946 | + if ( !empty( $status->value['revision'] ) ) { |
| 1947 | + $revId = $status->value['revision']->getId(); |
| 1948 | + } else { |
| 1949 | + $revId = false; |
| 1950 | + } |
| 1951 | + |
| 1952 | + wfRunHooks( 'ArticleRollbackComplete', array( $this, $wgUser, $target, $current ) ); |
| 1953 | + |
| 1954 | + $resultDetails = array( |
| 1955 | + 'summary' => $summary, |
| 1956 | + 'current' => $current, |
| 1957 | + 'target' => $target, |
| 1958 | + 'newid' => $revId |
| 1959 | + ); |
| 1960 | + |
| 1961 | + return array(); |
| 1962 | + } |
| 1963 | + |
| 1964 | + /** |
| 1965 | + * User interface for rollback operations |
| 1966 | + */ |
| 1967 | + public function rollback() { |
| 1968 | + global $wgUser, $wgOut, $wgRequest; |
| 1969 | + |
| 1970 | + $details = null; |
| 1971 | + |
| 1972 | + $result = $this->doRollback( |
| 1973 | + $wgRequest->getVal( 'from' ), |
| 1974 | + $wgRequest->getText( 'summary' ), |
| 1975 | + $wgRequest->getVal( 'token' ), |
| 1976 | + $wgRequest->getBool( 'bot' ), |
| 1977 | + $details |
| 1978 | + ); |
| 1979 | + |
| 1980 | + if ( in_array( array( 'actionthrottledtext' ), $result ) ) { |
| 1981 | + $wgOut->rateLimited(); |
| 1982 | + return; |
| 1983 | + } |
| 1984 | + |
| 1985 | + if ( isset( $result[0][0] ) && ( $result[0][0] == 'alreadyrolled' || $result[0][0] == 'cantrollback' ) ) { |
| 1986 | + $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) ); |
| 1987 | + $errArray = $result[0]; |
| 1988 | + $errMsg = array_shift( $errArray ); |
| 1989 | + $wgOut->addWikiMsgArray( $errMsg, $errArray ); |
| 1990 | + |
| 1991 | + if ( isset( $details['current'] ) ) { |
| 1992 | + $current = $details['current']; |
| 1993 | + |
| 1994 | + if ( $current->getComment() != '' ) { |
| 1995 | + $wgOut->addWikiMsgArray( 'editcomment', array( |
| 1996 | + Linker::formatComment( $current->getComment() ) ), array( 'replaceafter' ) ); |
| 1997 | + } |
| 1998 | + } |
| 1999 | + |
| 2000 | + return; |
| 2001 | + } |
| 2002 | + |
| 2003 | + # Display permissions errors before read-only message -- there's no |
| 2004 | + # point in misleading the user into thinking the inability to rollback |
| 2005 | + # is only temporary. |
| 2006 | + if ( !empty( $result ) && $result !== array( array( 'readonlytext' ) ) ) { |
| 2007 | + # array_diff is completely broken for arrays of arrays, sigh. |
| 2008 | + # Remove any 'readonlytext' error manually. |
| 2009 | + $out = array(); |
| 2010 | + foreach ( $result as $error ) { |
| 2011 | + if ( $error != array( 'readonlytext' ) ) { |
| 2012 | + $out [] = $error; |
| 2013 | + } |
| 2014 | + } |
| 2015 | + $wgOut->showPermissionsErrorPage( $out ); |
| 2016 | + |
| 2017 | + return; |
| 2018 | + } |
| 2019 | + |
| 2020 | + if ( $result == array( array( 'readonlytext' ) ) ) { |
| 2021 | + $wgOut->readOnlyPage(); |
| 2022 | + |
| 2023 | + return; |
| 2024 | + } |
| 2025 | + |
| 2026 | + $current = $details['current']; |
| 2027 | + $target = $details['target']; |
| 2028 | + $newId = $details['newid']; |
| 2029 | + $wgOut->setPageTitle( wfMsg( 'actioncomplete' ) ); |
| 2030 | + $wgOut->setRobotPolicy( 'noindex,nofollow' ); |
| 2031 | + |
| 2032 | + if ( $current->getUserText() === '' ) { |
| 2033 | + $old = wfMsg( 'rev-deleted-user' ); |
| 2034 | + } else { |
| 2035 | + $old = Linker::userLink( $current->getUser(), $current->getUserText() ) |
| 2036 | + . Linker::userToolLinks( $current->getUser(), $current->getUserText() ); |
| 2037 | + } |
| 2038 | + |
| 2039 | + $new = Linker::userLink( $target->getUser(), $target->getUserText() ) |
| 2040 | + . Linker::userToolLinks( $target->getUser(), $target->getUserText() ); |
| 2041 | + $wgOut->addHTML( wfMsgExt( 'rollback-success', array( 'parse', 'replaceafter' ), $old, $new ) ); |
| 2042 | + $wgOut->returnToMain( false, $this->mTitle ); |
| 2043 | + |
| 2044 | + if ( !$wgRequest->getBool( 'hidediff', false ) && !$wgUser->getBoolOption( 'norollbackdiff', false ) ) { |
| 2045 | + $de = new DifferenceEngine( $this->mTitle, $current->getId(), $newId, false, true ); |
| 2046 | + $de->showDiff( '', '' ); |
| 2047 | + } |
| 2048 | + } |
| 2049 | + |
| 2050 | + /** |
| 2051 | + * Do standard deferred updates after page view |
| 2052 | + */ |
| 2053 | + public function viewUpdates() { |
| 2054 | + global $wgDeferredUpdateList, $wgDisableCounters, $wgUser; |
| 2055 | + if ( wfReadOnly() ) { |
| 2056 | + return; |
| 2057 | + } |
| 2058 | + |
| 2059 | + # Don't update page view counters on views from bot users (bug 14044) |
| 2060 | + if ( !$wgDisableCounters && !$wgUser->isAllowed( 'bot' ) && $this->getID() ) { |
| 2061 | + $wgDeferredUpdateList[] = new ViewCountUpdate( $this->getID() ); |
| 2062 | + $wgDeferredUpdateList[] = new SiteStatsUpdate( 1, 0, 0 ); |
| 2063 | + } |
| 2064 | + |
| 2065 | + # Update newtalk / watchlist notification status |
| 2066 | + $wgUser->clearNotification( $this->mTitle ); |
| 2067 | + } |
| 2068 | + |
| 2069 | + /** |
| 2070 | + * Prepare text which is about to be saved. |
| 2071 | + * Returns a stdclass with source, pst and output members |
| 2072 | + */ |
| 2073 | + public function prepareTextForEdit( $text, $revid = null, User $user = null ) { |
| 2074 | + if ( $this->mPreparedEdit && $this->mPreparedEdit->newText == $text && $this->mPreparedEdit->revid == $revid ) { |
| 2075 | + // Already prepared |
| 2076 | + return $this->mPreparedEdit; |
| 2077 | + } |
| 2078 | + |
| 2079 | + global $wgParser; |
| 2080 | + |
| 2081 | + if( $user === null ) { |
| 2082 | + global $wgUser; |
| 2083 | + $user = $wgUser; |
| 2084 | + } |
| 2085 | + $popts = ParserOptions::newFromUser( $user ); |
| 2086 | + wfRunHooks( 'ArticlePrepareTextForEdit', array( $this, $popts ) ); |
| 2087 | + |
| 2088 | + $edit = (object)array(); |
| 2089 | + $edit->revid = $revid; |
| 2090 | + $edit->newText = $text; |
| 2091 | + $edit->pst = $this->preSaveTransform( $text, $user, $popts ); |
| 2092 | + $edit->popts = $this->getParserOptions(); |
| 2093 | + $edit->output = $wgParser->parse( $edit->pst, $this->mTitle, $edit->popts, true, true, $revid ); |
| 2094 | + $edit->oldText = $this->getRawText(); |
| 2095 | + |
| 2096 | + $this->mPreparedEdit = $edit; |
| 2097 | + |
| 2098 | + return $edit; |
| 2099 | + } |
| 2100 | + |
| 2101 | + /** |
| 2102 | + * Do standard deferred updates after page edit. |
| 2103 | + * Update links tables, site stats, search index and message cache. |
| 2104 | + * Purges pages that include this page if the text was changed here. |
| 2105 | + * Every 100th edit, prune the recent changes table. |
| 2106 | + * |
| 2107 | + * @private |
| 2108 | + * @param $text String: New text of the article |
| 2109 | + * @param $summary String: Edit summary |
| 2110 | + * @param $minoredit Boolean: Minor edit |
| 2111 | + * @param $timestamp_of_pagechange String timestamp associated with the page change |
| 2112 | + * @param $newid Integer: rev_id value of the new revision |
| 2113 | + * @param $changed Boolean: Whether or not the content actually changed |
| 2114 | + * @param $user User object: User doing the edit |
| 2115 | + */ |
| 2116 | + public function editUpdates( $text, $summary, $minoredit, $timestamp_of_pagechange, $newid, $changed = true, User $user = null ) { |
| 2117 | + global $wgDeferredUpdateList, $wgUser, $wgEnableParserCache; |
| 2118 | + |
| 2119 | + wfProfileIn( __METHOD__ ); |
| 2120 | + |
| 2121 | + # Parse the text |
| 2122 | + # Be careful not to double-PST: $text is usually already PST-ed once |
| 2123 | + if ( !$this->mPreparedEdit || $this->mPreparedEdit->output->getFlag( 'vary-revision' ) ) { |
| 2124 | + wfDebug( __METHOD__ . ": No prepared edit or vary-revision is set...\n" ); |
| 2125 | + $editInfo = $this->prepareTextForEdit( $text, $newid, $user ); |
| 2126 | + } else { |
| 2127 | + wfDebug( __METHOD__ . ": No vary-revision, using prepared edit...\n" ); |
| 2128 | + $editInfo = $this->mPreparedEdit; |
| 2129 | + } |
| 2130 | + |
| 2131 | + # Save it to the parser cache |
| 2132 | + if ( $wgEnableParserCache ) { |
| 2133 | + $parserCache = ParserCache::singleton(); |
| 2134 | + $parserCache->save( $editInfo->output, $this, $editInfo->popts ); |
| 2135 | + } |
| 2136 | + |
| 2137 | + # Update the links tables |
| 2138 | + $u = new LinksUpdate( $this->mTitle, $editInfo->output ); |
| 2139 | + $u->doUpdate(); |
| 2140 | + |
| 2141 | + wfRunHooks( 'ArticleEditUpdates', array( &$this, &$editInfo, $changed ) ); |
| 2142 | + |
| 2143 | + if ( wfRunHooks( 'ArticleEditUpdatesDeleteFromRecentchanges', array( &$this ) ) ) { |
| 2144 | + if ( 0 == mt_rand( 0, 99 ) ) { |
| 2145 | + // Flush old entries from the `recentchanges` table; we do this on |
| 2146 | + // random requests so as to avoid an increase in writes for no good reason |
| 2147 | + global $wgRCMaxAge; |
| 2148 | + |
| 2149 | + $dbw = wfGetDB( DB_MASTER ); |
| 2150 | + $cutoff = $dbw->timestamp( time() - $wgRCMaxAge ); |
| 2151 | + $dbw->delete( |
| 2152 | + 'recentchanges', |
| 2153 | + array( "rc_timestamp < '$cutoff'" ), |
| 2154 | + __METHOD__ |
| 2155 | + ); |
| 2156 | + } |
| 2157 | + } |
| 2158 | + |
| 2159 | + $id = $this->getID(); |
| 2160 | + $title = $this->mTitle->getPrefixedDBkey(); |
| 2161 | + $shortTitle = $this->mTitle->getDBkey(); |
| 2162 | + |
| 2163 | + if ( 0 == $id ) { |
| 2164 | + wfProfileOut( __METHOD__ ); |
| 2165 | + return; |
| 2166 | + } |
| 2167 | + |
| 2168 | + $u = new SiteStatsUpdate( 0, 1, $this->mGoodAdjustment, $this->mTotalAdjustment ); |
| 2169 | + array_push( $wgDeferredUpdateList, $u ); |
| 2170 | + $u = new SearchUpdate( $id, $title, $text ); |
| 2171 | + array_push( $wgDeferredUpdateList, $u ); |
| 2172 | + |
| 2173 | + # If this is another user's talk page, update newtalk |
| 2174 | + # Don't do this if $changed = false otherwise some idiot can null-edit a |
| 2175 | + # load of user talk pages and piss people off, nor if it's a minor edit |
| 2176 | + # by a properly-flagged bot. |
| 2177 | + if ( $this->mTitle->getNamespace() == NS_USER_TALK && $shortTitle != $wgUser->getTitleKey() && $changed |
| 2178 | + && !( $minoredit && $wgUser->isAllowed( 'nominornewtalk' ) ) |
| 2179 | + ) { |
| 2180 | + if ( wfRunHooks( 'ArticleEditUpdateNewTalk', array( &$this ) ) ) { |
| 2181 | + $other = User::newFromName( $shortTitle, false ); |
| 2182 | + if ( !$other ) { |
| 2183 | + wfDebug( __METHOD__ . ": invalid username\n" ); |
| 2184 | + } elseif ( User::isIP( $shortTitle ) ) { |
| 2185 | + // An anonymous user |
| 2186 | + $other->setNewtalk( true ); |
| 2187 | + } elseif ( $other->isLoggedIn() ) { |
| 2188 | + $other->setNewtalk( true ); |
| 2189 | + } else { |
| 2190 | + wfDebug( __METHOD__ . ": don't need to notify a nonexistent user\n" ); |
| 2191 | + } |
| 2192 | + } |
| 2193 | + } |
| 2194 | + |
| 2195 | + if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) { |
| 2196 | + MessageCache::singleton()->replace( $shortTitle, $text ); |
| 2197 | + } |
| 2198 | + |
| 2199 | + wfProfileOut( __METHOD__ ); |
| 2200 | + } |
| 2201 | + |
| 2202 | + /** |
| 2203 | + * Perform article updates on a special page creation. |
| 2204 | + * |
| 2205 | + * @param $rev Revision object |
| 2206 | + * |
| 2207 | + * @todo This is a shitty interface function. Kill it and replace the |
| 2208 | + * other shitty functions like editUpdates and such so it's not needed |
| 2209 | + * anymore. |
| 2210 | + */ |
| 2211 | + public function createUpdates( $rev ) { |
| 2212 | + $this->mGoodAdjustment = $this->isCountable( $rev->getText() ); |
| 2213 | + $this->mTotalAdjustment = 1; |
| 2214 | + $this->editUpdates( $rev->getText(), $rev->getComment(), |
| 2215 | + $rev->isMinor(), wfTimestamp(), $rev->getId(), true ); |
| 2216 | + } |
| 2217 | + |
| 2218 | + /** |
1631 | 2219 | * Generate the navigation links when browsing through an article revisions |
1632 | 2220 | * It shows the information as: |
1633 | 2221 | * Revision as of \<date\>; view current revision |
— | — | @@ -1894,6 +2482,166 @@ |
1895 | 2483 | } |
1896 | 2484 | |
1897 | 2485 | /** |
| 2486 | + * Return a list of templates used by this article. |
| 2487 | + * Uses the templatelinks table |
| 2488 | + * |
| 2489 | + * @return Array of Title objects |
| 2490 | + */ |
| 2491 | + public function getUsedTemplates() { |
| 2492 | + $result = array(); |
| 2493 | + $id = $this->mTitle->getArticleID(); |
| 2494 | + |
| 2495 | + if ( $id == 0 ) { |
| 2496 | + return array(); |
| 2497 | + } |
| 2498 | + |
| 2499 | + $dbr = wfGetDB( DB_SLAVE ); |
| 2500 | + $res = $dbr->select( array( 'templatelinks' ), |
| 2501 | + array( 'tl_namespace', 'tl_title' ), |
| 2502 | + array( 'tl_from' => $id ), |
| 2503 | + __METHOD__ ); |
| 2504 | + |
| 2505 | + if ( $res !== false ) { |
| 2506 | + foreach ( $res as $row ) { |
| 2507 | + $result[] = Title::makeTitle( $row->tl_namespace, $row->tl_title ); |
| 2508 | + } |
| 2509 | + } |
| 2510 | + |
| 2511 | + return $result; |
| 2512 | + } |
| 2513 | + |
| 2514 | + /** |
| 2515 | + * Return a list of distant templates used by this article. |
| 2516 | + * Uses the globaltemplatelinks table |
| 2517 | + * |
| 2518 | + * @return Array of Title objects |
| 2519 | + */ |
| 2520 | + public function getUsedDistantTemplates() { |
| 2521 | + global $wgGlobalDatabase; |
| 2522 | + |
| 2523 | + $result = array(); |
| 2524 | + |
| 2525 | + if ( $wgGlobalDatabase ) { |
| 2526 | + $id = $this->mTitle->getArticleID(); |
| 2527 | + |
| 2528 | + if ( $id == 0 ) { |
| 2529 | + return array(); |
| 2530 | + } |
| 2531 | + |
| 2532 | + $dbr = wfGetDB( DB_SLAVE, array(), $wgGlobalDatabase ); |
| 2533 | + $res = $dbr->select( array( 'globaltemplatelinks' ), |
| 2534 | + array( 'gtl_to_prefix', 'gtl_to_namespace', 'gtl_to_title' ), |
| 2535 | + array( 'gtl_from_wiki' => wfWikiID( ), 'gtl_from_page' => $id ), |
| 2536 | + __METHOD__ ); |
| 2537 | + |
| 2538 | + if ( $res !== false ) { |
| 2539 | + foreach ( $res as $row ) { |
| 2540 | + $result[] = Title::makeTitle( $row->gtl_to_namespace, $row->gtl_to_title, null, $row->gtl_to_prefix ); |
| 2541 | + } |
| 2542 | + } |
| 2543 | + |
| 2544 | + $dbr->freeResult( $res ); |
| 2545 | + } |
| 2546 | + |
| 2547 | + return $result; |
| 2548 | + } |
| 2549 | + |
| 2550 | + /** |
| 2551 | + * Returns a list of hidden categories this page is a member of. |
| 2552 | + * Uses the page_props and categorylinks tables. |
| 2553 | + * |
| 2554 | + * @return Array of Title objects |
| 2555 | + */ |
| 2556 | + public function getHiddenCategories() { |
| 2557 | + $result = array(); |
| 2558 | + $id = $this->mTitle->getArticleID(); |
| 2559 | + |
| 2560 | + if ( $id == 0 ) { |
| 2561 | + return array(); |
| 2562 | + } |
| 2563 | + |
| 2564 | + $dbr = wfGetDB( DB_SLAVE ); |
| 2565 | + $res = $dbr->select( array( 'categorylinks', 'page_props', 'page' ), |
| 2566 | + array( 'cl_to' ), |
| 2567 | + array( 'cl_from' => $id, 'pp_page=page_id', 'pp_propname' => 'hiddencat', |
| 2568 | + 'page_namespace' => NS_CATEGORY, 'page_title=cl_to' ), |
| 2569 | + __METHOD__ ); |
| 2570 | + |
| 2571 | + if ( $res !== false ) { |
| 2572 | + foreach ( $res as $row ) { |
| 2573 | + $result[] = Title::makeTitle( NS_CATEGORY, $row->cl_to ); |
| 2574 | + } |
| 2575 | + } |
| 2576 | + |
| 2577 | + return $result; |
| 2578 | + } |
| 2579 | + |
| 2580 | + /** |
| 2581 | + * Return an applicable autosummary if one exists for the given edit. |
| 2582 | + * @param $oldtext String: the previous text of the page. |
| 2583 | + * @param $newtext String: The submitted text of the page. |
| 2584 | + * @param $flags Int bitmask: a bitmask of flags submitted for the edit. |
| 2585 | + * @return string An appropriate autosummary, or an empty string. |
| 2586 | + */ |
| 2587 | + public static function getAutosummary( $oldtext, $newtext, $flags ) { |
| 2588 | + global $wgContLang; |
| 2589 | + |
| 2590 | + # Decide what kind of autosummary is needed. |
| 2591 | + |
| 2592 | + # Redirect autosummaries |
| 2593 | + $ot = Title::newFromRedirect( $oldtext ); |
| 2594 | + $rt = Title::newFromRedirect( $newtext ); |
| 2595 | + |
| 2596 | + if ( is_object( $rt ) && ( !is_object( $ot ) || !$rt->equals( $ot ) || $ot->getFragment() != $rt->getFragment() ) ) { |
| 2597 | + return wfMsgForContent( 'autoredircomment', $rt->getFullText() ); |
| 2598 | + } |
| 2599 | + |
| 2600 | + # New page autosummaries |
| 2601 | + if ( $flags & EDIT_NEW && strlen( $newtext ) ) { |
| 2602 | + # If they're making a new article, give its text, truncated, in the summary. |
| 2603 | + |
| 2604 | + $truncatedtext = $wgContLang->truncate( |
| 2605 | + str_replace( "\n", ' ', $newtext ), |
| 2606 | + max( 0, 200 - strlen( wfMsgForContent( 'autosumm-new' ) ) ) ); |
| 2607 | + |
| 2608 | + return wfMsgForContent( 'autosumm-new', $truncatedtext ); |
| 2609 | + } |
| 2610 | + |
| 2611 | + # Blanking autosummaries |
| 2612 | + if ( $oldtext != '' && $newtext == '' ) { |
| 2613 | + return wfMsgForContent( 'autosumm-blank' ); |
| 2614 | + } elseif ( strlen( $oldtext ) > 10 * strlen( $newtext ) && strlen( $newtext ) < 500 ) { |
| 2615 | + # Removing more than 90% of the article |
| 2616 | + |
| 2617 | + $truncatedtext = $wgContLang->truncate( |
| 2618 | + $newtext, |
| 2619 | + max( 0, 200 - strlen( wfMsgForContent( 'autosumm-replace' ) ) ) ); |
| 2620 | + |
| 2621 | + return wfMsgForContent( 'autosumm-replace', $truncatedtext ); |
| 2622 | + } |
| 2623 | + |
| 2624 | + # If we reach this point, there's no applicable autosummary for our case, so our |
| 2625 | + # autosummary is empty. |
| 2626 | + return ''; |
| 2627 | + } |
| 2628 | + |
| 2629 | + /** |
| 2630 | + * Add the primary page-view wikitext to the output buffer |
| 2631 | + * Saves the text into the parser cache if possible. |
| 2632 | + * Updates templatelinks if it is out of date. |
| 2633 | + * |
| 2634 | + * @param $text String |
| 2635 | + * @param $cache Boolean |
| 2636 | + * @param $parserOptions mixed ParserOptions object, or boolean false |
| 2637 | + */ |
| 2638 | + public function outputWikiText( $text, $cache = true, $parserOptions = false ) { |
| 2639 | + global $wgOut; |
| 2640 | + |
| 2641 | + $this->mParserOutput = $this->getOutputFromWikitext( $text, $cache, $parserOptions ); |
| 2642 | + $wgOut->addParserOutput( $this->mParserOutput ); |
| 2643 | + } |
| 2644 | + |
| 2645 | + /** |
1898 | 2646 | * This does all the heavy lifting for outputWikitext, except it returns the parser |
1899 | 2647 | * output instead of sending it straight to $wgOut. Makes things nice and simple for, |
1900 | 2648 | * say, embedding thread pages within a discussion system (LiquidThreads) |
Index: branches/iwtransclusion/phase3v3/includes/parser/Parser.php |
— | — | @@ -3051,7 +3051,7 @@ |
3052 | 3052 | * @private |
3053 | 3053 | */ |
3054 | 3054 | function braceSubstitution( $piece, $frame ) { |
3055 | | - global $wgContLang, $wgNonincludableNamespaces; |
| 3055 | + global $wgContLang, $wgNonincludableNamespaces, $wgEnableInterwikiTranscluding, $wgEnableInterwikiTemplatesTracking; |
3056 | 3056 | wfProfileIn( __METHOD__ ); |
3057 | 3057 | wfProfileIn( __METHOD__.'-setup' ); |
3058 | 3058 | |
— | — | @@ -3266,11 +3266,15 @@ |
3267 | 3267 | $text = "[[:$titleText]]"; |
3268 | 3268 | $found = true; |
3269 | 3269 | } |
3270 | | - } elseif ( $title->isTrans() ) { |
| 3270 | + } elseif ( $wgEnableInterwikiTranscluding && $title->isTrans() ) { |
3271 | 3271 | // TODO: Work by Peter17 in progress |
3272 | 3272 | |
3273 | 3273 | $text = Interwiki::interwikiTransclude( $title ); |
3274 | 3274 | |
| 3275 | + if ( $wgEnableInterwikiTemplatesTracking ) { |
| 3276 | + $this->registerDistantTemplate( $title ); |
| 3277 | + } |
| 3278 | + |
3275 | 3279 | if ( $text !== false ) { |
3276 | 3280 | # Preprocess it like a template |
3277 | 3281 | $text = $this->preprocessToDom( $text, self::PTD_FOR_INCLUSION ); |
— | — | @@ -3429,6 +3433,17 @@ |
3430 | 3434 | * @param Title $title |
3431 | 3435 | * @return mixed string or false |
3432 | 3436 | */ |
| 3437 | + function registerDistantTemplate( $title ) { |
| 3438 | + $stuff = Parser::distantTemplateCallback( $title, $this ); |
| 3439 | + $text = $stuff['text']; |
| 3440 | + $finalTitle = isset( $stuff['finalTitle'] ) ? $stuff['finalTitle'] : $title; |
| 3441 | + if ( isset( $stuff['deps'] ) ) { |
| 3442 | + foreach ( $stuff['deps'] as $dep ) { |
| 3443 | + $this->mOutput->addDistantTemplate( $dep['title'], $dep['page_id'], $dep['rev_id'] ); |
| 3444 | + } |
| 3445 | + } |
| 3446 | + } |
| 3447 | + |
3433 | 3448 | function fetchTemplate( $title ) { |
3434 | 3449 | $rv = $this->fetchTemplateAndTitle( $title ); |
3435 | 3450 | return $rv[0]; |
Index: branches/iwtransclusion/phase3v3/includes/parser/ParserOutput.php |
— | — | @@ -306,6 +306,31 @@ |
307 | 307 | $this->mTemplateIds[$ns][$dbk] = $rev_id; // For versioning |
308 | 308 | } |
309 | 309 | |
| 310 | + function addDistantTemplate( $title, $page_id, $rev_id ) { |
| 311 | + $prefix = $title->getInterwiki(); |
| 312 | + if ( $prefix !=='' ) { |
| 313 | + $ns = $title->getNamespace(); |
| 314 | + $dbk = $title->getDBkey(); |
| 315 | + |
| 316 | + if ( !isset( $this->mDistantTemplates[$prefix] ) ) { |
| 317 | + $this->mDistantTemplates[$prefix] = array(); |
| 318 | + } |
| 319 | + if ( !isset( $this->mDistantTemplates[$prefix][$ns] ) ) { |
| 320 | + $this->mDistantTemplates[$prefix][$ns] = array(); |
| 321 | + } |
| 322 | + $this->mDistantTemplates[$prefix][$ns][$dbk] = $page_id; |
| 323 | + |
| 324 | + // For versioning |
| 325 | + if ( !isset( $this->mDistantTemplateIds[$prefix] ) ) { |
| 326 | + $this->mDistantTemplateIds[$prefix] = array(); |
| 327 | + } |
| 328 | + if ( !isset( $this->mDistantTemplateIds[$prefix][$ns] ) ) { |
| 329 | + $this->mDistantTemplateIds[$prefix][$ns] = array(); |
| 330 | + } |
| 331 | + $this->mDistantTemplateIds[$prefix][$ns][$dbk] = $rev_id; |
| 332 | + } |
| 333 | + } |
| 334 | + |
310 | 335 | /** |
311 | 336 | * @param $title Title object, must be an interwiki link |
312 | 337 | * @throws MWException if given invalid input |
Index: branches/iwtransclusion/phase3v3/includes/Linker.php |
— | — | @@ -1646,6 +1646,48 @@ |
1647 | 1647 | } |
1648 | 1648 | |
1649 | 1649 | /** |
| 1650 | + * Returns HTML for the "templates used on this page" list. |
| 1651 | + * |
| 1652 | + * @param $templates Array of templates from Article::getUsedTemplate |
| 1653 | + * or similar |
| 1654 | + * @param $preview Boolean: whether this is for a preview |
| 1655 | + * @param $section Boolean: whether this is for a section edit |
| 1656 | + * @return String: HTML output |
| 1657 | + */ |
| 1658 | + public function formatDistantTemplates( $templates, $preview = false, $section = false ) { |
| 1659 | + wfProfileIn( __METHOD__ ); |
| 1660 | + |
| 1661 | + $outText = ''; |
| 1662 | + if ( count( $templates ) > 0 ) { |
| 1663 | + # Do a batch existence check |
| 1664 | + $batch = new LinkBatch; |
| 1665 | + foreach( $templates as $title ) { |
| 1666 | + $batch->addObj( $title ); |
| 1667 | + } |
| 1668 | + $batch->execute(); |
| 1669 | + |
| 1670 | + # Construct the HTML |
| 1671 | + $outText = '<div class="mw-templatesUsedExplanation">'; |
| 1672 | + if ( $preview ) { |
| 1673 | + $outText .= wfMsgExt( 'distanttemplatesusedpreview', array( 'parse' ), count( $templates ) ); |
| 1674 | + } elseif ( $section ) { |
| 1675 | + $outText .= wfMsgExt( 'distanttemplatesusedsection', array( 'parse' ), count( $templates ) ); |
| 1676 | + } else { |
| 1677 | + $outText .= wfMsgExt( 'distanttemplatesused', array( 'parse' ), count( $templates ) ); |
| 1678 | + } |
| 1679 | + $outText .= "</div><ul>\n"; |
| 1680 | + |
| 1681 | + usort( $templates, array( 'Title', 'compare' ) ); |
| 1682 | + foreach ( $templates as $titleObj ) { |
| 1683 | + $outText .= '<li>' . $this->link( $titleObj ) . '</li>'; |
| 1684 | + } |
| 1685 | + $outText .= '</ul>'; |
| 1686 | + } |
| 1687 | + wfProfileOut( __METHOD__ ); |
| 1688 | + return $outText; |
| 1689 | + } |
| 1690 | + |
| 1691 | + /** |
1650 | 1692 | * Returns HTML for the "hidden categories on this page" list. |
1651 | 1693 | * |
1652 | 1694 | * @param $hiddencats Array of hidden categories from Article::getHiddenCategories |
Index: branches/iwtransclusion/phase3v3/includes/db/Database.php |
— | — | @@ -1777,6 +1777,31 @@ |
1778 | 1778 | return false; |
1779 | 1779 | } |
1780 | 1780 | } |
| 1781 | + |
| 1782 | + /** |
| 1783 | + * Build a partial where clause from a 3-d array |
| 1784 | + * The keys on each level may be either integers or strings. |
| 1785 | + * |
| 1786 | + * @param $data Array: organized as 3-d array(baseKeyVal => array(middleKeyVal => array(subKeyVal => <ignored>, ...), ...), ...) |
| 1787 | + * @param $baseKey String: field name to match the base-level keys to (eg 'gtl_to_prefix') |
| 1788 | + * @param $middleKey String: field name to match the middle-level keys to (eg 'gtl_to_namespace') |
| 1789 | + * @param $subKey String: field name to match the sub-level keys to (eg 'gtl_to_title') |
| 1790 | + * @return Mixed: string SQL fragment, or false if no items in array. |
| 1791 | + */ |
| 1792 | + function makeWhereFrom3d( $data, $baseKey, $middleKey, $subKey ) { |
| 1793 | + $conds = array(); |
| 1794 | + foreach ( $data as $base => $subdata ) { |
| 1795 | + foreach ( $subdata as $middle => $sub ) { |
| 1796 | + if ( count( $sub ) ) { |
| 1797 | + $conds[] = $this->makeList( |
| 1798 | + array( $baseKey => $base, |
| 1799 | + $middleKey => $middle, |
| 1800 | + $subKey => array_keys( $sub ) ), |
| 1801 | + LIST_AND |
| 1802 | + ); |
| 1803 | + } |
| 1804 | + } |
| 1805 | + } |
1781 | 1806 | |
1782 | 1807 | /** |
1783 | 1808 | * Bitwise operations |
Property changes on: branches/iwtransclusion/phase3v3/includes/db/Database.php |
___________________________________________________________________ |
Modified: svn:mergeinfo |
1784 | 1809 | Merged /branches/iwtransclusion/phase3/includes/db/Database.php:r70764 |
1785 | 1810 | Merged /branches/iwtransclusion/phase3v2/includes/db/Database.php:r87108 |
Index: branches/iwtransclusion/phase3v3/includes/EditPage.php |
— | — | @@ -1319,8 +1319,8 @@ |
1320 | 1320 | * @param $formCallback Callback that takes an OutputPage parameter; will be called |
1321 | 1321 | * during form output near the top, for captchas and the like. |
1322 | 1322 | */ |
1323 | | - function showEditForm( $formCallback = null ) { |
1324 | | - global $wgOut, $wgUser; |
| 1323 | + function showEditForm( $formCallback=null ) { |
| 1324 | + global $wgOut, $wgUser, $wgTitle, $wgEnableInterwikiTranscluding, $wgEnableInterwikiTemplatesTracking; |
1325 | 1325 | |
1326 | 1326 | wfProfileIn( __METHOD__ ); |
1327 | 1327 | |
— | — | @@ -1365,6 +1365,9 @@ |
1366 | 1366 | |
1367 | 1367 | $templates = $this->getTemplates(); |
1368 | 1368 | $formattedtemplates = Linker::formatTemplates( $templates, $this->preview, $this->section != ''); |
| 1369 | + |
| 1370 | + $distantTemplates = $this->getDistantTemplates(); |
| 1371 | + $formattedDistantTemplates = $sk->formatDistantTemplates( $distantTemplates, $this->preview, $this->section != '' ); |
1369 | 1372 | |
1370 | 1373 | $hiddencats = $this->mArticle->getHiddenCategories(); |
1371 | 1374 | $formattedhiddencats = Linker::formatHiddenCategories( $hiddencats ); |
— | — | @@ -1464,6 +1467,21 @@ |
1465 | 1468 | <div class='templatesUsed'> |
1466 | 1469 | {$formattedtemplates} |
1467 | 1470 | </div> |
| 1471 | +HTML |
| 1472 | +); |
| 1473 | + |
| 1474 | + if ( $wgEnableInterwikiTranscluding && $wgEnableInterwikiTemplatesTracking ) { |
| 1475 | + $wgOut->addHTML( <<<HTML |
| 1476 | +{$this->editFormTextAfterTools} |
| 1477 | +<div class='distantTemplatesUsed'> |
| 1478 | +{$formattedDistantTemplates} |
| 1479 | +</div> |
| 1480 | +HTML |
| 1481 | +); |
| 1482 | + } |
| 1483 | + |
| 1484 | + $wgOut->addHTML( <<<HTML |
| 1485 | +{$this->editFormTextAfterTools} |
1468 | 1486 | <div class='hiddencats'> |
1469 | 1487 | {$formattedhiddencats} |
1470 | 1488 | </div> |
— | — | @@ -2119,6 +2137,28 @@ |
2120 | 2138 | return $this->mArticle->getUsedTemplates(); |
2121 | 2139 | } |
2122 | 2140 | } |
| 2141 | + |
| 2142 | + function getDistantTemplates() { |
| 2143 | + global $wgEnableInterwikiTemplatesTracking; |
| 2144 | + if ( !$wgEnableInterwikiTemplatesTracking ) { |
| 2145 | + return array( ); |
| 2146 | + } |
| 2147 | + if ( $this->preview || $this->section != '' ) { |
| 2148 | + $templates = array(); |
| 2149 | + if ( !isset( $this->mParserOutput ) ) return $templates; |
| 2150 | + $templatesList = $this->mParserOutput->getDistantTemplates(); |
| 2151 | + foreach( $templatesList as $prefix => $templatesbyns ) { |
| 2152 | + foreach( $templatesbyns as $ns => $template ) { |
| 2153 | + foreach( array_keys( $template ) as $dbk ) { |
| 2154 | + $templates[] = Title::makeTitle( $ns, $dbk, null, $prefix ); |
| 2155 | + } |
| 2156 | + } |
| 2157 | + } |
| 2158 | + return $templates; |
| 2159 | + } else { |
| 2160 | + return $this->mArticle->getUsedDistantTemplates(); |
| 2161 | + } |
| 2162 | + } |
2123 | 2163 | |
2124 | 2164 | /** |
2125 | 2165 | * Call the stock "user is blocked" page |
Index: branches/iwtransclusion/phase3v3/includes/OutputPage.php |
— | — | @@ -2095,6 +2095,9 @@ |
2096 | 2096 | * @param $action String: action that was denied or null if unknown |
2097 | 2097 | */ |
2098 | 2098 | public function readOnlyPage( $source = null, $protected = false, $reasons = array(), $action = null ) { |
| 2099 | + global $wgUser, $wgEnableInterwikiTranscluding, $wgEnableInterwikiTemplatesTracking; |
| 2100 | + $skin = $wgUser->getSkin(); |
| 2101 | + |
2099 | 2102 | $this->setRobotPolicy( 'noindex,nofollow' ); |
2100 | 2103 | $this->setArticleRelated( false ); |
2101 | 2104 | |
— | — | @@ -2140,6 +2143,12 @@ |
2141 | 2144 | $templates |
2142 | 2145 | </div> |
2143 | 2146 | " ); |
| 2147 | + if ( $wgEnableInterwikiTranscluding && $wgEnableInterwikiTemplatesTracking ) { |
| 2148 | + $this->addHTML( "<div class='distantTemplatesUsed'> |
| 2149 | +{$skin->formatDistantTemplates( $article->getUsedDistantTemplates( ) )} |
| 2150 | +</div> |
| 2151 | +" ); |
| 2152 | + } |
2144 | 2153 | } |
2145 | 2154 | |
2146 | 2155 | # If the title doesn't exist, it's fairly pointless to print a return |
Property changes on: branches/iwtransclusion/phase3v3/includes/OutputPage.php |
___________________________________________________________________ |
Modified: svn:mergeinfo |
2147 | 2156 | Merged /branches/iwtransclusion/phase3/includes/OutputPage.php:r70764 |
2148 | 2157 | Merged /branches/iwtransclusion/phase3v2/includes/OutputPage.php:r87108 |
Index: branches/iwtransclusion/phase3v3/includes/Title.php |
— | — | @@ -21,6 +21,7 @@ |
22 | 22 | */ |
23 | 23 | |
24 | 24 | /** |
| 25 | + |
25 | 26 | * Represents a title within MediaWiki. |
26 | 27 | * Optionally may contain an interwiki designation or namespace. |
27 | 28 | * @note This class can fetch various kinds of data from the database; |
— | — | @@ -41,13 +42,7 @@ |
42 | 43 | */ |
43 | 44 | const CACHE_MAX = 1000; |
44 | 45 | |
45 | | - /** |
46 | | - * Used to be GAID_FOR_UPDATE define. Used with getArticleID() and friends |
47 | | - * to use the master DB |
48 | | - */ |
49 | | - const GAID_FOR_UPDATE = 1; |
50 | 46 | |
51 | | - |
52 | 47 | /** |
53 | 48 | * @name Private member variables |
54 | 49 | * Please use the accessor functions instead. |
— | — | @@ -73,11 +68,11 @@ |
74 | 69 | var $mCascadeSources; ///< Where are the cascading restrictions coming from on this page? |
75 | 70 | var $mRestrictionsLoaded = false; ///< Boolean for initialisation on demand |
76 | 71 | var $mPrefixedText; ///< Text form including namespace/interwiki, initialised on demand |
77 | | - var $mTitleProtection; ///< Cached value for getTitleProtection (create protection) |
| 72 | + var $mTitleProtection; ///< Cached value of getTitleProtection |
78 | 73 | # Don't change the following default, NS_MAIN is hardcoded in several |
79 | 74 | # places. See bug 696. |
80 | 75 | var $mDefaultNamespace = NS_MAIN; // /< Namespace index when there is no namespace |
81 | | - # Zero except in {{transclusion}} tags |
| 76 | + # Zero except in {{transclusion}} tags |
82 | 77 | var $mWatched = null; // /< Is $wgUser watching this page? null if unfilled, accessed through userIsWatching() |
83 | 78 | var $mLength = -1; // /< The page length, 0 for special pages |
84 | 79 | var $mRedirect = null; // /< Is the article at this title a redirect? |
— | — | @@ -88,16 +83,17 @@ |
89 | 84 | |
90 | 85 | /** |
91 | 86 | * Constructor |
| 87 | + * @private |
92 | 88 | */ |
93 | | - /*protected*/ function __construct() { } |
| 89 | + /* private */ function __construct() { } |
94 | 90 | |
95 | 91 | /** |
96 | 92 | * Create a new Title from a prefixed DB key |
97 | 93 | * |
98 | | - * @param $key String the database key, which has underscores |
| 94 | + * @param $key \type{\string} The database key, which has underscores |
99 | 95 | * instead of spaces, possibly including namespace and |
100 | 96 | * interwiki prefixes |
101 | | - * @return Title, or NULL on an error |
| 97 | + * @return \type{Title} the new object, or NULL on an error |
102 | 98 | */ |
103 | 99 | public static function newFromDBkey( $key ) { |
104 | 100 | $t = new Title(); |
— | — | @@ -113,13 +109,13 @@ |
114 | 110 | * Create a new Title from text, such as what one would find in a link. De- |
115 | 111 | * codes any HTML entities in the text. |
116 | 112 | * |
117 | | - * @param $text String the link text; spaces, prefixes, and an |
| 113 | + * @param $text string The link text; spaces, prefixes, and an |
118 | 114 | * initial ':' indicating the main namespace are accepted. |
119 | | - * @param $defaultNamespace Int the namespace to use if none is speci- |
| 115 | + * @param $defaultNamespace int The namespace to use if none is speci- |
120 | 116 | * fied by a prefix. If you want to force a specific namespace even if |
121 | 117 | * $text might begin with a namespace prefix, use makeTitle() or |
122 | 118 | * makeTitleSafe(). |
123 | | - * @return Title, or null on an error. |
| 119 | + * @return Title The new object, or null on an error. |
124 | 120 | */ |
125 | 121 | public static function newFromText( $text, $defaultNamespace = NS_MAIN ) { |
126 | 122 | if ( is_object( $text ) ) { |
— | — | @@ -138,7 +134,9 @@ |
139 | 135 | return Title::$titleCache[$text]; |
140 | 136 | } |
141 | 137 | |
142 | | - # Convert things like é ā or 〗 into normalized (bug 14952) text |
| 138 | + /** |
| 139 | + * Convert things like é ā or 〗 into normalized(bug 14952) text |
| 140 | + */ |
143 | 141 | $filteredText = Sanitizer::decodeCharReferencesAndNormalize( $text ); |
144 | 142 | |
145 | 143 | $t = new Title(); |
— | — | @@ -175,8 +173,8 @@ |
176 | 174 | * Create a new Title from URL-encoded text. Ensures that |
177 | 175 | * the given title's length does not exceed the maximum. |
178 | 176 | * |
179 | | - * @param $url String the title, as might be taken from a URL |
180 | | - * @return Title the new object, or NULL on an error |
| 177 | + * @param $url \type{\string} the title, as might be taken from a URL |
| 178 | + * @return \type{Title} the new object, or NULL on an error |
181 | 179 | */ |
182 | 180 | public static function newFromURL( $url ) { |
183 | 181 | global $wgLegalTitleChars; |
— | — | @@ -200,12 +198,12 @@ |
201 | 199 | /** |
202 | 200 | * Create a new Title from an article ID |
203 | 201 | * |
204 | | - * @param $id Int the page_id corresponding to the Title to create |
205 | | - * @param $flags Int use Title::GAID_FOR_UPDATE to use master |
206 | | - * @return Title the new object, or NULL on an error |
| 202 | + * @param $id \type{\int} the page_id corresponding to the Title to create |
| 203 | + * @param $flags \type{\int} use GAID_FOR_UPDATE to use master |
| 204 | + * @return \type{Title} the new object, or NULL on an error |
207 | 205 | */ |
208 | 206 | public static function newFromID( $id, $flags = 0 ) { |
209 | | - $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE ); |
| 207 | + $db = ( $flags & GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE ); |
210 | 208 | $row = $db->selectRow( 'page', '*', array( 'page_id' => $id ), __METHOD__ ); |
211 | 209 | if ( $row !== false ) { |
212 | 210 | $title = Title::newFromRow( $row ); |
— | — | @@ -218,15 +216,15 @@ |
219 | 217 | /** |
220 | 218 | * Make an array of titles from an array of IDs |
221 | 219 | * |
222 | | - * @param $ids Array of Int Array of IDs |
223 | | - * @return Array of Titles |
| 220 | + * @param $ids \type{\arrayof{\int}} Array of IDs |
| 221 | + * @return \type{\arrayof{Title}} Array of Titles |
224 | 222 | */ |
225 | 223 | public static function newFromIDs( $ids ) { |
226 | 224 | if ( !count( $ids ) ) { |
227 | 225 | return array(); |
228 | 226 | } |
229 | 227 | $dbr = wfGetDB( DB_SLAVE ); |
230 | | - |
| 228 | + |
231 | 229 | $res = $dbr->select( |
232 | 230 | 'page', |
233 | 231 | array( |
— | — | @@ -247,8 +245,8 @@ |
248 | 246 | /** |
249 | 247 | * Make a Title object from a DB row |
250 | 248 | * |
251 | | - * @param $row Object database row (needs at least page_title,page_namespace) |
252 | | - * @return Title corresponding Title |
| 249 | + * @param $row \type{Row} (needs at least page_title,page_namespace) |
| 250 | + * @return \type{Title} corresponding Title |
253 | 251 | */ |
254 | 252 | public static function newFromRow( $row ) { |
255 | 253 | $t = self::makeTitle( $row->page_namespace, $row->page_title ); |
— | — | @@ -288,11 +286,11 @@ |
289 | 287 | * For convenience, spaces are converted to underscores so that |
290 | 288 | * eg user_text fields can be used directly. |
291 | 289 | * |
292 | | - * @param $ns Int the namespace of the article |
293 | | - * @param $title String the unprefixed database key form |
294 | | - * @param $fragment String the link fragment (after the "#") |
295 | | - * @param $interwiki String the interwiki prefix |
296 | | - * @return Title the new object |
| 290 | + * @param $ns \type{\int} the namespace of the article |
| 291 | + * @param $title \type{\string} the unprefixed database key form |
| 292 | + * @param $fragment \type{\string} The link fragment (after the "#") |
| 293 | + * @param $interwiki \type{\string} The interwiki prefix |
| 294 | + * @return \type{Title} the new object |
297 | 295 | */ |
298 | 296 | public static function &makeTitle( $ns, $title, $fragment = '', $interwiki = '' ) { |
299 | 297 | $t = new Title(); |
— | — | @@ -311,11 +309,11 @@ |
312 | 310 | * The parameters will be checked for validity, which is a bit slower |
313 | 311 | * than makeTitle() but safer for user-provided data. |
314 | 312 | * |
315 | | - * @param $ns Int the namespace of the article |
316 | | - * @param $title String database key form |
317 | | - * @param $fragment String the link fragment (after the "#") |
318 | | - * @param $interwiki String interwiki prefix |
319 | | - * @return Title the new object, or NULL on an error |
| 313 | + * @param $ns \type{\int} the namespace of the article |
| 314 | + * @param $title \type{\string} the database key form |
| 315 | + * @param $fragment \type{\string} The link fragment (after the "#") |
| 316 | + * @param $interwiki \type{\string} The interwiki prefix |
| 317 | + * @return \type{Title} the new object, or NULL on an error |
320 | 318 | */ |
321 | 319 | public static function makeTitleSafe( $ns, $title, $fragment = '', $interwiki = '' ) { |
322 | 320 | $t = new Title(); |
— | — | @@ -330,7 +328,7 @@ |
331 | 329 | /** |
332 | 330 | * Create a new Title for the Main Page |
333 | 331 | * |
334 | | - * @return Title the new object |
| 332 | + * @return \type{Title} the new object |
335 | 333 | */ |
336 | 334 | public static function newMainPage() { |
337 | 335 | $title = Title::newFromText( wfMsgForContent( 'mainpage' ) ); |
— | — | @@ -347,8 +345,8 @@ |
348 | 346 | * This will only return the very next target, useful for |
349 | 347 | * the redirect table and other checks that don't need full recursion |
350 | 348 | * |
351 | | - * @param $text String: Text with possible redirect |
352 | | - * @return Title: The corresponding Title |
| 349 | + * @param $text \type{\string} Text with possible redirect |
| 350 | + * @return \type{Title} The corresponding Title |
353 | 351 | */ |
354 | 352 | public static function newFromRedirect( $text ) { |
355 | 353 | return self::newFromRedirectInternal( $text ); |
— | — | @@ -360,8 +358,8 @@ |
361 | 359 | * This will recurse down $wgMaxRedirects times or until a non-redirect target is hit |
362 | 360 | * in order to provide (hopefully) the Title of the final destination instead of another redirect |
363 | 361 | * |
364 | | - * @param $text String Text with possible redirect |
365 | | - * @return Title |
| 362 | + * @param $text \type{\string} Text with possible redirect |
| 363 | + * @return \type{Title} The corresponding Title |
366 | 364 | */ |
367 | 365 | public static function newFromRedirectRecurse( $text ) { |
368 | 366 | $titles = self::newFromRedirectArray( $text ); |
— | — | @@ -374,11 +372,15 @@ |
375 | 373 | * The last element in the array is the final destination after all redirects |
376 | 374 | * have been resolved (up to $wgMaxRedirects times) |
377 | 375 | * |
378 | | - * @param $text String Text with possible redirect |
379 | | - * @return Array of Titles, with the destination last |
| 376 | + * @param $text \type{\string} Text with possible redirect |
| 377 | + * @return \type{\array} Array of Titles, with the destination last |
380 | 378 | */ |
381 | 379 | public static function newFromRedirectArray( $text ) { |
382 | 380 | global $wgMaxRedirects; |
| 381 | + // are redirects disabled? |
| 382 | + if ( $wgMaxRedirects < 1 ) { |
| 383 | + return null; |
| 384 | + } |
383 | 385 | $title = self::newFromRedirectInternal( $text ); |
384 | 386 | if ( is_null( $title ) ) { |
385 | 387 | return null; |
— | — | @@ -409,15 +411,10 @@ |
410 | 412 | * Really extract the redirect destination |
411 | 413 | * Do not call this function directly, use one of the newFromRedirect* functions above |
412 | 414 | * |
413 | | - * @param $text String Text with possible redirect |
414 | | - * @return Title |
| 415 | + * @param $text \type{\string} Text with possible redirect |
| 416 | + * @return \type{Title} The corresponding Title |
415 | 417 | */ |
416 | 418 | protected static function newFromRedirectInternal( $text ) { |
417 | | - global $wgMaxRedirects; |
418 | | - if ( $wgMaxRedirects < 1 ) { |
419 | | - //redirects are disabled, so quit early |
420 | | - return null; |
421 | | - } |
422 | 419 | $redir = MagicWord::get( 'redirect' ); |
423 | 420 | $text = trim( $text ); |
424 | 421 | if ( $redir->matchStartAndRemove( $text ) ) { |
— | — | @@ -430,7 +427,9 @@ |
431 | 428 | // and URL-decode links |
432 | 429 | if ( strpos( $m[1], '%' ) !== false ) { |
433 | 430 | // Match behavior of inline link parsing here; |
434 | | - $m[1] = rawurldecode( ltrim( $m[1], ':' ) ); |
| 431 | + // don't interpret + as " " most of the time! |
| 432 | + // It might be safe to just use rawurldecode instead, though. |
| 433 | + $m[1] = urldecode( ltrim( $m[1], ':' ) ); |
435 | 434 | } |
436 | 435 | $title = Title::newFromText( $m[1] ); |
437 | 436 | // If the title is a redirect to bad special pages or is invalid, return null |
— | — | @@ -450,8 +449,9 @@ |
451 | 450 | /** |
452 | 451 | * Get the prefixed DB key associated with an ID |
453 | 452 | * |
454 | | - * @param $id Int the page_id of the article |
455 | | - * @return Title an object representing the article, or NULL if no such article was found |
| 453 | + * @param $id \type{\int} the page_id of the article |
| 454 | + * @return \type{Title} an object representing the article, or NULL |
| 455 | + * if no such article was found |
456 | 456 | */ |
457 | 457 | public static function nameOf( $id ) { |
458 | 458 | $dbr = wfGetDB( DB_SLAVE ); |
— | — | @@ -473,7 +473,7 @@ |
474 | 474 | /** |
475 | 475 | * Get a regex character class describing the legal characters in a link |
476 | 476 | * |
477 | | - * @return String the list of characters, not delimited |
| 477 | + * @return \type{\string} the list of characters, not delimited |
478 | 478 | */ |
479 | 479 | public static function legalChars() { |
480 | 480 | global $wgLegalTitleChars; |
— | — | @@ -484,9 +484,10 @@ |
485 | 485 | * Get a string representation of a title suitable for |
486 | 486 | * including in a search index |
487 | 487 | * |
488 | | - * @param $ns Int a namespace index |
489 | | - * @param $title String text-form main part |
490 | | - * @return String a stripped-down title string ready for the search index |
| 488 | + * @param $ns \type{\int} a namespace index |
| 489 | + * @param $title \type{\string} text-form main part |
| 490 | + * @return \type{\string} a stripped-down title string ready for the |
| 491 | + * search index |
491 | 492 | */ |
492 | 493 | public static function indexTitle( $ns, $title ) { |
493 | 494 | global $wgContLang; |
— | — | @@ -511,11 +512,11 @@ |
512 | 513 | /** |
513 | 514 | * Make a prefixed DB key from a DB key and a namespace index |
514 | 515 | * |
515 | | - * @param $ns Int numerical representation of the namespace |
516 | | - * @param $title String the DB key form the title |
517 | | - * @param $fragment String The link fragment (after the "#") |
518 | | - * @param $interwiki String The interwiki prefix |
519 | | - * @return String the prefixed form of the title |
| 516 | + * @param $ns \type{\int} numerical representation of the namespace |
| 517 | + * @param $title \type{\string} the DB key form the title |
| 518 | + * @param $fragment \type{\string} The link fragment (after the "#") |
| 519 | + * @param $interwiki \type{\string} The interwiki prefix |
| 520 | + * @return \type{\string} the prefixed form of the title |
520 | 521 | */ |
521 | 522 | public static function makeName( $ns, $title, $fragment = '', $interwiki = '' ) { |
522 | 523 | global $wgContLang; |
— | — | @@ -535,7 +536,8 @@ |
536 | 537 | * Determine whether the object refers to a page within |
537 | 538 | * this project. |
538 | 539 | * |
539 | | - * @return Bool TRUE if this is an in-project interwiki link or a wikilink, FALSE otherwise |
| 540 | + * @return \type{\bool} TRUE if this is an in-project interwiki link |
| 541 | + * or a wikilink, FALSE otherwise |
540 | 542 | */ |
541 | 543 | public function isLocal() { |
542 | 544 | if ( $this->mInterwiki != '' ) { |
— | — | @@ -549,7 +551,7 @@ |
550 | 552 | * Determine whether the object refers to a page within |
551 | 553 | * this project and is transcludable. |
552 | 554 | * |
553 | | - * @return Bool TRUE if this is transcludable |
| 555 | + * @return \type{\bool} TRUE if this is transcludable |
554 | 556 | */ |
555 | 557 | public function isTrans() { |
556 | 558 | if ( $this->mInterwiki == '' ) { |
— | — | @@ -558,11 +560,25 @@ |
559 | 561 | |
560 | 562 | return Interwiki::fetch( $this->mInterwiki )->isTranscludable(); |
561 | 563 | } |
| 564 | + |
| 565 | + /** |
| 566 | + * Returns the API URL of the distant wiki |
| 567 | + * which owns the object. |
| 568 | + * |
| 569 | + * @return \type{\string} the API URL |
| 570 | + */ |
| 571 | + public function getTransAPI() { |
| 572 | + if ( $this->mInterwiki == '' ) |
| 573 | + return false; |
562 | 574 | |
| 575 | + return Interwiki::fetch( $this->mInterwiki )->getAPI(); |
| 576 | + } |
| 577 | + |
563 | 578 | /** |
564 | | - * Returns the DB name of the distant wiki which owns the object. |
| 579 | + * Returns the DB name of the distant wiki |
| 580 | + * which owns the object. |
565 | 581 | * |
566 | | - * @return String the DB name |
| 582 | + * @return \type{\string} the DB name |
567 | 583 | */ |
568 | 584 | public function getTransWikiID() { |
569 | 585 | if ( $this->mInterwiki == '' ) { |
— | — | @@ -594,35 +610,35 @@ |
595 | 611 | /** |
596 | 612 | * Get the text form (spaces not underscores) of the main part |
597 | 613 | * |
598 | | - * @return String Main part of the title |
| 614 | + * @return \type{\string} Main part of the title |
599 | 615 | */ |
600 | 616 | public function getText() { return $this->mTextform; } |
601 | 617 | |
602 | 618 | /** |
603 | 619 | * Get the URL-encoded form of the main part |
604 | 620 | * |
605 | | - * @return String Main part of the title, URL-encoded |
| 621 | + * @return \type{\string} Main part of the title, URL-encoded |
606 | 622 | */ |
607 | 623 | public function getPartialURL() { return $this->mUrlform; } |
608 | 624 | |
609 | 625 | /** |
610 | 626 | * Get the main part with underscores |
611 | 627 | * |
612 | | - * @return String: Main part of the title, with underscores |
| 628 | + * @return \type{\string} Main part of the title, with underscores |
613 | 629 | */ |
614 | 630 | public function getDBkey() { return $this->mDbkeyform; } |
615 | 631 | |
616 | 632 | /** |
617 | | - * Get the namespace index, i.e. one of the NS_xxxx constants. |
| 633 | + * Get the namespace index, i.e.\ one of the NS_xxxx constants. |
618 | 634 | * |
619 | | - * @return Integer: Namespace index |
| 635 | + * @return \type{\int} Namespace index |
620 | 636 | */ |
621 | 637 | public function getNamespace() { return $this->mNamespace; } |
622 | 638 | |
623 | 639 | /** |
624 | 640 | * Get the namespace text |
625 | 641 | * |
626 | | - * @return String: Namespace text |
| 642 | + * @return \type{\string} Namespace text |
627 | 643 | */ |
628 | 644 | public function getNsText() { |
629 | 645 | global $wgContLang; |
— | — | @@ -638,20 +654,13 @@ |
639 | 655 | return MWNamespace::getCanonicalName( $this->mNamespace ); |
640 | 656 | } |
641 | 657 | } |
642 | | - |
643 | | - if ( $wgContLang->needsGenderDistinction() && |
644 | | - MWNamespace::hasGenderDistinction( $this->mNamespace ) ) { |
645 | | - $gender = GenderCache::singleton()->getGenderOf( $this->getText(), __METHOD__ ); |
646 | | - return $wgContLang->getGenderNsText( $this->mNamespace, $gender ); |
647 | | - } |
648 | | - |
649 | 658 | return $wgContLang->getNsText( $this->mNamespace ); |
650 | 659 | } |
651 | 660 | |
652 | 661 | /** |
653 | 662 | * Get the DB key with the initial letter case as specified by the user |
654 | 663 | * |
655 | | - * @return String DB key |
| 664 | + * @return \type{\string} DB key |
656 | 665 | */ |
657 | 666 | function getUserCaseDBKey() { |
658 | 667 | return $this->mUserCaseDBKey; |
— | — | @@ -660,7 +669,7 @@ |
661 | 670 | /** |
662 | 671 | * Get the namespace text of the subject (rather than talk) page |
663 | 672 | * |
664 | | - * @return String Namespace text |
| 673 | + * @return \type{\string} Namespace text |
665 | 674 | */ |
666 | 675 | public function getSubjectNsText() { |
667 | 676 | global $wgContLang; |
— | — | @@ -670,7 +679,7 @@ |
671 | 680 | /** |
672 | 681 | * Get the namespace text of the talk page |
673 | 682 | * |
674 | | - * @return String Namespace text |
| 683 | + * @return \type{\string} Namespace text |
675 | 684 | */ |
676 | 685 | public function getTalkNsText() { |
677 | 686 | global $wgContLang; |
— | — | @@ -680,7 +689,7 @@ |
681 | 690 | /** |
682 | 691 | * Could this title have a corresponding talk page? |
683 | 692 | * |
684 | | - * @return Bool TRUE or FALSE |
| 693 | + * @return \type{\bool} TRUE or FALSE |
685 | 694 | */ |
686 | 695 | public function canTalk() { |
687 | 696 | return( MWNamespace::canTalk( $this->mNamespace ) ); |
— | — | @@ -689,20 +698,20 @@ |
690 | 699 | /** |
691 | 700 | * Get the interwiki prefix (or null string) |
692 | 701 | * |
693 | | - * @return String Interwiki prefix |
| 702 | + * @return \type{\string} Interwiki prefix |
694 | 703 | */ |
695 | 704 | public function getInterwiki() { return $this->mInterwiki; } |
696 | 705 | |
697 | 706 | /** |
698 | 707 | * Get the Title fragment (i.e.\ the bit after the #) in text form |
699 | 708 | * |
700 | | - * @return String Title fragment |
| 709 | + * @return \type{\string} Title fragment |
701 | 710 | */ |
702 | 711 | public function getFragment() { return $this->mFragment; } |
703 | 712 | |
704 | 713 | /** |
705 | 714 | * Get the fragment in URL form, including the "#" character if there is one |
706 | | - * @return String Fragment in URL form |
| 715 | + * @return \type{\string} Fragment in URL form |
707 | 716 | */ |
708 | 717 | public function getFragmentForURL() { |
709 | 718 | if ( $this->mFragment == '' ) { |
— | — | @@ -715,14 +724,14 @@ |
716 | 725 | /** |
717 | 726 | * Get the default namespace index, for when there is no namespace |
718 | 727 | * |
719 | | - * @return Int Default namespace index |
| 728 | + * @return \type{\int} Default namespace index |
720 | 729 | */ |
721 | 730 | public function getDefaultNamespace() { return $this->mDefaultNamespace; } |
722 | 731 | |
723 | 732 | /** |
724 | 733 | * Get title for search index |
725 | 734 | * |
726 | | - * @return String a stripped-down title string ready for the |
| 735 | + * @return \type{\string} a stripped-down title string ready for the |
727 | 736 | * search index |
728 | 737 | */ |
729 | 738 | public function getIndexTitle() { |
— | — | @@ -732,7 +741,7 @@ |
733 | 742 | /** |
734 | 743 | * Get the prefixed database key form |
735 | 744 | * |
736 | | - * @return String the prefixed title, with underscores and |
| 745 | + * @return \type{\string} the prefixed title, with underscores and |
737 | 746 | * any interwiki and namespace prefixes |
738 | 747 | */ |
739 | 748 | public function getPrefixedDBkey() { |
— | — | @@ -745,7 +754,7 @@ |
746 | 755 | * Get the prefixed title with spaces. |
747 | 756 | * This is the form usually used for display |
748 | 757 | * |
749 | | - * @return String the prefixed title, with spaces |
| 758 | + * @return \type{\string} the prefixed title, with spaces |
750 | 759 | */ |
751 | 760 | public function getPrefixedText() { |
752 | 761 | // @todo FIXME: Bad usage of empty() ? |
— | — | @@ -775,7 +784,8 @@ |
776 | 785 | * Get the prefixed title with spaces, plus any fragment |
777 | 786 | * (part beginning with '#') |
778 | 787 | * |
779 | | - * @return String the prefixed title, with spaces and the fragment, including '#' |
| 788 | + * @return \type{\string} the prefixed title, with spaces and |
| 789 | + * the fragment, including '#' |
780 | 790 | */ |
781 | 791 | public function getFullText() { |
782 | 792 | $text = $this->getPrefixedText(); |
— | — | @@ -788,7 +798,7 @@ |
789 | 799 | /** |
790 | 800 | * Get the base page name, i.e. the leftmost part before any slashes |
791 | 801 | * |
792 | | - * @return String Base name |
| 802 | + * @return \type{\string} Base name |
793 | 803 | */ |
794 | 804 | public function getBaseText() { |
795 | 805 | if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) { |
— | — | @@ -806,7 +816,7 @@ |
807 | 817 | /** |
808 | 818 | * Get the lowest-level subpage name, i.e. the rightmost part after any slashes |
809 | 819 | * |
810 | | - * @return String Subpage name |
| 820 | + * @return \type{\string} Subpage name |
811 | 821 | */ |
812 | 822 | public function getSubpageText() { |
813 | 823 | if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) { |
— | — | @@ -819,7 +829,7 @@ |
820 | 830 | /** |
821 | 831 | * Get a URL-encoded form of the subpage text |
822 | 832 | * |
823 | | - * @return String URL-encoded subpage name |
| 833 | + * @return \type{\string} URL-encoded subpage name |
824 | 834 | */ |
825 | 835 | public function getSubpageUrlForm() { |
826 | 836 | $text = $this->getSubpageText(); |
— | — | @@ -830,7 +840,7 @@ |
831 | 841 | /** |
832 | 842 | * Get a URL-encoded title (not an actual URL) including interwiki |
833 | 843 | * |
834 | | - * @return String the URL-encoded form |
| 844 | + * @return \type{\string} the URL-encoded form |
835 | 845 | */ |
836 | 846 | public function getPrefixedURL() { |
837 | 847 | $s = $this->prefix( $this->mDbkeyform ); |
— | — | @@ -845,8 +855,8 @@ |
846 | 856 | * @param $query \twotypes{\string,\array} an optional query string, not used for interwiki |
847 | 857 | * links. Can be specified as an associative array as well, e.g., |
848 | 858 | * array( 'action' => 'edit' ) (keys and values will be URL-escaped). |
849 | | - * @param $variant String language variant of url (for sr, zh..) |
850 | | - * @return String the URL |
| 859 | + * @param $variant \type{\string} language variant of url (for sr, zh..) |
| 860 | + * @return \type{\string} the URL |
851 | 861 | */ |
852 | 862 | public function getFullURL( $query = '', $variant = false ) { |
853 | 863 | global $wgServer, $wgRequest; |
— | — | @@ -892,17 +902,25 @@ |
893 | 903 | * $wgArticlePath will be used. Can be specified as an associative array |
894 | 904 | * as well, e.g., array( 'action' => 'edit' ) (keys and values will be |
895 | 905 | * URL-escaped). |
896 | | - * @param $variant String language variant of url (for sr, zh..) |
897 | | - * @return String the URL |
| 906 | + * @param $variant \type{\string} language variant of url (for sr, zh..) |
| 907 | + * @return \type{\string} the URL |
898 | 908 | */ |
899 | 909 | public function getLocalURL( $query = '', $variant = false ) { |
900 | 910 | global $wgArticlePath, $wgScript, $wgServer, $wgRequest; |
901 | | - global $wgVariantArticlePath, $wgContLang; |
| 911 | + global $wgVariantArticlePath, $wgContLang, $wgUser; |
902 | 912 | |
903 | 913 | if ( is_array( $query ) ) { |
904 | 914 | $query = wfArrayToCGI( $query ); |
905 | 915 | } |
906 | 916 | |
| 917 | + // internal links should point to same variant as current page (only anonymous users) |
| 918 | + if ( !$variant && $wgContLang->hasVariants() && !$wgUser->isLoggedIn() ) { |
| 919 | + $pref = $wgContLang->getPreferredVariant( false ); |
| 920 | + if ( $pref != $wgContLang->getCode() ) { |
| 921 | + $variant = $pref; |
| 922 | + } |
| 923 | + } |
| 924 | + |
907 | 925 | if ( $this->isExternal() ) { |
908 | 926 | $url = $this->getFullURL(); |
909 | 927 | if ( $query ) { |
— | — | @@ -974,12 +992,12 @@ |
975 | 993 | * The result obviously should not be URL-escaped, but does need to be |
976 | 994 | * HTML-escaped if it's being output in HTML. |
977 | 995 | * |
978 | | - * @param $query Array of Strings An associative array of key => value pairs for the |
| 996 | + * @param $query \type{\arrayof{\string}} An associative array of key => value pairs for the |
979 | 997 | * query string. Keys and values will be escaped. |
980 | | - * @param $variant String language variant of URL (for sr, zh..). Ignored |
| 998 | + * @param $variant \type{\string} Language variant of URL (for sr, zh..). Ignored |
981 | 999 | * for external links. Default is "false" (same variant as current page, |
982 | 1000 | * for anonymous users). |
983 | | - * @return String the URL |
| 1001 | + * @return \type{\string} the URL |
984 | 1002 | */ |
985 | 1003 | public function getLinkUrl( $query = array(), $variant = false ) { |
986 | 1004 | wfProfileIn( __METHOD__ ); |
— | — | @@ -998,8 +1016,8 @@ |
999 | 1017 | * Get an HTML-escaped version of the URL form, suitable for |
1000 | 1018 | * using in a link, without a server name or fragment |
1001 | 1019 | * |
1002 | | - * @param $query String an optional query string |
1003 | | - * @return String the URL |
| 1020 | + * @param $query \type{\string} an optional query string |
| 1021 | + * @return \type{\string} the URL |
1004 | 1022 | */ |
1005 | 1023 | public function escapeLocalURL( $query = '' ) { |
1006 | 1024 | return htmlspecialchars( $this->getLocalURL( $query ) ); |
— | — | @@ -1009,8 +1027,8 @@ |
1010 | 1028 | * Get an HTML-escaped version of the URL form, suitable for |
1011 | 1029 | * using in a link, including the server name and fragment |
1012 | 1030 | * |
1013 | | - * @param $query String an optional query string |
1014 | | - * @return String the URL |
| 1031 | + * @param $query \type{\string} an optional query string |
| 1032 | + * @return \type{\string} the URL |
1015 | 1033 | */ |
1016 | 1034 | public function escapeFullURL( $query = '' ) { |
1017 | 1035 | return htmlspecialchars( $this->getFullURL( $query ) ); |
— | — | @@ -1021,14 +1039,13 @@ |
1022 | 1040 | * - Used in various Squid-related code, in case we have a different |
1023 | 1041 | * internal hostname for the server from the exposed one. |
1024 | 1042 | * |
1025 | | - * @param $query String an optional query string |
1026 | | - * @param $variant String language variant of url (for sr, zh..) |
1027 | | - * @return String the URL |
| 1043 | + * @param $query \type{\string} an optional query string |
| 1044 | + * @param $variant \type{\string} language variant of url (for sr, zh..) |
| 1045 | + * @return \type{\string} the URL |
1028 | 1046 | */ |
1029 | 1047 | public function getInternalURL( $query = '', $variant = false ) { |
1030 | | - global $wgInternalServer, $wgServer; |
1031 | | - $server = $wgInternalServer !== false ? $wgInternalServer : $wgServer; |
1032 | | - $url = $server . $this->getLocalURL( $query, $variant ); |
| 1048 | + global $wgInternalServer; |
| 1049 | + $url = $wgInternalServer . $this->getLocalURL( $query, $variant ); |
1033 | 1050 | wfRunHooks( 'GetInternalURL', array( &$this, &$url, $query ) ); |
1034 | 1051 | return $url; |
1035 | 1052 | } |
— | — | @@ -1036,7 +1053,7 @@ |
1037 | 1054 | /** |
1038 | 1055 | * Get the edit URL for this Title |
1039 | 1056 | * |
1040 | | - * @return String the URL, or a null string if this is an |
| 1057 | + * @return \type{\string} the URL, or a null string if this is an |
1041 | 1058 | * interwiki link |
1042 | 1059 | */ |
1043 | 1060 | public function getEditURL() { |
— | — | @@ -1052,7 +1069,7 @@ |
1053 | 1070 | * Get the HTML-escaped displayable text form. |
1054 | 1071 | * Used for the title field in <a> tags. |
1055 | 1072 | * |
1056 | | - * @return String the text, including any prefixes |
| 1073 | + * @return \type{\string} the text, including any prefixes |
1057 | 1074 | */ |
1058 | 1075 | public function getEscapedText() { |
1059 | 1076 | return htmlspecialchars( $this->getPrefixedText() ); |
— | — | @@ -1061,7 +1078,7 @@ |
1062 | 1079 | /** |
1063 | 1080 | * Is this Title interwiki? |
1064 | 1081 | * |
1065 | | - * @return Bool |
| 1082 | + * @return \type{\bool} |
1066 | 1083 | */ |
1067 | 1084 | public function isExternal() { |
1068 | 1085 | return ( $this->mInterwiki != '' ); |
— | — | @@ -1070,8 +1087,8 @@ |
1071 | 1088 | /** |
1072 | 1089 | * Is this page "semi-protected" - the *only* protection is autoconfirm? |
1073 | 1090 | * |
1074 | | - * @param $action String Action to check (default: edit) |
1075 | | - * @return Bool |
| 1091 | + * @param $action \type{\string} Action to check (default: edit) |
| 1092 | + * @return \type{\bool} |
1076 | 1093 | */ |
1077 | 1094 | public function isSemiProtected( $action = 'edit' ) { |
1078 | 1095 | if ( $this->exists() ) { |
— | — | @@ -1096,9 +1113,9 @@ |
1097 | 1114 | /** |
1098 | 1115 | * Does the title correspond to a protected article? |
1099 | 1116 | * |
1100 | | - * @param $action String the action the page is protected from, |
| 1117 | + * @param $action \type{\string} the action the page is protected from, |
1101 | 1118 | * by default checks all actions. |
1102 | | - * @return Bool |
| 1119 | + * @return \type{\bool} |
1103 | 1120 | */ |
1104 | 1121 | public function isProtected( $action = '' ) { |
1105 | 1122 | global $wgRestrictionLevels; |
— | — | @@ -1128,7 +1145,7 @@ |
1129 | 1146 | /** |
1130 | 1147 | * Is this a conversion table for the LanguageConverter? |
1131 | 1148 | * |
1132 | | - * @return Bool |
| 1149 | + * @return \type{\bool} |
1133 | 1150 | */ |
1134 | 1151 | public function isConversionTable() { |
1135 | 1152 | if( |
— | — | @@ -1145,7 +1162,7 @@ |
1146 | 1163 | /** |
1147 | 1164 | * Is $wgUser watching this page? |
1148 | 1165 | * |
1149 | | - * @return Bool |
| 1166 | + * @return \type{\bool} |
1150 | 1167 | */ |
1151 | 1168 | public function userIsWatching() { |
1152 | 1169 | global $wgUser; |
— | — | @@ -1170,26 +1187,25 @@ |
1171 | 1188 | * |
1172 | 1189 | * May provide false positives, but should never provide a false negative. |
1173 | 1190 | * |
1174 | | - * @param $action String action that permission needs to be checked for |
1175 | | - * @return Bool |
| 1191 | + * @param $action \type{\string} action that permission needs to be checked for |
| 1192 | + * @return \type{\bool} |
1176 | 1193 | */ |
1177 | 1194 | public function quickUserCan( $action ) { |
1178 | 1195 | return $this->userCan( $action, false ); |
1179 | 1196 | } |
1180 | 1197 | |
1181 | 1198 | /** |
1182 | | - * Determines if $user is unable to edit this page because it has been protected |
| 1199 | + * Determines if $wgUser is unable to edit this page because it has been protected |
1183 | 1200 | * by $wgNamespaceProtection. |
1184 | 1201 | * |
1185 | 1202 | * @param $user User object to check permissions |
1186 | 1203 | * @return Bool |
1187 | 1204 | */ |
1188 | | - public function isNamespaceProtected( User $user ) { |
1189 | | - global $wgNamespaceProtection; |
1190 | | - |
| 1205 | + public function isNamespaceProtected() { |
| 1206 | + global $wgNamespaceProtection, $wgUser; |
1191 | 1207 | if ( isset( $wgNamespaceProtection[$this->mNamespace] ) ) { |
1192 | 1208 | foreach ( (array)$wgNamespaceProtection[$this->mNamespace] as $right ) { |
1193 | | - if ( $right != '' && !$user->isAllowed( $right ) ) { |
| 1209 | + if ( $right != '' && !$wgUser->isAllowed( $right ) ) { |
1194 | 1210 | return true; |
1195 | 1211 | } |
1196 | 1212 | } |
— | — | @@ -1200,9 +1216,9 @@ |
1201 | 1217 | /** |
1202 | 1218 | * Can $wgUser perform $action on this page? |
1203 | 1219 | * |
1204 | | - * @param $action String action that permission needs to be checked for |
1205 | | - * @param $doExpensiveQueries Bool Set this to false to avoid doing unnecessary queries. |
1206 | | - * @return Bool |
| 1220 | + * @param $action \type{\string} action that permission needs to be checked for |
| 1221 | + * @param $doExpensiveQueries \type{\bool} Set this to false to avoid doing unnecessary queries. |
| 1222 | + * @return \type{\bool} |
1207 | 1223 | */ |
1208 | 1224 | public function userCan( $action, $doExpensiveQueries = true ) { |
1209 | 1225 | global $wgUser; |
— | — | @@ -1222,6 +1238,14 @@ |
1223 | 1239 | * @return Array of arguments to wfMsg to explain permissions problems. |
1224 | 1240 | */ |
1225 | 1241 | public function getUserPermissionsErrors( $action, $user, $doExpensiveQueries = true, $ignoreErrors = array() ) { |
| 1242 | + if ( !StubObject::isRealObject( $user ) ) { |
| 1243 | + // Since StubObject is always used on globals, we can |
| 1244 | + // unstub $wgUser here and set $user = $wgUser |
| 1245 | + global $wgUser; |
| 1246 | + $wgUser->_unstub( '', 5 ); |
| 1247 | + $user = $wgUser; |
| 1248 | + } |
| 1249 | + |
1226 | 1250 | $errors = $this->getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries ); |
1227 | 1251 | |
1228 | 1252 | // Remove the errors being ignored. |
— | — | @@ -1292,6 +1316,8 @@ |
1293 | 1317 | $errors[] = array( 'cant-move-to-user-page' ); |
1294 | 1318 | } |
1295 | 1319 | } elseif ( !$user->isAllowed( $action, $ns ) ) { |
| 1320 | + $return = null; |
| 1321 | + |
1296 | 1322 | // We avoid expensive display logic for quickUserCan's and such |
1297 | 1323 | $groups = false; |
1298 | 1324 | if ( !$short ) { |
— | — | @@ -1342,18 +1368,10 @@ |
1343 | 1369 | |
1344 | 1370 | /** |
1345 | 1371 | * Check various permission hooks |
1346 | | - * |
1347 | | - * @param $action String the action to check |
1348 | | - * @param $user User user to check |
1349 | | - * @param $errors Array list of current errors |
1350 | | - * @param $doExpensiveQueries Boolean whether or not to perform expensive queries |
1351 | | - * @param $short Boolean short circuit on first error |
1352 | | - * |
1353 | | - * @return Array list of errors |
| 1372 | + * @see checkQuickPermissions for parameter information |
1354 | 1373 | */ |
1355 | 1374 | private function checkPermissionHooks( $action, $user, $errors, $doExpensiveQueries, $short ) { |
1356 | 1375 | // Use getUserPermissionsErrors instead |
1357 | | - $result = ''; |
1358 | 1376 | if ( !wfRunHooks( 'userCan', array( &$this, &$user, $action, &$result ) ) ) { |
1359 | 1377 | return $result ? array() : array( array( 'badaccess-group0' ) ); |
1360 | 1378 | } |
— | — | @@ -1372,14 +1390,7 @@ |
1373 | 1391 | |
1374 | 1392 | /** |
1375 | 1393 | * Check permissions on special pages & namespaces |
1376 | | - * |
1377 | | - * @param $action String the action to check |
1378 | | - * @param $user User user to check |
1379 | | - * @param $errors Array list of current errors |
1380 | | - * @param $doExpensiveQueries Boolean whether or not to perform expensive queries |
1381 | | - * @param $short Boolean short circuit on first error |
1382 | | - * |
1383 | | - * @return Array list of errors |
| 1394 | + * @see checkQuickPermissions for parameter information |
1384 | 1395 | */ |
1385 | 1396 | private function checkSpecialsAndNSPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) { |
1386 | 1397 | # Only 'createaccount' and 'execute' can be performed on |
— | — | @@ -1390,7 +1401,7 @@ |
1391 | 1402 | } |
1392 | 1403 | |
1393 | 1404 | # Check $wgNamespaceProtection for restricted namespaces |
1394 | | - if ( $this->isNamespaceProtected( $user ) ) { |
| 1405 | + if ( $this->isNamespaceProtected() ) { |
1395 | 1406 | $ns = $this->mNamespace == NS_MAIN ? |
1396 | 1407 | wfMsg( 'nstab-main' ) : $this->getNsText(); |
1397 | 1408 | $errors[] = $this->mNamespace == NS_MEDIAWIKI ? |
— | — | @@ -1402,14 +1413,7 @@ |
1403 | 1414 | |
1404 | 1415 | /** |
1405 | 1416 | * Check CSS/JS sub-page permissions |
1406 | | - * |
1407 | | - * @param $action String the action to check |
1408 | | - * @param $user User user to check |
1409 | | - * @param $errors Array list of current errors |
1410 | | - * @param $doExpensiveQueries Boolean whether or not to perform expensive queries |
1411 | | - * @param $short Boolean short circuit on first error |
1412 | | - * |
1413 | | - * @return Array list of errors |
| 1417 | + * @see checkQuickPermissions for parameter information |
1414 | 1418 | */ |
1415 | 1419 | private function checkCSSandJSPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) { |
1416 | 1420 | # Protect css/js subpages of user pages |
— | — | @@ -1431,14 +1435,7 @@ |
1432 | 1436 | * Check against page_restrictions table requirements on this |
1433 | 1437 | * page. The user must possess all required rights for this |
1434 | 1438 | * action. |
1435 | | - * |
1436 | | - * @param $action String the action to check |
1437 | | - * @param $user User user to check |
1438 | | - * @param $errors Array list of current errors |
1439 | | - * @param $doExpensiveQueries Boolean whether or not to perform expensive queries |
1440 | | - * @param $short Boolean short circuit on first error |
1441 | | - * |
1442 | | - * @return Array list of errors |
| 1439 | + * @see checkQuickPermissions for parameter information |
1443 | 1440 | */ |
1444 | 1441 | private function checkPageRestrictions( $action, $user, $errors, $doExpensiveQueries, $short ) { |
1445 | 1442 | foreach ( $this->getRestrictions( $action ) as $right ) { |
— | — | @@ -1465,14 +1462,7 @@ |
1466 | 1463 | |
1467 | 1464 | /** |
1468 | 1465 | * Check restrictions on cascading pages. |
1469 | | - * |
1470 | | - * @param $action String the action to check |
1471 | | - * @param $user User to check |
1472 | | - * @param $errors Array list of current errors |
1473 | | - * @param $doExpensiveQueries Boolean whether or not to perform expensive queries |
1474 | | - * @param $short Boolean short circuit on first error |
1475 | | - * |
1476 | | - * @return Array list of errors |
| 1466 | + * @see checkQuickPermissions for parameter information |
1477 | 1467 | */ |
1478 | 1468 | private function checkCascadingSourcesRestrictions( $action, $user, $errors, $doExpensiveQueries, $short ) { |
1479 | 1469 | if ( $doExpensiveQueries && !$this->isCssJsSubpage() ) { |
— | — | @@ -1504,14 +1494,7 @@ |
1505 | 1495 | |
1506 | 1496 | /** |
1507 | 1497 | * Check action permissions not already checked in checkQuickPermissions |
1508 | | - * |
1509 | | - * @param $action String the action to check |
1510 | | - * @param $user User to check |
1511 | | - * @param $errors Array list of current errors |
1512 | | - * @param $doExpensiveQueries Boolean whether or not to perform expensive queries |
1513 | | - * @param $short Boolean short circuit on first error |
1514 | | - * |
1515 | | - * @return Array list of errors |
| 1498 | + * @see checkQuickPermissions for parameter information |
1516 | 1499 | */ |
1517 | 1500 | private function checkActionPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) { |
1518 | 1501 | if ( $action == 'protect' ) { |
— | — | @@ -1552,14 +1535,7 @@ |
1553 | 1536 | |
1554 | 1537 | /** |
1555 | 1538 | * Check that the user isn't blocked from editting. |
1556 | | - * |
1557 | | - * @param $action String the action to check |
1558 | | - * @param $user User to check |
1559 | | - * @param $errors Array list of current errors |
1560 | | - * @param $doExpensiveQueries Boolean whether or not to perform expensive queries |
1561 | | - * @param $short Boolean short circuit on first error |
1562 | | - * |
1563 | | - * @return Array list of errors |
| 1539 | + * @see checkQuickPermissions for parameter information |
1564 | 1540 | */ |
1565 | 1541 | private function checkUserBlock( $action, $user, $errors, $doExpensiveQueries, $short ) { |
1566 | 1542 | if( !$doExpensiveQueries ) { |
— | — | @@ -1572,14 +1548,8 @@ |
1573 | 1549 | $errors[] = array( 'confirmedittext' ); |
1574 | 1550 | } |
1575 | 1551 | |
1576 | | - if ( in_array( $action, array( 'read', 'createaccount', 'unblock' ) ) ){ |
1577 | | - // Edit blocks should not affect reading. |
1578 | | - // Account creation blocks handled at userlogin. |
1579 | | - // Unblocking handled in SpecialUnblock |
1580 | | - } elseif( ( $action == 'edit' || $action == 'create' ) && !$user->isBlockedFrom( $this ) ){ |
1581 | | - // Don't block the user from editing their own talk page unless they've been |
1582 | | - // explicitly blocked from that too. |
1583 | | - } elseif( $user->isBlocked() && $user->mBlock->prevents( $action ) !== false ) { |
| 1552 | + // Edit blocks should not affect reading. Account creation blocks handled at userlogin. |
| 1553 | + if ( $action != 'read' && $action != 'createaccount' && $user->isBlockedFrom( $this ) ) { |
1584 | 1554 | $block = $user->mBlock; |
1585 | 1555 | |
1586 | 1556 | // This is from OutputPage::blockedPage |
— | — | @@ -1599,16 +1569,29 @@ |
1600 | 1570 | } |
1601 | 1571 | |
1602 | 1572 | $link = '[[' . $wgContLang->getNsText( NS_USER ) . ":{$name}|{$name}]]"; |
1603 | | - $blockid = $block->getId(); |
| 1573 | + $blockid = $block->mId; |
1604 | 1574 | $blockExpiry = $user->mBlock->mExpiry; |
1605 | 1575 | $blockTimestamp = $wgLang->timeanddate( wfTimestamp( TS_MW, $user->mBlock->mTimestamp ), true ); |
1606 | 1576 | if ( $blockExpiry == 'infinity' ) { |
1607 | | - $blockExpiry = wfMessage( 'infiniteblock' )->text(); |
| 1577 | + // Entry in database (table ipblocks) is 'infinity' but 'ipboptions' uses 'infinite' or 'indefinite' |
| 1578 | + $scBlockExpiryOptions = wfMsg( 'ipboptions' ); |
| 1579 | + |
| 1580 | + foreach ( explode( ',', $scBlockExpiryOptions ) as $option ) { |
| 1581 | + if ( !strpos( $option, ':' ) ) |
| 1582 | + continue; |
| 1583 | + |
| 1584 | + list( $show, $value ) = explode( ':', $option ); |
| 1585 | + |
| 1586 | + if ( $value == 'infinite' || $value == 'indefinite' ) { |
| 1587 | + $blockExpiry = $show; |
| 1588 | + break; |
| 1589 | + } |
| 1590 | + } |
1608 | 1591 | } else { |
1609 | 1592 | $blockExpiry = $wgLang->timeanddate( wfTimestamp( TS_MW, $blockExpiry ), true ); |
1610 | 1593 | } |
1611 | 1594 | |
1612 | | - $intended = strval( $user->mBlock->getTarget() ); |
| 1595 | + $intended = $user->mBlock->mAddress; |
1613 | 1596 | |
1614 | 1597 | $errors[] = array( ( $block->mAuto ? 'autoblockedtext' : 'blockedtext' ), $link, $reason, $ip, $name, |
1615 | 1598 | $blockid, $blockExpiry, $intended, $blockTimestamp ); |
— | — | @@ -1622,11 +1605,11 @@ |
1623 | 1606 | * which checks ONLY that previously checked by userCan (i.e. it leaves out |
1624 | 1607 | * checks on wfReadOnly() and blocks) |
1625 | 1608 | * |
1626 | | - * @param $action String action that permission needs to be checked for |
1627 | | - * @param $user User to check |
1628 | | - * @param $doExpensiveQueries Bool Set this to false to avoid doing unnecessary queries. |
1629 | | - * @param $short Bool Set this to true to stop after the first permission error. |
1630 | | - * @return Array of arrays of the arguments to wfMsg to explain permissions problems. |
| 1609 | + * @param $action \type{\string} action that permission needs to be checked for |
| 1610 | + * @param $user \type{User} user to check |
| 1611 | + * @param $doExpensiveQueries \type{\bool} Set this to false to avoid doing unnecessary queries. |
| 1612 | + * @param $short \type{\bool} Set this to true to stop after the first permission error. |
| 1613 | + * @return \type{\array} Array of arrays of the arguments to wfMsg to explain permissions problems. |
1631 | 1614 | */ |
1632 | 1615 | protected function getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries = true, $short = false ) { |
1633 | 1616 | wfProfileIn( __METHOD__ ); |
— | — | @@ -1655,9 +1638,8 @@ |
1656 | 1639 | |
1657 | 1640 | /** |
1658 | 1641 | * Is this title subject to title protection? |
1659 | | - * Title protection is the one applied against creation of such title. |
1660 | 1642 | * |
1661 | | - * @return Mixed An associative array representing any existent title |
| 1643 | + * @return \type{\mixed} An associative array representing any existent title |
1662 | 1644 | * protection, or false if there's none. |
1663 | 1645 | */ |
1664 | 1646 | private function getTitleProtection() { |
— | — | @@ -1686,9 +1668,9 @@ |
1687 | 1669 | /** |
1688 | 1670 | * Update the title protection status |
1689 | 1671 | * |
1690 | | - * @param $create_perm String Permission required for creation |
1691 | | - * @param $reason String Reason for protection |
1692 | | - * @param $expiry String Expiry timestamp |
| 1672 | + * @param $create_perm \type{\string} Permission required for creation |
| 1673 | + * @param $reason \type{\string} Reason for protection |
| 1674 | + * @param $expiry \type{\string} Expiry timestamp |
1693 | 1675 | * @return boolean true |
1694 | 1676 | */ |
1695 | 1677 | public function updateTitleProtection( $create_perm, $reason, $expiry ) { |
— | — | @@ -1704,10 +1686,10 @@ |
1705 | 1687 | |
1706 | 1688 | $dbw = wfGetDB( DB_MASTER ); |
1707 | 1689 | |
1708 | | - $encodedExpiry = $dbw->encodeExpiry( $expiry ); |
| 1690 | + $encodedExpiry = Block::encodeExpiry( $expiry, $dbw ); |
1709 | 1691 | |
1710 | 1692 | $expiry_description = ''; |
1711 | | - if ( $encodedExpiry != $dbw->getInfinity() ) { |
| 1693 | + if ( $encodedExpiry != 'infinity' ) { |
1712 | 1694 | $expiry_description = ' (' . wfMsgForContent( 'protect-expiring', $wgContLang->timeanddate( $expiry ), |
1713 | 1695 | $wgContLang->date( $expiry ) , $wgContLang->time( $expiry ) ) . ')'; |
1714 | 1696 | } else { |
— | — | @@ -1716,23 +1698,21 @@ |
1717 | 1699 | |
1718 | 1700 | # Update protection table |
1719 | 1701 | if ( $create_perm != '' ) { |
1720 | | - $this->mTitleProtection = array( |
| 1702 | + $dbw->replace( 'protected_titles', array( array( 'pt_namespace', 'pt_title' ) ), |
| 1703 | + array( |
1721 | 1704 | 'pt_namespace' => $namespace, |
1722 | 1705 | 'pt_title' => $title, |
1723 | 1706 | 'pt_create_perm' => $create_perm, |
1724 | | - 'pt_timestamp' => $dbw->encodeExpiry( wfTimestampNow() ), |
| 1707 | + 'pt_timestamp' => Block::encodeExpiry( wfTimestampNow(), $dbw ), |
1725 | 1708 | 'pt_expiry' => $encodedExpiry, |
1726 | 1709 | 'pt_user' => $wgUser->getId(), |
1727 | 1710 | 'pt_reason' => $reason, |
1728 | | - ); |
1729 | | - $dbw->replace( 'protected_titles', array( array( 'pt_namespace', 'pt_title' ) ), |
1730 | | - $this->mTitleProtection, __METHOD__ ); |
| 1711 | + ), __METHOD__ |
| 1712 | + ); |
1731 | 1713 | } else { |
1732 | 1714 | $dbw->delete( 'protected_titles', array( 'pt_namespace' => $namespace, |
1733 | 1715 | 'pt_title' => $title ), __METHOD__ ); |
1734 | | - $this->mTitleProtection = false; |
1735 | 1716 | } |
1736 | | - |
1737 | 1717 | # Update the protection log |
1738 | 1718 | if ( $dbw->affectedRows() ) { |
1739 | 1719 | $log = new LogPage( 'protect' ); |
— | — | @@ -1759,14 +1739,13 @@ |
1760 | 1740 | array( 'pt_namespace' => $this->getNamespace(), 'pt_title' => $this->getDBkey() ), |
1761 | 1741 | __METHOD__ |
1762 | 1742 | ); |
1763 | | - $this->mTitleProtection = false; |
1764 | 1743 | } |
1765 | 1744 | |
1766 | 1745 | /** |
1767 | 1746 | * Would anybody with sufficient privileges be able to move this page? |
1768 | 1747 | * Some pages just aren't movable. |
1769 | 1748 | * |
1770 | | - * @return Bool TRUE or FALSE |
| 1749 | + * @return \type{\bool} TRUE or FALSE |
1771 | 1750 | */ |
1772 | 1751 | public function isMovable() { |
1773 | 1752 | return MWNamespace::isMovable( $this->getNamespace() ) && $this->getInterwiki() == ''; |
— | — | @@ -1775,7 +1754,7 @@ |
1776 | 1755 | /** |
1777 | 1756 | * Can $wgUser read this page? |
1778 | 1757 | * |
1779 | | - * @return Bool |
| 1758 | + * @return \type{\bool} |
1780 | 1759 | * @todo fold these checks into userCan() |
1781 | 1760 | */ |
1782 | 1761 | public function userCanRead() { |
— | — | @@ -1823,36 +1802,47 @@ |
1824 | 1803 | } else { |
1825 | 1804 | global $wgWhitelistRead; |
1826 | 1805 | |
1827 | | - # Always grant access to the login page. |
1828 | | - # Even anons need to be able to log in. |
1829 | | - if ( $this->isSpecial( 'Userlogin' ) || $this->isSpecial( 'ChangePassword' ) ) { |
| 1806 | + /** |
| 1807 | + * Always grant access to the login page. |
| 1808 | + * Even anons need to be able to log in. |
| 1809 | + */ |
| 1810 | + if ( $this->isSpecial( 'Userlogin' ) || $this->isSpecial( 'Resetpass' ) ) { |
1830 | 1811 | return true; |
1831 | 1812 | } |
1832 | 1813 | |
1833 | | - # Bail out if there isn't whitelist |
| 1814 | + /** |
| 1815 | + * Bail out if there isn't whitelist |
| 1816 | + */ |
1834 | 1817 | if ( !is_array( $wgWhitelistRead ) ) { |
1835 | 1818 | return false; |
1836 | 1819 | } |
1837 | 1820 | |
1838 | | - # Check for explicit whitelisting |
| 1821 | + /** |
| 1822 | + * Check for explicit whitelisting |
| 1823 | + */ |
1839 | 1824 | $name = $this->getPrefixedText(); |
1840 | 1825 | $dbName = $this->getPrefixedDBKey(); |
1841 | 1826 | // Check with and without underscores |
1842 | 1827 | if ( in_array( $name, $wgWhitelistRead, true ) || in_array( $dbName, $wgWhitelistRead, true ) ) |
1843 | 1828 | return true; |
1844 | 1829 | |
1845 | | - # Old settings might have the title prefixed with |
1846 | | - # a colon for main-namespace pages |
| 1830 | + /** |
| 1831 | + * Old settings might have the title prefixed with |
| 1832 | + * a colon for main-namespace pages |
| 1833 | + */ |
1847 | 1834 | if ( $this->getNamespace() == NS_MAIN ) { |
1848 | 1835 | if ( in_array( ':' . $name, $wgWhitelistRead ) ) { |
1849 | 1836 | return true; |
1850 | 1837 | } |
1851 | 1838 | } |
1852 | 1839 | |
1853 | | - # If it's a special page, ditch the subpage bit and check again |
| 1840 | + /** |
| 1841 | + * If it's a special page, ditch the subpage bit |
| 1842 | + * and check again |
| 1843 | + */ |
1854 | 1844 | if ( $this->getNamespace() == NS_SPECIAL ) { |
1855 | 1845 | $name = $this->getDBkey(); |
1856 | | - list( $name, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $name ); |
| 1846 | + list( $name, /* $subpage */ ) = SpecialPage::resolveAliasWithSubpage( $name ); |
1857 | 1847 | if ( $name === false ) { |
1858 | 1848 | # Invalid special page, but we show standard login required message |
1859 | 1849 | return false; |
— | — | @@ -1869,22 +1859,9 @@ |
1870 | 1860 | } |
1871 | 1861 | |
1872 | 1862 | /** |
1873 | | - * Is this the mainpage? |
1874 | | - * @note Title::newFromText seams to be sufficiently optimized by the title |
1875 | | - * cache that we don't need to over-optimize by doing direct comparisons and |
1876 | | - * acidentally creating new bugs where $title->equals( Title::newFromText() ) |
1877 | | - * ends up reporting something differently than $title->isMainPage(); |
1878 | | - * |
1879 | | - * @return Bool |
1880 | | - */ |
1881 | | - public function isMainPage() { |
1882 | | - return $this->equals( Title::newMainPage() ); |
1883 | | - } |
1884 | | - |
1885 | | - /** |
1886 | 1863 | * Is this a talk page of some sort? |
1887 | 1864 | * |
1888 | | - * @return Bool |
| 1865 | + * @return \type{\bool} |
1889 | 1866 | */ |
1890 | 1867 | public function isTalkPage() { |
1891 | 1868 | return MWNamespace::isTalk( $this->getNamespace() ); |
— | — | @@ -1893,7 +1870,7 @@ |
1894 | 1871 | /** |
1895 | 1872 | * Is this a subpage? |
1896 | 1873 | * |
1897 | | - * @return Bool |
| 1874 | + * @return \type{\bool} |
1898 | 1875 | */ |
1899 | 1876 | public function isSubpage() { |
1900 | 1877 | return MWNamespace::hasSubpages( $this->mNamespace ) |
— | — | @@ -1904,7 +1881,7 @@ |
1905 | 1882 | /** |
1906 | 1883 | * Does this have subpages? (Warning, usually requires an extra DB query.) |
1907 | 1884 | * |
1908 | | - * @return Bool |
| 1885 | + * @return \type{\bool} |
1909 | 1886 | */ |
1910 | 1887 | public function hasSubpages() { |
1911 | 1888 | if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) { |
— | — | @@ -1930,7 +1907,7 @@ |
1931 | 1908 | /** |
1932 | 1909 | * Get all subpages of this page. |
1933 | 1910 | * |
1934 | | - * @param $limit Int maximum number of subpages to fetch; -1 for no limit |
| 1911 | + * @param $limit Maximum number of subpages to fetch; -1 for no limit |
1935 | 1912 | * @return mixed TitleArray, or empty array if this page's namespace |
1936 | 1913 | * doesn't allow subpages |
1937 | 1914 | */ |
— | — | @@ -1960,7 +1937,7 @@ |
1961 | 1938 | * Could this page contain custom CSS or JavaScript, based |
1962 | 1939 | * on the title? |
1963 | 1940 | * |
1964 | | - * @return Bool |
| 1941 | + * @return \type{\bool} |
1965 | 1942 | */ |
1966 | 1943 | public function isCssOrJsPage() { |
1967 | 1944 | return $this->mNamespace == NS_MEDIAWIKI |
— | — | @@ -1969,7 +1946,7 @@ |
1970 | 1947 | |
1971 | 1948 | /** |
1972 | 1949 | * Is this a .css or .js subpage of a user page? |
1973 | | - * @return Bool |
| 1950 | + * @return \type{\bool} |
1974 | 1951 | */ |
1975 | 1952 | public function isCssJsSubpage() { |
1976 | 1953 | return ( NS_USER == $this->mNamespace and preg_match( "/\\/.*\\.(?:css|js)$/", $this->mTextform ) ); |
— | — | @@ -1977,12 +1954,21 @@ |
1978 | 1955 | |
1979 | 1956 | /** |
1980 | 1957 | * Is this a *valid* .css or .js subpage of a user page? |
| 1958 | + * Check that the corresponding skin exists |
1981 | 1959 | * |
1982 | | - * @return Bool |
1983 | | - * @deprecated since 1.17 |
| 1960 | + * @return \type{\bool} |
1984 | 1961 | */ |
1985 | 1962 | public function isValidCssJsSubpage() { |
1986 | | - return $this->isCssJsSubpage(); |
| 1963 | + if ( $this->isCssJsSubpage() ) { |
| 1964 | + $name = $this->getSkinFromCssJsSubpage(); |
| 1965 | + if ( $name == 'common' ) { |
| 1966 | + return true; |
| 1967 | + } |
| 1968 | + $skinNames = Skin::getSkinNames(); |
| 1969 | + return array_key_exists( $name, $skinNames ); |
| 1970 | + } else { |
| 1971 | + return false; |
| 1972 | + } |
1987 | 1973 | } |
1988 | 1974 | |
1989 | 1975 | /** |
— | — | @@ -1999,7 +1985,7 @@ |
2000 | 1986 | /** |
2001 | 1987 | * Is this a .css subpage of a user page? |
2002 | 1988 | * |
2003 | | - * @return Bool |
| 1989 | + * @return \type{\bool} |
2004 | 1990 | */ |
2005 | 1991 | public function isCssSubpage() { |
2006 | 1992 | return ( NS_USER == $this->mNamespace && preg_match( "/\\/.*\\.css$/", $this->mTextform ) ); |
— | — | @@ -2008,7 +1994,7 @@ |
2009 | 1995 | /** |
2010 | 1996 | * Is this a .js subpage of a user page? |
2011 | 1997 | * |
2012 | | - * @return Bool |
| 1998 | + * @return \type{\bool} |
2013 | 1999 | */ |
2014 | 2000 | public function isJsSubpage() { |
2015 | 2001 | return ( NS_USER == $this->mNamespace && preg_match( "/\\/.*\\.js$/", $this->mTextform ) ); |
— | — | @@ -2045,7 +2031,7 @@ |
2046 | 2032 | /** |
2047 | 2033 | * Cascading protection: Return true if cascading restrictions apply to this page, false if not. |
2048 | 2034 | * |
2049 | | - * @return Bool If the page is subject to cascading restrictions. |
| 2035 | + * @return \type{\bool} If the page is subject to cascading restrictions. |
2050 | 2036 | */ |
2051 | 2037 | public function isCascadeProtected() { |
2052 | 2038 | list( $sources, /* $restrictions */ ) = $this->getCascadeProtectionSources( false ); |
— | — | @@ -2055,15 +2041,15 @@ |
2056 | 2042 | /** |
2057 | 2043 | * Cascading protection: Get the source of any cascading restrictions on this page. |
2058 | 2044 | * |
2059 | | - * @param $getPages Bool Whether or not to retrieve the actual pages |
| 2045 | + * @param $getPages \type{\bool} Whether or not to retrieve the actual pages |
2060 | 2046 | * that the restrictions have come from. |
2061 | | - * @return Mixed Array of Title objects of the pages from which cascading restrictions |
2062 | | - * have come, false for none, or true if such restrictions exist, but $getPages |
2063 | | - * was not set. The restriction array is an array of each type, each of which |
2064 | | - * contains a array of unique groups. |
| 2047 | + * @return \type{\arrayof{mixed title array, restriction array}} Array of the Title |
| 2048 | + * objects of the pages from which cascading restrictions have come, |
| 2049 | + * false for none, or true if such restrictions exist, but $getPages was not set. |
| 2050 | + * The restriction array is an array of each type, each of which contains a |
| 2051 | + * array of unique groups. |
2065 | 2052 | */ |
2066 | 2053 | public function getCascadeProtectionSources( $getPages = true ) { |
2067 | | - global $wgContLang; |
2068 | 2054 | $pagerestrictions = array(); |
2069 | 2055 | |
2070 | 2056 | if ( isset( $this->mCascadeSources ) && $getPages ) { |
— | — | @@ -2109,7 +2095,7 @@ |
2110 | 2096 | $purgeExpired = false; |
2111 | 2097 | |
2112 | 2098 | foreach ( $res as $row ) { |
2113 | | - $expiry = $wgContLang->formatExpiry( $row->pr_expiry, TS_MW ); |
| 2099 | + $expiry = Block::decodeExpiry( $row->pr_expiry ); |
2114 | 2100 | if ( $expiry > $now ) { |
2115 | 2101 | if ( $getPages ) { |
2116 | 2102 | $page_id = $row->pr_page; |
— | — | @@ -2139,6 +2125,8 @@ |
2140 | 2126 | Title::purgeExpiredRestrictions(); |
2141 | 2127 | } |
2142 | 2128 | |
| 2129 | + wfProfileOut( __METHOD__ ); |
| 2130 | + |
2143 | 2131 | if ( $getPages ) { |
2144 | 2132 | $this->mCascadeSources = $sources; |
2145 | 2133 | $this->mCascadingRestrictions = $pagerestrictions; |
— | — | @@ -2146,7 +2134,6 @@ |
2147 | 2135 | $this->mHasCascadingRestrictions = $sources; |
2148 | 2136 | } |
2149 | 2137 | |
2150 | | - wfProfileOut( __METHOD__ ); |
2151 | 2138 | return array( $sources, $pagerestrictions ); |
2152 | 2139 | } |
2153 | 2140 | |
— | — | @@ -2166,14 +2153,15 @@ |
2167 | 2154 | /** |
2168 | 2155 | * Loads a string into mRestrictions array |
2169 | 2156 | * |
2170 | | - * @param $res Resource restrictions as an SQL result. |
2171 | | - * @param $oldFashionedRestrictions String comma-separated list of page |
| 2157 | + * @param $res \type{Resource} restrictions as an SQL result. |
| 2158 | + * @param $oldFashionedRestrictions string comma-separated list of page |
2172 | 2159 | * restrictions from page table (pre 1.10) |
2173 | 2160 | */ |
2174 | 2161 | private function loadRestrictionsFromResultWrapper( $res, $oldFashionedRestrictions = null ) { |
2175 | 2162 | $rows = array(); |
| 2163 | + $dbr = wfGetDB( DB_SLAVE ); |
2176 | 2164 | |
2177 | | - foreach ( $res as $row ) { |
| 2165 | + while ( $row = $dbr->fetchObject( $res ) ) { |
2178 | 2166 | $rows[] = $row; |
2179 | 2167 | } |
2180 | 2168 | |
— | — | @@ -2182,22 +2170,20 @@ |
2183 | 2171 | |
2184 | 2172 | /** |
2185 | 2173 | * Compiles list of active page restrictions from both page table (pre 1.10) |
2186 | | - * and page_restrictions table for this existing page. |
2187 | | - * Public for usage by LiquidThreads. |
| 2174 | + * and page_restrictions table |
2188 | 2175 | * |
2189 | 2176 | * @param $rows array of db result objects |
2190 | 2177 | * @param $oldFashionedRestrictions string comma-separated list of page |
2191 | 2178 | * restrictions from page table (pre 1.10) |
2192 | 2179 | */ |
2193 | 2180 | public function loadRestrictionsFromRows( $rows, $oldFashionedRestrictions = null ) { |
2194 | | - global $wgContLang; |
2195 | 2181 | $dbr = wfGetDB( DB_SLAVE ); |
2196 | 2182 | |
2197 | 2183 | $restrictionTypes = $this->getRestrictionTypes(); |
2198 | 2184 | |
2199 | 2185 | foreach ( $restrictionTypes as $type ) { |
2200 | 2186 | $this->mRestrictions[$type] = array(); |
2201 | | - $this->mRestrictionsExpiry[$type] = $wgContLang->formatExpiry( '', TS_MW ); |
| 2187 | + $this->mRestrictionsExpiry[$type] = Block::decodeExpiry( '' ); |
2202 | 2188 | } |
2203 | 2189 | |
2204 | 2190 | $this->mCascadeRestriction = false; |
— | — | @@ -2231,16 +2217,17 @@ |
2232 | 2218 | $now = wfTimestampNow(); |
2233 | 2219 | $purgeExpired = false; |
2234 | 2220 | |
2235 | | - # Cycle through all the restrictions. |
2236 | 2221 | foreach ( $rows as $row ) { |
| 2222 | + # Cycle through all the restrictions. |
2237 | 2223 | |
2238 | 2224 | // Don't take care of restrictions types that aren't allowed |
| 2225 | + |
2239 | 2226 | if ( !in_array( $row->pr_type, $restrictionTypes ) ) |
2240 | 2227 | continue; |
2241 | 2228 | |
2242 | 2229 | // This code should be refactored, now that it's being used more generally, |
2243 | 2230 | // But I don't really see any harm in leaving it in Block for now -werdna |
2244 | | - $expiry = $wgContLang->formatExpiry( $row->pr_expiry, TS_MW ); |
| 2231 | + $expiry = Block::decodeExpiry( $row->pr_expiry ); |
2245 | 2232 | |
2246 | 2233 | // Only apply the restrictions if they haven't expired! |
2247 | 2234 | if ( !$expiry || $expiry > $now ) { |
— | — | @@ -2265,21 +2252,16 @@ |
2266 | 2253 | /** |
2267 | 2254 | * Load restrictions from the page_restrictions table |
2268 | 2255 | * |
2269 | | - * @param $oldFashionedRestrictions String comma-separated list of page |
| 2256 | + * @param $oldFashionedRestrictions string comma-separated list of page |
2270 | 2257 | * restrictions from page table (pre 1.10) |
2271 | 2258 | */ |
2272 | 2259 | public function loadRestrictions( $oldFashionedRestrictions = null ) { |
2273 | | - global $wgContLang; |
2274 | 2260 | if ( !$this->mRestrictionsLoaded ) { |
2275 | 2261 | if ( $this->exists() ) { |
2276 | 2262 | $dbr = wfGetDB( DB_SLAVE ); |
2277 | 2263 | |
2278 | | - $res = $dbr->select( |
2279 | | - 'page_restrictions', |
2280 | | - '*', |
2281 | | - array( 'pr_page' => $this->getArticleId() ), |
2282 | | - __METHOD__ |
2283 | | - ); |
| 2264 | + $res = $dbr->select( 'page_restrictions', '*', |
| 2265 | + array( 'pr_page' => $this->getArticleId() ), __METHOD__ ); |
2284 | 2266 | |
2285 | 2267 | $this->loadRestrictionsFromResultWrapper( $res, $oldFashionedRestrictions ); |
2286 | 2268 | } else { |
— | — | @@ -2287,7 +2269,7 @@ |
2288 | 2270 | |
2289 | 2271 | if ( $title_protection ) { |
2290 | 2272 | $now = wfTimestampNow(); |
2291 | | - $expiry = $wgContLang->formatExpiry( $title_protection['pt_expiry'], TS_MW ); |
| 2273 | + $expiry = Block::decodeExpiry( $title_protection['pt_expiry'] ); |
2292 | 2274 | |
2293 | 2275 | if ( !$expiry || $expiry > $now ) { |
2294 | 2276 | // Apply the restrictions |
— | — | @@ -2295,10 +2277,9 @@ |
2296 | 2278 | $this->mRestrictions['create'] = explode( ',', trim( $title_protection['pt_create_perm'] ) ); |
2297 | 2279 | } else { // Get rid of the old restrictions |
2298 | 2280 | Title::purgeExpiredRestrictions(); |
2299 | | - $this->mTitleProtection = false; |
2300 | 2281 | } |
2301 | 2282 | } else { |
2302 | | - $this->mRestrictionsExpiry['create'] = $wgContLang->formatExpiry( '', TS_MW ); |
| 2283 | + $this->mRestrictionsExpiry['create'] = Block::decodeExpiry( '' ); |
2303 | 2284 | } |
2304 | 2285 | $this->mRestrictionsLoaded = true; |
2305 | 2286 | } |
— | — | @@ -2326,8 +2307,8 @@ |
2327 | 2308 | /** |
2328 | 2309 | * Accessor/initialisation for mRestrictions |
2329 | 2310 | * |
2330 | | - * @param $action String action that permission needs to be checked for |
2331 | | - * @return Array of Strings the array of groups allowed to edit this article |
| 2311 | + * @param $action \type{\string} action that permission needs to be checked for |
| 2312 | + * @return \type{\arrayof{\string}} the array of groups allowed to edit this article |
2332 | 2313 | */ |
2333 | 2314 | public function getRestrictions( $action ) { |
2334 | 2315 | if ( !$this->mRestrictionsLoaded ) { |
— | — | @@ -2341,7 +2322,7 @@ |
2342 | 2323 | /** |
2343 | 2324 | * Get the expiry time for the restriction against a given action |
2344 | 2325 | * |
2345 | | - * @return String|Bool 14-char timestamp, or 'infinity' if the page is protected forever |
| 2326 | + * @return 14-char timestamp, or 'infinity' if the page is protected forever |
2346 | 2327 | * or not protected at all, or false if the action is not recognised. |
2347 | 2328 | */ |
2348 | 2329 | public function getRestrictionExpiry( $action ) { |
— | — | @@ -2419,16 +2400,16 @@ |
2420 | 2401 | * Get the article ID for this Title from the link cache, |
2421 | 2402 | * adding it if necessary |
2422 | 2403 | * |
2423 | | - * @param $flags Int a bit field; may be Title::GAID_FOR_UPDATE to select |
| 2404 | + * @param $flags \type{\int} a bit field; may be GAID_FOR_UPDATE to select |
2424 | 2405 | * for update |
2425 | | - * @return Int the ID |
| 2406 | + * @return \type{\int} the ID |
2426 | 2407 | */ |
2427 | 2408 | public function getArticleID( $flags = 0 ) { |
2428 | 2409 | if ( $this->getNamespace() < 0 ) { |
2429 | 2410 | return $this->mArticleID = 0; |
2430 | 2411 | } |
2431 | 2412 | $linkCache = LinkCache::singleton(); |
2432 | | - if ( $flags & self::GAID_FOR_UPDATE ) { |
| 2413 | + if ( $flags & GAID_FOR_UPDATE ) { |
2433 | 2414 | $oldUpdate = $linkCache->forUpdate( true ); |
2434 | 2415 | $linkCache->clearLink( $this ); |
2435 | 2416 | $this->mArticleID = $linkCache->addLinkObj( $this ); |
— | — | @@ -2445,8 +2426,8 @@ |
2446 | 2427 | * Is this an article that is a redirect page? |
2447 | 2428 | * Uses link cache, adding it if necessary |
2448 | 2429 | * |
2449 | | - * @param $flags Int a bit field; may be Title::GAID_FOR_UPDATE to select for update |
2450 | | - * @return Bool |
| 2430 | + * @param $flags \type{\int} a bit field; may be GAID_FOR_UPDATE to select for update |
| 2431 | + * @return \type{\bool} |
2451 | 2432 | */ |
2452 | 2433 | public function isRedirect( $flags = 0 ) { |
2453 | 2434 | if ( !is_null( $this->mRedirect ) ) { |
— | — | @@ -2466,8 +2447,8 @@ |
2467 | 2448 | * What is the length of this page? |
2468 | 2449 | * Uses link cache, adding it if necessary |
2469 | 2450 | * |
2470 | | - * @param $flags Int a bit field; may be Title::GAID_FOR_UPDATE to select for update |
2471 | | - * @return Int |
| 2451 | + * @param $flags \type{\int} a bit field; may be GAID_FOR_UPDATE to select for update |
| 2452 | + * @return \type{\bool} |
2472 | 2453 | */ |
2473 | 2454 | public function getLength( $flags = 0 ) { |
2474 | 2455 | if ( $this->mLength != -1 ) { |
— | — | @@ -2486,8 +2467,8 @@ |
2487 | 2468 | /** |
2488 | 2469 | * What is the page_latest field for this page? |
2489 | 2470 | * |
2490 | | - * @param $flags Int a bit field; may be Title::GAID_FOR_UPDATE to select for update |
2491 | | - * @return Int or 0 if the page doesn't exist |
| 2471 | + * @param $flags \type{\int} a bit field; may be GAID_FOR_UPDATE to select for update |
| 2472 | + * @return \type{\int} or 0 if the page doesn't exist |
2492 | 2473 | */ |
2493 | 2474 | public function getLatestRevID( $flags = 0 ) { |
2494 | 2475 | if ( $this->mLatestID !== false ) { |
— | — | @@ -2507,11 +2488,7 @@ |
2508 | 2489 | * This clears some fields in this object, and clears any associated |
2509 | 2490 | * keys in the "bad links" section of the link cache. |
2510 | 2491 | * |
2511 | | - * - This is called from Article::doEdit() and Article::insertOn() to allow |
2512 | | - * loading of the new page_id. It's also called from |
2513 | | - * Article::doDeleteArticle() |
2514 | | - * |
2515 | | - * @param $newid Int the new Article ID |
| 2492 | + * @param $newid \type{\int} the new Article ID |
2516 | 2493 | */ |
2517 | 2494 | public function resetArticleID( $newid ) { |
2518 | 2495 | $linkCache = LinkCache::singleton(); |
— | — | @@ -2532,7 +2509,7 @@ |
2533 | 2510 | /** |
2534 | 2511 | * Updates page_touched for this page; called from LinksUpdate.php |
2535 | 2512 | * |
2536 | | - * @return Bool true if the update succeded |
| 2513 | + * @return \type{\bool} true if the update succeded |
2537 | 2514 | */ |
2538 | 2515 | public function invalidateCache() { |
2539 | 2516 | if ( wfReadOnly() ) { |
— | — | @@ -2553,16 +2530,15 @@ |
2554 | 2531 | * Prefix some arbitrary text with the namespace or interwiki prefix |
2555 | 2532 | * of this object |
2556 | 2533 | * |
2557 | | - * @param $name String the text |
2558 | | - * @return String the prefixed text |
| 2534 | + * @param $name \type{\string} the text |
| 2535 | + * @return \type{\string} the prefixed text |
2559 | 2536 | * @private |
2560 | 2537 | */ |
2561 | | - private function prefix( $name ) { |
| 2538 | + /* private */ function prefix( $name ) { |
2562 | 2539 | $p = ''; |
2563 | 2540 | if ( $this->mInterwiki != '' ) { |
2564 | 2541 | $p = $this->mInterwiki . ':'; |
2565 | 2542 | } |
2566 | | - |
2567 | 2543 | if ( 0 != $this->mNamespace ) { |
2568 | 2544 | $p .= $this->getNsText() . ':'; |
2569 | 2545 | } |
— | — | @@ -2574,7 +2550,7 @@ |
2575 | 2551 | * Note that this doesn't pick up many things that could be wrong with titles, but that |
2576 | 2552 | * replacing this regex with something valid will make many titles valid. |
2577 | 2553 | * |
2578 | | - * @return String regex string |
| 2554 | + * @return string regex string |
2579 | 2555 | */ |
2580 | 2556 | static function getTitleInvalidRegex() { |
2581 | 2557 | static $rxTc = false; |
— | — | @@ -2599,7 +2575,7 @@ |
2600 | 2576 | /** |
2601 | 2577 | * Capitalize a text string for a title if it belongs to a namespace that capitalizes |
2602 | 2578 | * |
2603 | | - * @param $text String containing title to capitalize |
| 2579 | + * @param $text string containing title to capitalize |
2604 | 2580 | * @param $ns int namespace index, defaults to NS_MAIN |
2605 | 2581 | * @return String containing capitalized title |
2606 | 2582 | */ |
— | — | @@ -2622,12 +2598,14 @@ |
2623 | 2599 | * namespace prefixes, sets the other forms, and canonicalizes |
2624 | 2600 | * everything. |
2625 | 2601 | * |
2626 | | - * @return Bool true on success |
| 2602 | + * @return \type{\bool} true on success |
2627 | 2603 | */ |
2628 | 2604 | private function secureAndSplit() { |
2629 | 2605 | global $wgContLang, $wgLocalInterwiki; |
2630 | 2606 | |
2631 | 2607 | # Initialisation |
| 2608 | + $rxTc = self::getTitleInvalidRegex(); |
| 2609 | + |
2632 | 2610 | $this->mInterwiki = $this->mFragment = ''; |
2633 | 2611 | $this->mNamespace = $this->mDefaultNamespace; # Usually NS_MAIN |
2634 | 2612 | |
— | — | @@ -2642,6 +2620,7 @@ |
2643 | 2621 | # Note: use of the /u option on preg_replace here will cause |
2644 | 2622 | # input with invalid UTF-8 sequences to be nullified out in PHP 5.2.x, |
2645 | 2623 | # conveniently disabling them. |
| 2624 | + # |
2646 | 2625 | $dbkey = preg_replace( '/[ _\xA0\x{1680}\x{180E}\x{2000}-\x{200A}\x{2028}\x{2029}\x{202F}\x{205F}\x{3000}]+/u', '_', $dbkey ); |
2647 | 2626 | $dbkey = trim( $dbkey, '_' ); |
2648 | 2627 | |
— | — | @@ -2678,11 +2657,9 @@ |
2679 | 2658 | # For Talk:X pages, check if X has a "namespace" prefix |
2680 | 2659 | if ( $ns == NS_TALK && preg_match( $prefixRegexp, $dbkey, $x ) ) { |
2681 | 2660 | if ( $wgContLang->getNsIndex( $x[1] ) ) { |
2682 | | - # Disallow Talk:File:x type titles... |
2683 | | - return false; |
| 2661 | + return false; # Disallow Talk:File:x type titles... |
2684 | 2662 | } elseif ( Interwiki::isValidInterwiki( $x[1] ) ) { |
2685 | | - # Disallow Talk:Interwiki:x type titles... |
2686 | | - return false; |
| 2663 | + return false; # Disallow Talk:Interwiki:x type titles... |
2687 | 2664 | } |
2688 | 2665 | } |
2689 | 2666 | } elseif ( Interwiki::isValidInterwiki( $p ) ) { |
— | — | @@ -2697,9 +2674,7 @@ |
2698 | 2675 | $this->mInterwiki = $wgContLang->lc( $p ); |
2699 | 2676 | |
2700 | 2677 | # Redundant interwiki prefix to the local wiki |
2701 | | - if ( $wgLocalInterwiki !== false |
2702 | | - && 0 == strcasecmp( $this->mInterwiki, $wgLocalInterwiki ) ) |
2703 | | - { |
| 2678 | + if ( 0 == strcasecmp( $this->mInterwiki, $wgLocalInterwiki ) ) { |
2704 | 2679 | if ( $dbkey == '' ) { |
2705 | 2680 | # Can't have an empty self-link |
2706 | 2681 | return false; |
— | — | @@ -2724,12 +2699,13 @@ |
2725 | 2700 | } while ( true ); |
2726 | 2701 | |
2727 | 2702 | # We already know that some pages won't be in the database! |
| 2703 | + # |
2728 | 2704 | if ( $this->mInterwiki != '' || NS_SPECIAL == $this->mNamespace ) { |
2729 | 2705 | $this->mArticleID = 0; |
2730 | 2706 | } |
2731 | 2707 | $fragment = strstr( $dbkey, '#' ); |
2732 | 2708 | if ( false !== $fragment ) { |
2733 | | - $this->setFragment( $fragment ); |
| 2709 | + $this->setFragment( preg_replace( '/^#_*/', '#', $fragment ) ); |
2734 | 2710 | $dbkey = substr( $dbkey, 0, strlen( $dbkey ) - strlen( $fragment ) ); |
2735 | 2711 | # remove whitespace again: prevents "Foo_bar_#" |
2736 | 2712 | # becoming "Foo_bar_" |
— | — | @@ -2737,65 +2713,79 @@ |
2738 | 2714 | } |
2739 | 2715 | |
2740 | 2716 | # Reject illegal characters. |
2741 | | - $rxTc = self::getTitleInvalidRegex(); |
| 2717 | + # |
2742 | 2718 | if ( preg_match( $rxTc, $dbkey ) ) { |
2743 | 2719 | return false; |
2744 | 2720 | } |
2745 | 2721 | |
2746 | | - # Pages with "/./" or "/../" appearing in the URLs will often be un- |
2747 | | - # reachable due to the way web browsers deal with 'relative' URLs. |
2748 | | - # Also, they conflict with subpage syntax. Forbid them explicitly. |
| 2722 | + /** |
| 2723 | + * Pages with "/./" or "/../" appearing in the URLs will often be un- |
| 2724 | + * reachable due to the way web browsers deal with 'relative' URLs. |
| 2725 | + * Also, they conflict with subpage syntax. Forbid them explicitly. |
| 2726 | + */ |
2749 | 2727 | if ( strpos( $dbkey, '.' ) !== false && |
2750 | | - ( $dbkey === '.' || $dbkey === '..' || |
2751 | | - strpos( $dbkey, './' ) === 0 || |
2752 | | - strpos( $dbkey, '../' ) === 0 || |
2753 | | - strpos( $dbkey, '/./' ) !== false || |
2754 | | - strpos( $dbkey, '/../' ) !== false || |
2755 | | - substr( $dbkey, -2 ) == '/.' || |
2756 | | - substr( $dbkey, -3 ) == '/..' ) ) |
| 2728 | + ( $dbkey === '.' || $dbkey === '..' || |
| 2729 | + strpos( $dbkey, './' ) === 0 || |
| 2730 | + strpos( $dbkey, '../' ) === 0 || |
| 2731 | + strpos( $dbkey, '/./' ) !== false || |
| 2732 | + strpos( $dbkey, '/../' ) !== false || |
| 2733 | + substr( $dbkey, -2 ) == '/.' || |
| 2734 | + substr( $dbkey, -3 ) == '/..' ) ) |
2757 | 2735 | { |
2758 | 2736 | return false; |
2759 | 2737 | } |
2760 | 2738 | |
2761 | | - # Magic tilde sequences? Nu-uh! |
| 2739 | + /** |
| 2740 | + * Magic tilde sequences? Nu-uh! |
| 2741 | + */ |
2762 | 2742 | if ( strpos( $dbkey, '~~~' ) !== false ) { |
2763 | 2743 | return false; |
2764 | 2744 | } |
2765 | 2745 | |
2766 | | - # Limit the size of titles to 255 bytes. This is typically the size of the |
2767 | | - # underlying database field. We make an exception for special pages, which |
2768 | | - # don't need to be stored in the database, and may edge over 255 bytes due |
2769 | | - # to subpage syntax for long titles, e.g. [[Special:Block/Long name]] |
| 2746 | + /** |
| 2747 | + * Limit the size of titles to 255 bytes. |
| 2748 | + * This is typically the size of the underlying database field. |
| 2749 | + * We make an exception for special pages, which don't need to be stored |
| 2750 | + * in the database, and may edge over 255 bytes due to subpage syntax |
| 2751 | + * for long titles, e.g. [[Special:Block/Long name]] |
| 2752 | + */ |
2770 | 2753 | if ( ( $this->mNamespace != NS_SPECIAL && strlen( $dbkey ) > 255 ) || |
2771 | 2754 | strlen( $dbkey ) > 512 ) |
2772 | 2755 | { |
2773 | 2756 | return false; |
2774 | 2757 | } |
2775 | 2758 | |
2776 | | - # Normally, all wiki links are forced to have an initial capital letter so [[foo]] |
2777 | | - # and [[Foo]] point to the same place. Don't force it for interwikis, since the |
2778 | | - # other site might be case-sensitive. |
| 2759 | + /** |
| 2760 | + * Normally, all wiki links are forced to have |
| 2761 | + * an initial capital letter so [[foo]] and [[Foo]] |
| 2762 | + * point to the same place. |
| 2763 | + * |
| 2764 | + * Don't force it for interwikis, since the other |
| 2765 | + * site might be case-sensitive. |
| 2766 | + */ |
2779 | 2767 | $this->mUserCaseDBKey = $dbkey; |
2780 | 2768 | if ( $this->mInterwiki == '' ) { |
2781 | 2769 | $dbkey = self::capitalize( $dbkey, $this->mNamespace ); |
2782 | 2770 | } |
2783 | 2771 | |
2784 | | - # Can't make a link to a namespace alone... "empty" local links can only be |
2785 | | - # self-links with a fragment identifier. |
2786 | | - if ( $dbkey == '' && $this->mInterwiki == '' && $this->mNamespace != NS_MAIN ) { |
| 2772 | + /** |
| 2773 | + * Can't make a link to a namespace alone... |
| 2774 | + * "empty" local links can only be self-links |
| 2775 | + * with a fragment identifier. |
| 2776 | + */ |
| 2777 | + if ( $dbkey == '' && |
| 2778 | + $this->mInterwiki == '' && |
| 2779 | + $this->mNamespace != NS_MAIN ) { |
2787 | 2780 | return false; |
2788 | 2781 | } |
2789 | | - |
2790 | 2782 | // Allow IPv6 usernames to start with '::' by canonicalizing IPv6 titles. |
2791 | 2783 | // IP names are not allowed for accounts, and can only be referring to |
2792 | 2784 | // edits from the IP. Given '::' abbreviations and caps/lowercaps, |
2793 | 2785 | // there are numerous ways to present the same IP. Having sp:contribs scan |
2794 | 2786 | // them all is silly and having some show the edits and others not is |
2795 | 2787 | // inconsistent. Same for talk/userpages. Keep them normalized instead. |
2796 | | - $dbkey = ( $this->mNamespace == NS_USER || $this->mNamespace == NS_USER_TALK ) |
2797 | | - ? IP::sanitizeIP( $dbkey ) |
2798 | | - : $dbkey; |
2799 | | - |
| 2788 | + $dbkey = ( $this->mNamespace == NS_USER || $this->mNamespace == NS_USER_TALK ) ? |
| 2789 | + IP::sanitizeIP( $dbkey ) : $dbkey; |
2800 | 2790 | // Any remaining initial :s are illegal. |
2801 | 2791 | if ( $dbkey !== '' && ':' == $dbkey { 0 } ) { |
2802 | 2792 | return false; |
— | — | @@ -2818,7 +2808,7 @@ |
2819 | 2809 | * Deprecated for public use, use Title::makeTitle() with fragment parameter. |
2820 | 2810 | * Still in active use privately. |
2821 | 2811 | * |
2822 | | - * @param $fragment String text |
| 2812 | + * @param $fragment \type{\string} text |
2823 | 2813 | */ |
2824 | 2814 | public function setFragment( $fragment ) { |
2825 | 2815 | $this->mFragment = str_replace( '_', ' ', substr( $fragment, 1 ) ); |
— | — | @@ -2831,7 +2821,7 @@ |
2832 | 2822 | /** |
2833 | 2823 | * Get a Title object associated with the talk page of this article |
2834 | 2824 | * |
2835 | | - * @return Title the object for the talk page |
| 2825 | + * @return \type{Title} the object for the talk page |
2836 | 2826 | */ |
2837 | 2827 | public function getTalkPage() { |
2838 | 2828 | return Title::makeTitle( MWNamespace::getTalk( $this->getNamespace() ), $this->getDBkey() ); |
— | — | @@ -2841,7 +2831,7 @@ |
2842 | 2832 | * Get a title object associated with the subject page of this |
2843 | 2833 | * talk page |
2844 | 2834 | * |
2845 | | - * @return Title the object for the subject page |
| 2835 | + * @return \type{Title} the object for the subject page |
2846 | 2836 | */ |
2847 | 2837 | public function getSubjectPage() { |
2848 | 2838 | // Is this the same title? |
— | — | @@ -2862,7 +2852,7 @@ |
2863 | 2853 | * @param $options Array: may be FOR UPDATE |
2864 | 2854 | * @param $table String: table name |
2865 | 2855 | * @param $prefix String: fields prefix |
2866 | | - * @return Array of Title objects linking here |
| 2856 | + * @return \type{\arrayof{Title}} the Title objects linking here |
2867 | 2857 | */ |
2868 | 2858 | public function getLinksTo( $options = array(), $table = 'pagelinks', $prefix = 'pl' ) { |
2869 | 2859 | $linkCache = LinkCache::singleton(); |
— | — | @@ -2887,13 +2877,13 @@ |
2888 | 2878 | $retVal = array(); |
2889 | 2879 | if ( $db->numRows( $res ) ) { |
2890 | 2880 | foreach ( $res as $row ) { |
2891 | | - $titleObj = Title::makeTitle( $row->page_namespace, $row->page_title ); |
2892 | | - if ( $titleObj ) { |
| 2881 | + if ( $titleObj = Title::makeTitle( $row->page_namespace, $row->page_title ) ) { |
2893 | 2882 | $linkCache->addGoodLinkObj( $row->page_id, $titleObj, $row->page_len, $row->page_is_redirect, $row->page_latest ); |
2894 | 2883 | $retVal[] = $titleObj; |
2895 | 2884 | } |
2896 | 2885 | } |
2897 | 2886 | } |
| 2887 | + $db->freeResult( $res ); |
2898 | 2888 | return $retVal; |
2899 | 2889 | } |
2900 | 2890 | |
— | — | @@ -2905,7 +2895,7 @@ |
2906 | 2896 | * On heavily-used templates it will max out the memory. |
2907 | 2897 | * |
2908 | 2898 | * @param $options Array: may be FOR UPDATE |
2909 | | - * @return Array of Title the Title objects linking here |
| 2899 | + * @return \type{\arrayof{Title}} the Title objects linking here |
2910 | 2900 | */ |
2911 | 2901 | public function getTemplateLinksTo( $options = array() ) { |
2912 | 2902 | return $this->getLinksTo( $options, 'templatelinks', 'tl' ); |
— | — | @@ -2915,7 +2905,7 @@ |
2916 | 2906 | * Get an array of Title objects referring to non-existent articles linked from this page |
2917 | 2907 | * |
2918 | 2908 | * @todo check if needed (used only in SpecialBrokenRedirects.php, and should use redirect table in this case) |
2919 | | - * @return Array of Title the Title objects |
| 2909 | + * @return \type{\arrayof{Title}} the Title objects |
2920 | 2910 | */ |
2921 | 2911 | public function getBrokenLinksFrom() { |
2922 | 2912 | if ( $this->getArticleId() == 0 ) { |
— | — | @@ -2952,7 +2942,7 @@ |
2953 | 2943 | * Get a list of URLs to purge from the Squid cache when this |
2954 | 2944 | * page changes |
2955 | 2945 | * |
2956 | | - * @return Array of String the URLs |
| 2946 | + * @return \type{\arrayof{\string}} the URLs |
2957 | 2947 | */ |
2958 | 2948 | public function getSquidURLs() { |
2959 | 2949 | global $wgContLang; |
— | — | @@ -2988,8 +2978,8 @@ |
2989 | 2979 | /** |
2990 | 2980 | * Move this page without authentication |
2991 | 2981 | * |
2992 | | - * @param $nt Title the new page Title |
2993 | | - * @return Mixed true on success, getUserPermissionsErrors()-like array on failure |
| 2982 | + * @param $nt \type{Title} the new page Title |
| 2983 | + * @return \type{\mixed} true on success, getUserPermissionsErrors()-like array on failure |
2994 | 2984 | */ |
2995 | 2985 | public function moveNoAuth( &$nt ) { |
2996 | 2986 | return $this->moveTo( $nt, false ); |
— | — | @@ -2999,11 +2989,11 @@ |
3000 | 2990 | * Check whether a given move operation would be valid. |
3001 | 2991 | * Returns true if ok, or a getUserPermissionsErrors()-like array otherwise |
3002 | 2992 | * |
3003 | | - * @param $nt Title the new title |
3004 | | - * @param $auth Bool indicates whether $wgUser's permissions |
| 2993 | + * @param $nt \type{Title} the new title |
| 2994 | + * @param $auth \type{\bool} indicates whether $wgUser's permissions |
3005 | 2995 | * should be checked |
3006 | | - * @param $reason String is the log summary of the move, used for spam checking |
3007 | | - * @return Mixed True on success, getUserPermissionsErrors()-like array on failure |
| 2996 | + * @param $reason \type{\string} is the log summary of the move, used for spam checking |
| 2997 | + * @return \type{\mixed} True on success, getUserPermissionsErrors()-like array on failure |
3008 | 2998 | */ |
3009 | 2999 | public function isValidMoveOperation( &$nt, $auth = true, $reason = '' ) { |
3010 | 3000 | global $wgUser; |
— | — | @@ -3035,13 +3025,28 @@ |
3036 | 3026 | } |
3037 | 3027 | if ( ( $this->getDBkey() == '' ) || |
3038 | 3028 | ( !$oldid ) || |
3039 | | - ( $nt->getDBkey() == '' ) ) { |
| 3029 | + ( $nt->getDBkey() == '' ) ) { |
3040 | 3030 | $errors[] = array( 'badarticleerror' ); |
3041 | 3031 | } |
3042 | 3032 | |
3043 | 3033 | // Image-specific checks |
3044 | 3034 | if ( $this->getNamespace() == NS_FILE ) { |
3045 | | - $errors = array_merge( $errors, $this->validateFileMoveOperation( $nt ) ); |
| 3035 | + if ( $nt->getNamespace() != NS_FILE ) { |
| 3036 | + $errors[] = array( 'imagenocrossnamespace' ); |
| 3037 | + } |
| 3038 | + $file = wfLocalFile( $this ); |
| 3039 | + if ( $file->exists() ) { |
| 3040 | + if ( $nt->getText() != wfStripIllegalFilenameChars( $nt->getText() ) ) { |
| 3041 | + $errors[] = array( 'imageinvalidfilename' ); |
| 3042 | + } |
| 3043 | + if ( !File::checkExtensionCompatibility( $file, $nt->getDBkey() ) ) { |
| 3044 | + $errors[] = array( 'imagetypemismatch' ); |
| 3045 | + } |
| 3046 | + } |
| 3047 | + $destfile = wfLocalFile( $nt ); |
| 3048 | + if ( !$wgUser->isAllowed( 'reupload-shared' ) && !$destfile->exists() && wfFindFile( $nt ) ) { |
| 3049 | + $errors[] = array( 'file-exists-sharedrepo' ); |
| 3050 | + } |
3046 | 3051 | } |
3047 | 3052 | |
3048 | 3053 | if ( $nt->getNamespace() == NS_FILE && $this->getNamespace() != NS_FILE ) { |
— | — | @@ -3089,56 +3094,25 @@ |
3090 | 3095 | } |
3091 | 3096 | |
3092 | 3097 | /** |
3093 | | - * Check if the requested move target is a valid file move target |
3094 | | - * @param Title $nt Target title |
3095 | | - * @return array List of errors |
3096 | | - */ |
3097 | | - protected function validateFileMoveOperation( $nt ) { |
3098 | | - global $wgUser; |
3099 | | - |
3100 | | - $errors = array(); |
3101 | | - |
3102 | | - if ( $nt->getNamespace() != NS_FILE ) { |
3103 | | - $errors[] = array( 'imagenocrossnamespace' ); |
3104 | | - } |
3105 | | - |
3106 | | - $file = wfLocalFile( $this ); |
3107 | | - if ( $file->exists() ) { |
3108 | | - if ( $nt->getText() != wfStripIllegalFilenameChars( $nt->getText() ) ) { |
3109 | | - $errors[] = array( 'imageinvalidfilename' ); |
3110 | | - } |
3111 | | - if ( !File::checkExtensionCompatibility( $file, $nt->getDBkey() ) ) { |
3112 | | - $errors[] = array( 'imagetypemismatch' ); |
3113 | | - } |
3114 | | - } |
3115 | | - |
3116 | | - $destFile = wfLocalFile( $nt ); |
3117 | | - if ( !$wgUser->isAllowed( 'reupload-shared' ) && !$destFile->exists() && wfFindFile( $nt ) ) { |
3118 | | - $errors[] = array( 'file-exists-sharedrepo' ); |
3119 | | - } |
3120 | | - |
3121 | | - return $errors; |
3122 | | - } |
3123 | | - |
3124 | | - /** |
3125 | 3098 | * Move a title to a new location |
3126 | 3099 | * |
3127 | | - * @param $nt Title the new title |
3128 | | - * @param $auth Bool indicates whether $wgUser's permissions |
| 3100 | + * @param $nt \type{Title} the new title |
| 3101 | + * @param $auth \type{\bool} indicates whether $wgUser's permissions |
3129 | 3102 | * should be checked |
3130 | | - * @param $reason String the reason for the move |
3131 | | - * @param $createRedirect Bool Whether to create a redirect from the old title to the new title. |
| 3103 | + * @param $reason \type{\string} The reason for the move |
| 3104 | + * @param $createRedirect \type{\bool} Whether to create a redirect from the old title to the new title. |
3132 | 3105 | * Ignored if the user doesn't have the suppressredirect right. |
3133 | | - * @return Mixed true on success, getUserPermissionsErrors()-like array on failure |
| 3106 | + * @return \type{\mixed} true on success, getUserPermissionsErrors()-like array on failure |
3134 | 3107 | */ |
3135 | 3108 | public function moveTo( &$nt, $auth = true, $reason = '', $createRedirect = true ) { |
| 3109 | + global $wgContLang, $wgEnableInterwikiTemplatesTracking, $wgGlobalDatabase; |
| 3110 | + |
3136 | 3111 | $err = $this->isValidMoveOperation( $nt, $auth, $reason ); |
3137 | 3112 | if ( is_array( $err ) ) { |
3138 | 3113 | return $err; |
3139 | 3114 | } |
3140 | 3115 | |
3141 | | - // If it is a file, move it first. It is done before all other moving stuff is |
3142 | | - // done because it's hard to revert |
| 3116 | + // If it is a file, move it first. It is done before all other moving stuff is done because it's hard to revert |
3143 | 3117 | $dbw = wfGetDB( DB_MASTER ); |
3144 | 3118 | if ( $this->getNamespace() == NS_FILE ) { |
3145 | 3119 | $file = wfLocalFile( $this ); |
— | — | @@ -3153,39 +3127,43 @@ |
3154 | 3128 | $dbw->begin(); # If $file was a LocalFile, its transaction would have closed our own. |
3155 | 3129 | $pageid = $this->getArticleID( self::GAID_FOR_UPDATE ); |
3156 | 3130 | $protected = $this->isProtected(); |
3157 | | - $pageCountChange = ( $createRedirect ? 1 : 0 ) - ( $nt->exists() ? 1 : 0 ); |
| 3131 | + if ( $nt->exists() ) { |
| 3132 | + $err = $this->moveOverExistingRedirect( $nt, $reason, $createRedirect ); |
| 3133 | + $pageCountChange = ( $createRedirect ? 0 : -1 ); |
| 3134 | + } else { # Target didn't exist, do normal move. |
| 3135 | + $err = $this->moveToNewTitle( $nt, $reason, $createRedirect ); |
| 3136 | + $pageCountChange = ( $createRedirect ? 1 : 0 ); |
| 3137 | + } |
3158 | 3138 | |
3159 | | - // Do the actual move |
3160 | | - $err = $this->moveToInternal( $nt, $reason, $createRedirect ); |
3161 | 3139 | if ( is_array( $err ) ) { |
3162 | 3140 | # @todo FIXME: What about the File we have already moved? |
3163 | 3141 | $dbw->rollback(); |
3164 | 3142 | return $err; |
3165 | 3143 | } |
3166 | | - |
3167 | 3144 | $redirid = $this->getArticleID(); |
3168 | 3145 | |
3169 | 3146 | // Refresh the sortkey for this row. Be careful to avoid resetting |
3170 | 3147 | // cl_timestamp, which may disturb time-based lists on some sites. |
3171 | | - $prefixes = $dbw->select( |
| 3148 | + $prefix = $dbw->selectField( |
3172 | 3149 | 'categorylinks', |
3173 | | - array( 'cl_sortkey_prefix', 'cl_to' ), |
| 3150 | + 'cl_sortkey_prefix', |
3174 | 3151 | array( 'cl_from' => $pageid ), |
3175 | 3152 | __METHOD__ |
3176 | 3153 | ); |
3177 | | - foreach ( $prefixes as $prefixRow ) { |
3178 | | - $prefix = $prefixRow->cl_sortkey_prefix; |
3179 | | - $catTo = $prefixRow->cl_to; |
3180 | | - $dbw->update( 'categorylinks', |
3181 | | - array( |
3182 | | - 'cl_sortkey' => Collation::singleton()->getSortKey( |
3183 | | - $nt->getCategorySortkey( $prefix ) ), |
3184 | | - 'cl_timestamp=cl_timestamp' ), |
3185 | | - array( |
3186 | | - 'cl_from' => $pageid, |
3187 | | - 'cl_to' => $catTo ), |
3188 | | - __METHOD__ |
3189 | | - ); |
| 3154 | + $dbw->update( 'categorylinks', |
| 3155 | + array( |
| 3156 | + 'cl_sortkey' => $wgContLang->convertToSortkey( $nt->getCategorySortkey( $prefix ) ), |
| 3157 | + 'cl_timestamp=cl_timestamp' ), |
| 3158 | + array( 'cl_from' => $pageid ), |
| 3159 | + __METHOD__ ); |
| 3160 | + |
| 3161 | + if ( $wgEnableInterwikiTemplatesTracking && $wgGlobalDatabase ) { |
| 3162 | + $dbw2 = wfGetDB( DB_MASTER, array(), $wgGlobalDatabase ); |
| 3163 | + $dbw2->update( 'globaltemplatelinks', |
| 3164 | + array( 'gtl_from_namespace' => $nt->getNsText(), |
| 3165 | + 'gtl_from_title' => $nt->getText() ), |
| 3166 | + array ( 'gtl_from_page' => $pageid ), |
| 3167 | + __METHOD__ ); |
3190 | 3168 | } |
3191 | 3169 | |
3192 | 3170 | if ( $protected ) { |
— | — | @@ -3251,19 +3229,20 @@ |
3252 | 3230 | $u->doUpdate(); |
3253 | 3231 | } |
3254 | 3232 | # Update message cache for interface messages |
3255 | | - if ( $this->getNamespace() == NS_MEDIAWIKI ) { |
| 3233 | + if ( $nt->getNamespace() == NS_MEDIAWIKI ) { |
| 3234 | + global $wgMessageCache; |
| 3235 | + |
3256 | 3236 | # @bug 17860: old article can be deleted, if this the case, |
3257 | 3237 | # delete it from message cache |
3258 | 3238 | if ( $this->getArticleID() === 0 ) { |
3259 | | - MessageCache::singleton()->replace( $this->getDBkey(), false ); |
| 3239 | + $wgMessageCache->replace( $this->getDBkey(), false ); |
3260 | 3240 | } else { |
3261 | 3241 | $oldarticle = new Article( $this ); |
3262 | | - MessageCache::singleton()->replace( $this->getDBkey(), $oldarticle->getContent() ); |
| 3242 | + $wgMessageCache->replace( $this->getDBkey(), $oldarticle->getContent() ); |
3263 | 3243 | } |
3264 | | - } |
3265 | | - if ( $nt->getNamespace() == NS_MEDIAWIKI ) { |
| 3244 | + |
3266 | 3245 | $newarticle = new Article( $nt ); |
3267 | | - MessageCache::singleton()->replace( $nt->getDBkey(), $newarticle->getContent() ); |
| 3246 | + $wgMessageCache->replace( $nt->getDBkey(), $newarticle->getContent() ); |
3268 | 3247 | } |
3269 | 3248 | |
3270 | 3249 | global $wgUser; |
— | — | @@ -3272,80 +3251,78 @@ |
3273 | 3252 | } |
3274 | 3253 | |
3275 | 3254 | /** |
3276 | | - * Move page to a title which is either a redirect to the |
3277 | | - * source page or nonexistent |
| 3255 | + * Move page to a title which is at present a redirect to the |
| 3256 | + * source page |
3278 | 3257 | * |
3279 | | - * @param $nt Title the page to move to, which should be a redirect or nonexistent |
3280 | | - * @param $reason String The reason for the move |
3281 | | - * @param $createRedirect Bool Whether to leave a redirect at the old title. Ignored |
3282 | | - * if the user doesn't have the suppressredirect right |
| 3258 | + * @param $nt \type{Title} the page to move to, which should currently |
| 3259 | + * be a redirect |
| 3260 | + * @param $reason \type{\string} The reason for the move |
| 3261 | + * @param $createRedirect \type{\bool} Whether to leave a redirect at the old title. |
| 3262 | + * Ignored if the user doesn't have the suppressredirect right |
3283 | 3263 | */ |
3284 | | - private function moveToInternal( &$nt, $reason = '', $createRedirect = true ) { |
3285 | | - global $wgUser, $wgContLang; |
| 3264 | + private function moveOverExistingRedirect( &$nt, $reason = '', $createRedirect = true ) { |
| 3265 | + global $wgUseSquid, $wgUser, $wgContLang, $wgEnableInterwikiTemplatesTracking, $wgGlobalDatabase; |
3286 | 3266 | |
3287 | | - $moveOverRedirect = $nt->exists(); |
| 3267 | + $comment = wfMsgForContent( '1movedto2_redir', $this->getPrefixedText(), $nt->getPrefixedText() ); |
3288 | 3268 | |
3289 | | - $commentMsg = ( $moveOverRedirect ? '1movedto2_redir' : '1movedto2' ); |
3290 | | - $comment = wfMsgForContent( $commentMsg, $this->getPrefixedText(), $nt->getPrefixedText() ); |
3291 | | - |
3292 | 3269 | if ( $reason ) { |
3293 | 3270 | $comment .= wfMsgForContent( 'colon-separator' ) . $reason; |
3294 | 3271 | } |
3295 | | - # Truncate for whole multibyte characters. |
3296 | | - $comment = $wgContLang->truncate( $comment, 255 ); |
| 3272 | + # Truncate for whole multibyte characters. +5 bytes for ellipsis |
| 3273 | + $comment = $wgContLang->truncate( $comment, 250 ); |
3297 | 3274 | |
| 3275 | + $now = wfTimestampNow(); |
| 3276 | + $newid = $nt->getArticleID(); |
3298 | 3277 | $oldid = $this->getArticleID(); |
3299 | 3278 | $latest = $this->getLatestRevID(); |
3300 | 3279 | |
3301 | | - $oldns = $this->getNamespace(); |
3302 | | - $olddbk = $this->getDBkey(); |
3303 | | - |
3304 | 3280 | $dbw = wfGetDB( DB_MASTER ); |
3305 | 3281 | |
3306 | | - if ( $moveOverRedirect ) { |
3307 | | - $rcts = $dbw->timestamp( $nt->getEarliestRevTime() ); |
| 3282 | + $rcts = $dbw->timestamp( $nt->getEarliestRevTime() ); |
| 3283 | + $newns = $nt->getNamespace(); |
| 3284 | + $newdbk = $nt->getDBkey(); |
3308 | 3285 | |
3309 | | - $newid = $nt->getArticleID(); |
3310 | | - $newns = $nt->getNamespace(); |
3311 | | - $newdbk = $nt->getDBkey(); |
3312 | | - |
3313 | | - # Delete the old redirect. We don't save it to history since |
3314 | | - # by definition if we've got here it's rather uninteresting. |
3315 | | - # We have to remove it so that the next step doesn't trigger |
3316 | | - # a conflict on the unique namespace+title index... |
3317 | | - $dbw->delete( 'page', array( 'page_id' => $newid ), __METHOD__ ); |
3318 | | - if ( !$dbw->cascadingDeletes() ) { |
3319 | | - $dbw->delete( 'revision', array( 'rev_page' => $newid ), __METHOD__ ); |
3320 | | - global $wgUseTrackbacks; |
3321 | | - if ( $wgUseTrackbacks ) { |
3322 | | - $dbw->delete( 'trackbacks', array( 'tb_page' => $newid ), __METHOD__ ); |
3323 | | - } |
3324 | | - $dbw->delete( 'pagelinks', array( 'pl_from' => $newid ), __METHOD__ ); |
3325 | | - $dbw->delete( 'imagelinks', array( 'il_from' => $newid ), __METHOD__ ); |
3326 | | - $dbw->delete( 'categorylinks', array( 'cl_from' => $newid ), __METHOD__ ); |
3327 | | - $dbw->delete( 'templatelinks', array( 'tl_from' => $newid ), __METHOD__ ); |
3328 | | - $dbw->delete( 'externallinks', array( 'el_from' => $newid ), __METHOD__ ); |
3329 | | - $dbw->delete( 'langlinks', array( 'll_from' => $newid ), __METHOD__ ); |
3330 | | - $dbw->delete( 'redirect', array( 'rd_from' => $newid ), __METHOD__ ); |
| 3286 | + # Delete the old redirect. We don't save it to history since |
| 3287 | + # by definition if we've got here it's rather uninteresting. |
| 3288 | + # We have to remove it so that the next step doesn't trigger |
| 3289 | + # a conflict on the unique namespace+title index... |
| 3290 | + $dbw->delete( 'page', array( 'page_id' => $newid ), __METHOD__ ); |
| 3291 | + if ( !$dbw->cascadingDeletes() ) { |
| 3292 | + $dbw->delete( 'revision', array( 'rev_page' => $newid ), __METHOD__ ); |
| 3293 | + global $wgUseTrackbacks; |
| 3294 | + if ( $wgUseTrackbacks ) { |
| 3295 | + $dbw->delete( 'trackbacks', array( 'tb_page' => $newid ), __METHOD__ ); |
3331 | 3296 | } |
3332 | | - // If the target page was recently created, it may have an entry in recentchanges still |
3333 | | - $dbw->delete( 'recentchanges', |
3334 | | - array( 'rc_timestamp' => $rcts, 'rc_namespace' => $newns, 'rc_title' => $newdbk, 'rc_new' => 1 ), |
3335 | | - __METHOD__ |
3336 | | - ); |
| 3297 | + $dbw->delete( 'pagelinks', array( 'pl_from' => $newid ), __METHOD__ ); |
| 3298 | + $dbw->delete( 'imagelinks', array( 'il_from' => $newid ), __METHOD__ ); |
| 3299 | + $dbw->delete( 'categorylinks', array( 'cl_from' => $newid ), __METHOD__ ); |
| 3300 | + $dbw->delete( 'templatelinks', array( 'tl_from' => $newid ), __METHOD__ ); |
| 3301 | + $dbw->delete( 'externallinks', array( 'el_from' => $newid ), __METHOD__ ); |
| 3302 | + $dbw->delete( 'langlinks', array( 'll_from' => $newid ), __METHOD__ ); |
| 3303 | + $dbw->delete( 'redirect', array( 'rd_from' => $newid ), __METHOD__ ); |
| 3304 | + |
| 3305 | + if ( $wgEnableInterwikiTemplatesTracking && $wgGlobalDatabase ) { |
| 3306 | + $dbw2 = wfGetDB( DB_MASTER, array(), $wgGlobalDatabase ); |
| 3307 | + $dbw2->delete( 'globaltemplatelinks', |
| 3308 | + array( 'gtl_from_wiki' => wfWikiID( ), |
| 3309 | + 'gtl_from_page' => $newid ), |
| 3310 | + __METHOD__ ); |
| 3311 | + } |
3337 | 3312 | } |
| 3313 | + // If the redirect was recently created, it may have an entry in recentchanges still |
| 3314 | + $dbw->delete( 'recentchanges', |
| 3315 | + array( 'rc_timestamp' => $rcts, 'rc_namespace' => $newns, 'rc_title' => $newdbk, 'rc_new' => 1 ), |
| 3316 | + __METHOD__ |
| 3317 | + ); |
3338 | 3318 | |
3339 | 3319 | # Save a null revision in the page's history notifying of the move |
3340 | 3320 | $nullRevision = Revision::newNullRevision( $dbw, $oldid, $comment, true ); |
3341 | | - if ( !is_object( $nullRevision ) ) { |
3342 | | - throw new MWException( 'No valid null revision produced in ' . __METHOD__ ); |
3343 | | - } |
3344 | 3321 | $nullRevId = $nullRevision->insertOn( $dbw ); |
3345 | 3322 | |
3346 | 3323 | # Change the name of the target page: |
3347 | 3324 | $dbw->update( 'page', |
3348 | 3325 | /* SET */ array( |
3349 | | - 'page_touched' => $dbw->timestamp(), |
| 3326 | + 'page_touched' => $dbw->timestamp( $now ), |
3350 | 3327 | 'page_namespace' => $nt->getNamespace(), |
3351 | 3328 | 'page_title' => $nt->getDBkey(), |
3352 | 3329 | 'page_latest' => $nullRevId, |
— | — | @@ -3384,34 +3361,110 @@ |
3385 | 3362 | __METHOD__ ); |
3386 | 3363 | $redirectSuppressed = false; |
3387 | 3364 | } else { |
3388 | | - // Get rid of old new page entries in Special:NewPages and RC. |
3389 | | - // Needs to be before $this->resetArticleID( 0 ). |
3390 | | - $dbw->delete( 'recentchanges', array( |
3391 | | - 'rc_timestamp' => $dbw->timestamp( $this->getEarliestRevTime() ), |
3392 | | - 'rc_namespace' => $oldns, |
3393 | | - 'rc_title' => $olddbk, |
3394 | | - 'rc_new' => 1 |
3395 | | - ), |
3396 | | - __METHOD__ |
3397 | | - ); |
3398 | | - |
3399 | 3365 | $this->resetArticleID( 0 ); |
3400 | 3366 | $redirectSuppressed = true; |
3401 | 3367 | } |
3402 | 3368 | |
3403 | 3369 | # Log the move |
3404 | 3370 | $log = new LogPage( 'move' ); |
3405 | | - $logType = ( $moveOverRedirect ? 'move_redir' : 'move' ); |
3406 | | - $log->addEntry( $logType, $this, $reason, array( 1 => $nt->getPrefixedText(), 2 => $redirectSuppressed ) ); |
| 3371 | + $log->addEntry( 'move_redir', $this, $reason, array( 1 => $nt->getPrefixedText(), 2 => $redirectSuppressed ) ); |
3407 | 3372 | |
3408 | | - # Purge caches for old and new titles |
3409 | | - if ( $moveOverRedirect ) { |
3410 | | - # A simple purge is enough when moving over a redirect |
3411 | | - $nt->purgeSquid(); |
| 3373 | + # Purge squid |
| 3374 | + if ( $wgUseSquid ) { |
| 3375 | + $urls = array_merge( $nt->getSquidURLs(), $this->getSquidURLs() ); |
| 3376 | + $u = new SquidUpdate( $urls ); |
| 3377 | + $u->doUpdate(); |
| 3378 | + } |
| 3379 | + |
| 3380 | + } |
| 3381 | + |
| 3382 | + /** |
| 3383 | + * Move page to non-existing title. |
| 3384 | + * |
| 3385 | + * @param $nt \type{Title} the new Title |
| 3386 | + * @param $reason \type{\string} The reason for the move |
| 3387 | + * @param $createRedirect \type{\bool} Whether to create a redirect from the old title to the new title |
| 3388 | + * Ignored if the user doesn't have the suppressredirect right |
| 3389 | + */ |
| 3390 | + private function moveToNewTitle( &$nt, $reason = '', $createRedirect = true ) { |
| 3391 | + global $wgUser, $wgContLang; |
| 3392 | + |
| 3393 | + $comment = wfMsgForContent( '1movedto2', $this->getPrefixedText(), $nt->getPrefixedText() ); |
| 3394 | + if ( $reason ) { |
| 3395 | + $comment .= wfMsgExt( 'colon-separator', |
| 3396 | + array( 'escapenoentities', 'content' ) ); |
| 3397 | + $comment .= $reason; |
| 3398 | + } |
| 3399 | + # Truncate for whole multibyte characters. +5 bytes for ellipsis |
| 3400 | + $comment = $wgContLang->truncate( $comment, 250 ); |
| 3401 | + |
| 3402 | + $newid = $nt->getArticleID(); |
| 3403 | + $oldid = $this->getArticleID(); |
| 3404 | + $latest = $this->getLatestRevId(); |
| 3405 | + |
| 3406 | + $dbw = wfGetDB( DB_MASTER ); |
| 3407 | + $now = $dbw->timestamp(); |
| 3408 | + |
| 3409 | + # Save a null revision in the page's history notifying of the move |
| 3410 | + $nullRevision = Revision::newNullRevision( $dbw, $oldid, $comment, true ); |
| 3411 | + if ( !is_object( $nullRevision ) ) { |
| 3412 | + throw new MWException( 'No valid null revision produced in ' . __METHOD__ ); |
| 3413 | + } |
| 3414 | + $nullRevId = $nullRevision->insertOn( $dbw ); |
| 3415 | + |
| 3416 | + $article = new Article( $this ); |
| 3417 | + wfRunHooks( 'NewRevisionFromEditComplete', array( $article, $nullRevision, $latest, $wgUser ) ); |
| 3418 | + |
| 3419 | + # Rename page entry |
| 3420 | + $dbw->update( 'page', |
| 3421 | + /* SET */ array( |
| 3422 | + 'page_touched' => $now, |
| 3423 | + 'page_namespace' => $nt->getNamespace(), |
| 3424 | + 'page_title' => $nt->getDBkey(), |
| 3425 | + 'page_latest' => $nullRevId, |
| 3426 | + ), |
| 3427 | + /* WHERE */ array( 'page_id' => $oldid ), |
| 3428 | + __METHOD__ |
| 3429 | + ); |
| 3430 | + $nt->resetArticleID( $oldid ); |
| 3431 | + |
| 3432 | + if ( $createRedirect || !$wgUser->isAllowed( 'suppressredirect' ) ) { |
| 3433 | + # Insert redirect |
| 3434 | + $mwRedir = MagicWord::get( 'redirect' ); |
| 3435 | + $redirectText = $mwRedir->getSynonym( 0 ) . ' [[' . $nt->getPrefixedText() . "]]\n"; |
| 3436 | + $redirectArticle = new Article( $this ); |
| 3437 | + $newid = $redirectArticle->insertOn( $dbw ); |
| 3438 | + $redirectRevision = new Revision( array( |
| 3439 | + 'page' => $newid, |
| 3440 | + 'comment' => $comment, |
| 3441 | + 'text' => $redirectText ) ); |
| 3442 | + $redirectRevision->insertOn( $dbw ); |
| 3443 | + $redirectArticle->updateRevisionOn( $dbw, $redirectRevision, 0 ); |
| 3444 | + |
| 3445 | + wfRunHooks( 'NewRevisionFromEditComplete', array( $redirectArticle, $redirectRevision, false, $wgUser ) ); |
| 3446 | + |
| 3447 | + # Record the just-created redirect's linking to the page |
| 3448 | + $dbw->insert( 'pagelinks', |
| 3449 | + array( |
| 3450 | + 'pl_from' => $newid, |
| 3451 | + 'pl_namespace' => $nt->getNamespace(), |
| 3452 | + 'pl_title' => $nt->getDBkey() ), |
| 3453 | + __METHOD__ ); |
| 3454 | + $redirectSuppressed = false; |
3412 | 3455 | } else { |
3413 | | - # Purge caches as per article creation, including any pages that link to this title |
3414 | | - Article::onArticleCreate( $nt ); |
| 3456 | + $this->resetArticleID( 0 ); |
| 3457 | + $redirectSuppressed = true; |
3415 | 3458 | } |
| 3459 | + |
| 3460 | + # Log the move |
| 3461 | + $log = new LogPage( 'move' ); |
| 3462 | + $log->addEntry( 'move', $this, $reason, array( 1 => $nt->getPrefixedText(), 2 => $redirectSuppressed ) ); |
| 3463 | + |
| 3464 | + # Purge caches as per article creation |
| 3465 | + Article::onArticleCreate( $nt ); |
| 3466 | + |
| 3467 | + # Purge old title from squid |
| 3468 | + # The new title, and links to the new title, are purged in Article::onArticleCreate() |
3416 | 3469 | $this->purgeSquid(); |
3417 | 3470 | } |
3418 | 3471 | |
— | — | @@ -3421,11 +3474,10 @@ |
3422 | 3475 | * @param $nt Title Move target |
3423 | 3476 | * @param $auth bool Whether $wgUser's permissions should be checked |
3424 | 3477 | * @param $reason string The reason for the move |
3425 | | - * @param $createRedirect bool Whether to create redirects from the old subpages to |
3426 | | - * the new ones Ignored if the user doesn't have the 'suppressredirect' right |
| 3478 | + * @param $createRedirect bool Whether to create redirects from the old subpages to the new ones |
| 3479 | + * Ignored if the user doesn't have the 'suppressredirect' right |
3427 | 3480 | * @return mixed array with old page titles as keys, and strings (new page titles) or |
3428 | | - * arrays (errors) as values, or an error array with numeric indices if no pages |
3429 | | - * were moved |
| 3481 | + * arrays (errors) as values, or an error array with numeric indices if no pages were moved |
3430 | 3482 | */ |
3431 | 3483 | public function moveSubpages( $nt, $auth = true, $reason = '', $createRedirect = true ) { |
3432 | 3484 | global $wgMaximumMovedPages; |
— | — | @@ -3492,7 +3544,7 @@ |
3493 | 3545 | * Checks if this page is just a one-rev redirect. |
3494 | 3546 | * Adds lock, so don't use just for light purposes. |
3495 | 3547 | * |
3496 | | - * @return Bool |
| 3548 | + * @return \type{\bool} |
3497 | 3549 | */ |
3498 | 3550 | public function isSingleRevRedirect() { |
3499 | 3551 | $dbw = wfGetDB( DB_MASTER ); |
— | — | @@ -3529,8 +3581,8 @@ |
3530 | 3582 | * Checks if $this can be moved to a given Title |
3531 | 3583 | * - Selects for update, so don't call it unless you mean business |
3532 | 3584 | * |
3533 | | - * @param $nt Title the new title to check |
3534 | | - * @return Bool |
| 3585 | + * @param $nt \type{Title} the new title to check |
| 3586 | + * @return \type{\bool} TRUE or FALSE |
3535 | 3587 | */ |
3536 | 3588 | public function isValidMoveTarget( $nt ) { |
3537 | 3589 | # Is it an existing file? |
— | — | @@ -3571,7 +3623,7 @@ |
3572 | 3624 | /** |
3573 | 3625 | * Can this title be added to a user's watchlist? |
3574 | 3626 | * |
3575 | | - * @return Bool TRUE or FALSE |
| 3627 | + * @return \type{\bool} TRUE or FALSE |
3576 | 3628 | */ |
3577 | 3629 | public function isWatchable() { |
3578 | 3630 | return !$this->isExternal() && MWNamespace::isWatchable( $this->getNamespace() ); |
— | — | @@ -3581,35 +3633,31 @@ |
3582 | 3634 | * Get categories to which this Title belongs and return an array of |
3583 | 3635 | * categories' names. |
3584 | 3636 | * |
3585 | | - * @return Array of parents in the form: |
3586 | | - * $parent => $currentarticle |
| 3637 | + * @return \type{\array} array an array of parents in the form: |
| 3638 | + * $parent => $currentarticle |
3587 | 3639 | */ |
3588 | 3640 | public function getParentCategories() { |
3589 | 3641 | global $wgContLang; |
3590 | 3642 | |
3591 | | - $data = array(); |
3592 | | - |
3593 | | - $titleKey = $this->getArticleId(); |
3594 | | - |
3595 | | - if ( $titleKey === 0 ) { |
3596 | | - return $data; |
3597 | | - } |
3598 | | - |
| 3643 | + $titlekey = $this->getArticleId(); |
3599 | 3644 | $dbr = wfGetDB( DB_SLAVE ); |
| 3645 | + $categorylinks = $dbr->tableName( 'categorylinks' ); |
3600 | 3646 | |
3601 | | - $res = $dbr->select( 'categorylinks', '*', |
3602 | | - array( |
3603 | | - 'cl_from' => $titleKey, |
3604 | | - ), |
3605 | | - __METHOD__, |
3606 | | - array() |
3607 | | - ); |
| 3647 | + # NEW SQL |
| 3648 | + $sql = "SELECT * FROM $categorylinks" |
| 3649 | + . " WHERE cl_from='$titlekey'" |
| 3650 | + . " AND cl_from <> '0'" |
| 3651 | + . " ORDER BY cl_sortkey"; |
3608 | 3652 | |
| 3653 | + $res = $dbr->query( $sql ); |
| 3654 | + |
3609 | 3655 | if ( $dbr->numRows( $res ) > 0 ) { |
3610 | | - foreach ( $res as $row ) { |
| 3656 | + foreach ( $res as $row ) |
3611 | 3657 | // $data[] = Title::newFromText($wgContLang->getNSText ( NS_CATEGORY ).':'.$row->cl_to); |
3612 | 3658 | $data[$wgContLang->getNSText( NS_CATEGORY ) . ':' . $row->cl_to] = $this->getFullText(); |
3613 | | - } |
| 3659 | + $dbr->freeResult( $res ); |
| 3660 | + } else { |
| 3661 | + $data = array(); |
3614 | 3662 | } |
3615 | 3663 | return $data; |
3616 | 3664 | } |
— | — | @@ -3617,8 +3665,8 @@ |
3618 | 3666 | /** |
3619 | 3667 | * Get a tree of parent categories |
3620 | 3668 | * |
3621 | | - * @param $children Array with the children in the keys, to check for circular refs |
3622 | | - * @return Array Tree of parent categories |
| 3669 | + * @param $children \type{\array} an array with the children in the keys, to check for circular refs |
| 3670 | + * @return \type{\array} Tree of parent categories |
3623 | 3671 | */ |
3624 | 3672 | public function getParentCategoryTree( $children = array() ) { |
3625 | 3673 | $stack = array(); |
— | — | @@ -3636,16 +3684,18 @@ |
3637 | 3685 | } |
3638 | 3686 | } |
3639 | 3687 | } |
| 3688 | + return $stack; |
| 3689 | + } else { |
| 3690 | + return array(); |
3640 | 3691 | } |
3641 | | - |
3642 | | - return $stack; |
3643 | 3692 | } |
3644 | 3693 | |
| 3694 | + |
3645 | 3695 | /** |
3646 | 3696 | * Get an associative array for selecting this title from |
3647 | 3697 | * the "page" table |
3648 | 3698 | * |
3649 | | - * @return Array suitable for the $where parameter of DB::select() |
| 3699 | + * @return \type{\array} Selection array |
3650 | 3700 | */ |
3651 | 3701 | public function pageCond() { |
3652 | 3702 | if ( $this->mArticleID > 0 ) { |
— | — | @@ -3659,12 +3709,12 @@ |
3660 | 3710 | /** |
3661 | 3711 | * Get the revision ID of the previous revision |
3662 | 3712 | * |
3663 | | - * @param $revId Int Revision ID. Get the revision that was before this one. |
3664 | | - * @param $flags Int Title::GAID_FOR_UPDATE |
3665 | | - * @return Int|Bool Old revision ID, or FALSE if none exists |
| 3713 | + * @param $revId \type{\int} Revision ID. Get the revision that was before this one. |
| 3714 | + * @param $flags \type{\int} GAID_FOR_UPDATE |
| 3715 | + * @return \twotypes{\int,\bool} Old revision ID, or FALSE if none exists |
3666 | 3716 | */ |
3667 | 3717 | public function getPreviousRevisionID( $revId, $flags = 0 ) { |
3668 | | - $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE ); |
| 3718 | + $db = ( $flags & GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE ); |
3669 | 3719 | return $db->selectField( 'revision', 'rev_id', |
3670 | 3720 | array( |
3671 | 3721 | 'rev_page' => $this->getArticleId( $flags ), |
— | — | @@ -3678,12 +3728,12 @@ |
3679 | 3729 | /** |
3680 | 3730 | * Get the revision ID of the next revision |
3681 | 3731 | * |
3682 | | - * @param $revId Int Revision ID. Get the revision that was after this one. |
3683 | | - * @param $flags Int Title::GAID_FOR_UPDATE |
3684 | | - * @return Int|Bool Next revision ID, or FALSE if none exists |
| 3732 | + * @param $revId \type{\int} Revision ID. Get the revision that was after this one. |
| 3733 | + * @param $flags \type{\int} GAID_FOR_UPDATE |
| 3734 | + * @return \twotypes{\int,\bool} Next revision ID, or FALSE if none exists |
3685 | 3735 | */ |
3686 | 3736 | public function getNextRevisionID( $revId, $flags = 0 ) { |
3687 | | - $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE ); |
| 3737 | + $db = ( $flags & GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE ); |
3688 | 3738 | return $db->selectField( 'revision', 'rev_id', |
3689 | 3739 | array( |
3690 | 3740 | 'rev_page' => $this->getArticleId( $flags ), |
— | — | @@ -3697,37 +3747,28 @@ |
3698 | 3748 | /** |
3699 | 3749 | * Get the first revision of the page |
3700 | 3750 | * |
3701 | | - * @param $flags Int Title::GAID_FOR_UPDATE |
3702 | | - * @return Revision|Null if page doesn't exist |
| 3751 | + * @param $flags \type{\int} GAID_FOR_UPDATE |
| 3752 | + * @return Revision (or NULL if page doesn't exist) |
3703 | 3753 | */ |
3704 | 3754 | public function getFirstRevision( $flags = 0 ) { |
| 3755 | + $db = ( $flags & GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE ); |
3705 | 3756 | $pageId = $this->getArticleId( $flags ); |
3706 | | - if ( $pageId ) { |
3707 | | - $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_SLAVE ); |
3708 | | - $row = $db->selectRow( 'revision', '*', |
3709 | | - array( 'rev_page' => $pageId ), |
3710 | | - __METHOD__, |
3711 | | - array( 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 1 ) |
3712 | | - ); |
3713 | | - if ( $row ) { |
3714 | | - return new Revision( $row ); |
3715 | | - } |
| 3757 | + if ( !$pageId ) { |
| 3758 | + return null; |
3716 | 3759 | } |
3717 | | - return null; |
| 3760 | + $row = $db->selectRow( 'revision', '*', |
| 3761 | + array( 'rev_page' => $pageId ), |
| 3762 | + __METHOD__, |
| 3763 | + array( 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 1 ) |
| 3764 | + ); |
| 3765 | + if ( !$row ) { |
| 3766 | + return null; |
| 3767 | + } else { |
| 3768 | + return new Revision( $row ); |
| 3769 | + } |
3718 | 3770 | } |
3719 | 3771 | |
3720 | 3772 | /** |
3721 | | - * Get the oldest revision timestamp of this page |
3722 | | - * |
3723 | | - * @param $flags Int Title::GAID_FOR_UPDATE |
3724 | | - * @return String: MW timestamp |
3725 | | - */ |
3726 | | - public function getEarliestRevTime( $flags = 0 ) { |
3727 | | - $rev = $this->getFirstRevision( $flags ); |
3728 | | - return $rev ? $rev->getTimestamp() : null; |
3729 | | - } |
3730 | | - |
3731 | | - /** |
3732 | 3773 | * Check if this is a new page |
3733 | 3774 | * |
3734 | 3775 | * @return bool |
— | — | @@ -3738,70 +3779,45 @@ |
3739 | 3780 | } |
3740 | 3781 | |
3741 | 3782 | /** |
3742 | | - * Get the number of revisions between the given revision. |
3743 | | - * Used for diffs and other things that really need it. |
| 3783 | + * Get the oldest revision timestamp of this page |
3744 | 3784 | * |
3745 | | - * @param $old int|Revision Old revision or rev ID (first before range) |
3746 | | - * @param $new int|Revision New revision or rev ID (first after range) |
3747 | | - * @return Int Number of revisions between these revisions. |
| 3785 | + * @return String: MW timestamp |
3748 | 3786 | */ |
3749 | | - public function countRevisionsBetween( $old, $new ) { |
3750 | | - if ( !( $old instanceof Revision ) ) { |
3751 | | - $old = Revision::newFromTitle( $this, (int)$old ); |
3752 | | - } |
3753 | | - if ( !( $new instanceof Revision ) ) { |
3754 | | - $new = Revision::newFromTitle( $this, (int)$new ); |
3755 | | - } |
3756 | | - if ( !$old || !$new ) { |
3757 | | - return 0; // nothing to compare |
3758 | | - } |
| 3787 | + public function getEarliestRevTime() { |
3759 | 3788 | $dbr = wfGetDB( DB_SLAVE ); |
3760 | | - return (int)$dbr->selectField( 'revision', 'count(*)', |
3761 | | - array( |
3762 | | - 'rev_page' => $this->getArticleId(), |
3763 | | - 'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ), |
3764 | | - 'rev_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) ) |
3765 | | - ), |
3766 | | - __METHOD__ |
3767 | | - ); |
| 3789 | + if ( $this->exists() ) { |
| 3790 | + $min = $dbr->selectField( 'revision', |
| 3791 | + 'MIN(rev_timestamp)', |
| 3792 | + array( 'rev_page' => $this->getArticleId() ), |
| 3793 | + __METHOD__ ); |
| 3794 | + return wfTimestampOrNull( TS_MW, $min ); |
| 3795 | + } |
| 3796 | + return null; |
3768 | 3797 | } |
3769 | 3798 | |
3770 | 3799 | /** |
3771 | | - * Get the number of authors between the given revision IDs. |
| 3800 | + * Get the number of revisions between the given revision IDs. |
3772 | 3801 | * Used for diffs and other things that really need it. |
3773 | 3802 | * |
3774 | | - * @param $old int|Revision Old revision or rev ID (first before range) |
3775 | | - * @param $new int|Revision New revision or rev ID (first after range) |
3776 | | - * @param $limit Int Maximum number of authors |
3777 | | - * @return Int Number of revision authors between these revisions. |
| 3803 | + * @param $old \type{\int} Revision ID. |
| 3804 | + * @param $new \type{\int} Revision ID. |
| 3805 | + * @return \type{\int} Number of revisions between these IDs. |
3778 | 3806 | */ |
3779 | | - public function countAuthorsBetween( $old, $new, $limit ) { |
3780 | | - if ( !( $old instanceof Revision ) ) { |
3781 | | - $old = Revision::newFromTitle( $this, (int)$old ); |
3782 | | - } |
3783 | | - if ( !( $new instanceof Revision ) ) { |
3784 | | - $new = Revision::newFromTitle( $this, (int)$new ); |
3785 | | - } |
3786 | | - if ( !$old || !$new ) { |
3787 | | - return 0; // nothing to compare |
3788 | | - } |
| 3807 | + public function countRevisionsBetween( $old, $new ) { |
3789 | 3808 | $dbr = wfGetDB( DB_SLAVE ); |
3790 | | - $res = $dbr->select( 'revision', 'DISTINCT rev_user_text', |
3791 | | - array( |
3792 | | - 'rev_page' => $this->getArticleID(), |
3793 | | - 'rev_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( $old->getTimestamp() ) ), |
3794 | | - 'rev_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( $new->getTimestamp() ) ) |
3795 | | - ), __METHOD__, |
3796 | | - array( 'LIMIT' => $limit + 1 ) // add one so caller knows it was truncated |
| 3809 | + return (int)$dbr->selectField( 'revision', 'count(*)', |
| 3810 | + 'rev_page = ' . intval( $this->getArticleId() ) . |
| 3811 | + ' AND rev_id > ' . intval( $old ) . |
| 3812 | + ' AND rev_id < ' . intval( $new ), |
| 3813 | + __METHOD__ |
3797 | 3814 | ); |
3798 | | - return (int)$dbr->numRows( $res ); |
3799 | 3815 | } |
3800 | 3816 | |
3801 | 3817 | /** |
3802 | 3818 | * Compare with another title. |
3803 | 3819 | * |
3804 | | - * @param $title Title |
3805 | | - * @return Bool |
| 3820 | + * @param $title \type{Title} |
| 3821 | + * @return \type{\bool} TRUE or FALSE |
3806 | 3822 | */ |
3807 | 3823 | public function equals( Title $title ) { |
3808 | 3824 | // Note: === is necessary for proper matching of number-like titles. |
— | — | @@ -3812,10 +3828,7 @@ |
3813 | 3829 | |
3814 | 3830 | /** |
3815 | 3831 | * Callback for usort() to do title sorts by (namespace, title) |
3816 | | - * |
3817 | | - * @param $a Title |
3818 | | - * @param $b Title |
3819 | | - * |
| 3832 | + * |
3820 | 3833 | * @return Integer: result of string comparison, or namespace comparison |
3821 | 3834 | */ |
3822 | 3835 | public static function compare( $a, $b ) { |
— | — | @@ -3829,7 +3842,7 @@ |
3830 | 3843 | /** |
3831 | 3844 | * Return a string representation of this title |
3832 | 3845 | * |
3833 | | - * @return String representation of this title |
| 3846 | + * @return \type{\string} String representation of this title |
3834 | 3847 | */ |
3835 | 3848 | public function __toString() { |
3836 | 3849 | return $this->getPrefixedText(); |
— | — | @@ -3842,7 +3855,7 @@ |
3843 | 3856 | * If you want to know if a title can be meaningfully viewed, you should |
3844 | 3857 | * probably call the isKnown() method instead. |
3845 | 3858 | * |
3846 | | - * @return Bool |
| 3859 | + * @return \type{\bool} |
3847 | 3860 | */ |
3848 | 3861 | public function exists() { |
3849 | 3862 | return $this->getArticleId() != 0; |
— | — | @@ -3862,28 +3875,28 @@ |
3863 | 3876 | * existing code, but we might want to add an optional parameter to skip |
3864 | 3877 | * it and any other expensive checks.) |
3865 | 3878 | * |
3866 | | - * @return Bool |
| 3879 | + * @return \type{\bool} |
3867 | 3880 | */ |
3868 | 3881 | public function isAlwaysKnown() { |
3869 | 3882 | if ( $this->mInterwiki != '' ) { |
3870 | 3883 | return true; // any interwiki link might be viewable, for all we know |
3871 | 3884 | } |
3872 | 3885 | switch( $this->mNamespace ) { |
3873 | | - case NS_MEDIA: |
3874 | | - case NS_FILE: |
3875 | | - // file exists, possibly in a foreign repo |
3876 | | - return (bool)wfFindFile( $this ); |
3877 | | - case NS_SPECIAL: |
3878 | | - // valid special page |
3879 | | - return SpecialPageFactory::exists( $this->getDBkey() ); |
3880 | | - case NS_MAIN: |
3881 | | - // selflink, possibly with fragment |
3882 | | - return $this->mDbkeyform == ''; |
3883 | | - case NS_MEDIAWIKI: |
3884 | | - // known system message |
3885 | | - return $this->getDefaultMessageText() !== false; |
3886 | | - default: |
3887 | | - return false; |
| 3886 | + case NS_MEDIA: |
| 3887 | + case NS_FILE: |
| 3888 | + return (bool)wfFindFile( $this ); // file exists, possibly in a foreign repo |
| 3889 | + case NS_SPECIAL: |
| 3890 | + return SpecialPage::exists( $this->getDBkey() ); // valid special page |
| 3891 | + case NS_MAIN: |
| 3892 | + return $this->mDbkeyform == ''; // selflink, possibly with fragment |
| 3893 | + case NS_MEDIAWIKI: |
| 3894 | + // If the page is form Mediawiki:message/lang, calling wfMsgWeirdKey causes |
| 3895 | + // the full l10n of that language to be loaded. That takes much memory and |
| 3896 | + // isn't needed. So we strip the language part away. |
| 3897 | + list( $basename, /* rest */ ) = explode( '/', $this->mDbkeyform, 2 ); |
| 3898 | + return (bool)wfMsgWeirdKey( $basename ); // known system message |
| 3899 | + default: |
| 3900 | + return false; |
3888 | 3901 | } |
3889 | 3902 | } |
3890 | 3903 | |
— | — | @@ -3893,10 +3906,10 @@ |
3894 | 3907 | * links to the title should be rendered as "bluelinks" (as opposed to |
3895 | 3908 | * "redlinks" to non-existent pages). |
3896 | 3909 | * |
3897 | | - * @return Bool |
| 3910 | + * @return \type{\bool} |
3898 | 3911 | */ |
3899 | 3912 | public function isKnown() { |
3900 | | - return $this->isAlwaysKnown() || $this->exists(); |
| 3913 | + return $this->exists() || $this->isAlwaysKnown(); |
3901 | 3914 | } |
3902 | 3915 | |
3903 | 3916 | /** |
— | — | @@ -3912,38 +3925,20 @@ |
3913 | 3926 | if ( $this->mNamespace == NS_MEDIAWIKI ) { |
3914 | 3927 | // If the page doesn't exist but is a known system message, default |
3915 | 3928 | // message content will be displayed, same for language subpages |
3916 | | - return $this->getDefaultMessageText() !== false; |
| 3929 | + // Also, if the page is form Mediawiki:message/lang, calling wfMsgWeirdKey |
| 3930 | + // causes the full l10n of that language to be loaded. That takes much |
| 3931 | + // memory and isn't needed. So we strip the language part away. |
| 3932 | + list( $basename, /* rest */ ) = explode( '/', $this->mDbkeyform, 2 ); |
| 3933 | + return (bool)wfMsgWeirdKey( $basename ); |
3917 | 3934 | } |
3918 | 3935 | |
3919 | 3936 | return false; |
3920 | 3937 | } |
3921 | 3938 | |
3922 | 3939 | /** |
3923 | | - * Get the default message text or false if the message doesn't exist |
3924 | | - * |
3925 | | - * @return String or false |
3926 | | - */ |
3927 | | - public function getDefaultMessageText() { |
3928 | | - global $wgContLang; |
3929 | | - |
3930 | | - if ( $this->getNamespace() != NS_MEDIAWIKI ) { // Just in case |
3931 | | - return false; |
3932 | | - } |
3933 | | - |
3934 | | - list( $name, $lang ) = MessageCache::singleton()->figureMessage( $wgContLang->lcfirst( $this->getText() ) ); |
3935 | | - $message = wfMessage( $name )->inLanguage( $lang )->useDatabase( false ); |
3936 | | - |
3937 | | - if ( $message->exists() ) { |
3938 | | - return $message->plain(); |
3939 | | - } else { |
3940 | | - return false; |
3941 | | - } |
3942 | | - } |
3943 | | - |
3944 | | - /** |
3945 | 3940 | * Is this in a namespace that allows actual pages? |
3946 | 3941 | * |
3947 | | - * @return Bool |
| 3942 | + * @return \type{\bool} |
3948 | 3943 | * @internal note -- uses hardcoded namespace index instead of constants |
3949 | 3944 | */ |
3950 | 3945 | public function canExist() { |
— | — | @@ -3969,7 +3964,7 @@ |
3970 | 3965 | * Get the last touched timestamp |
3971 | 3966 | * |
3972 | 3967 | * @param $db DatabaseBase: optional db |
3973 | | - * @return String last-touched timestamp |
| 3968 | + * @return \type{\string} Last touched timestamp |
3974 | 3969 | */ |
3975 | 3970 | public function getTouched( $db = null ) { |
3976 | 3971 | $db = isset( $db ) ? $db : wfGetDB( DB_SLAVE ); |
— | — | @@ -3981,7 +3976,7 @@ |
3982 | 3977 | * Get the timestamp when this page was updated since the user last saw it. |
3983 | 3978 | * |
3984 | 3979 | * @param $user User |
3985 | | - * @return String|Null |
| 3980 | + * @return Mixed: string/null |
3986 | 3981 | */ |
3987 | 3982 | public function getNotificationTimestamp( $user = null ) { |
3988 | 3983 | global $wgUser, $wgShowUpdatedMarker; |
— | — | @@ -3991,8 +3986,7 @@ |
3992 | 3987 | } |
3993 | 3988 | // Check cache first |
3994 | 3989 | $uid = $user->getId(); |
3995 | | - // avoid isset here, as it'll return false for null entries |
3996 | | - if ( array_key_exists( $uid, $this->mNotificationTimestamp ) ) { |
| 3990 | + if ( isset( $this->mNotificationTimestamp[$uid] ) ) { |
3997 | 3991 | return $this->mNotificationTimestamp[$uid]; |
3998 | 3992 | } |
3999 | 3993 | if ( !$uid || !$wgShowUpdatedMarker ) { |
— | — | @@ -4017,7 +4011,7 @@ |
4018 | 4012 | /** |
4019 | 4013 | * Get the trackback URL for this page |
4020 | 4014 | * |
4021 | | - * @return String Trackback URL |
| 4015 | + * @return \type{\string} Trackback URL |
4022 | 4016 | */ |
4023 | 4017 | public function trackbackURL() { |
4024 | 4018 | global $wgScriptPath, $wgServer, $wgScriptExtension; |
— | — | @@ -4029,7 +4023,7 @@ |
4030 | 4024 | /** |
4031 | 4025 | * Get the trackback RDF for this page |
4032 | 4026 | * |
4033 | | - * @return String Trackback RDF |
| 4027 | + * @return \type{\string} Trackback RDF |
4034 | 4028 | */ |
4035 | 4029 | public function trackbackRDF() { |
4036 | 4030 | $url = htmlspecialchars( $this->getFullURL() ); |
— | — | @@ -4043,8 +4037,8 @@ |
4044 | 4038 | // Spec: http://www.sixapart.com/pronet/docs/trackback_spec |
4045 | 4039 | return "<!-- |
4046 | 4040 | <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" |
4047 | | - xmlns:dc=\"http://purl.org/dc/elements/1.1/\" |
4048 | | - xmlns:trackback=\"http://madskills.com/public/xml/rss/module/trackback/\"> |
| 4041 | + xmlns:dc=\"http://purl.org/dc/elements/1.1/\" |
| 4042 | + xmlns:trackback=\"http://madskills.com/public/xml/rss/module/trackback/\"> |
4049 | 4043 | <rdf:Description |
4050 | 4044 | rdf:about=\"$url\" |
4051 | 4045 | dc:identifier=\"$url\" |
— | — | @@ -4058,7 +4052,7 @@ |
4059 | 4053 | * Generate strings used for xml 'id' names in monobook tabs |
4060 | 4054 | * |
4061 | 4055 | * @param $prepend string defaults to 'nstab-' |
4062 | | - * @return String XML 'id' name |
| 4056 | + * @return \type{\string} XML 'id' name |
4063 | 4057 | */ |
4064 | 4058 | public function getNamespaceKey( $prepend = 'nstab-' ) { |
4065 | 4059 | global $wgContLang; |
— | — | @@ -4097,12 +4091,12 @@ |
4098 | 4092 | /** |
4099 | 4093 | * Returns true if this title resolves to the named special page |
4100 | 4094 | * |
4101 | | - * @param $name String The special page name |
| 4095 | + * @param $name \type{\string} The special page name |
4102 | 4096 | * @return boolean |
4103 | 4097 | */ |
4104 | 4098 | public function isSpecial( $name ) { |
4105 | 4099 | if ( $this->getNamespace() == NS_SPECIAL ) { |
4106 | | - list( $thisName, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $this->getDBkey() ); |
| 4100 | + list( $thisName, /* $subpage */ ) = SpecialPage::resolveAliasWithSubpage( $this->getDBkey() ); |
4107 | 4101 | if ( $name == $thisName ) { |
4108 | 4102 | return true; |
4109 | 4103 | } |
— | — | @@ -4111,16 +4105,16 @@ |
4112 | 4106 | } |
4113 | 4107 | |
4114 | 4108 | /** |
4115 | | - * If the Title refers to a special page alias which is not the local default, resolve |
4116 | | - * the alias, and localise the name as necessary. Otherwise, return $this |
| 4109 | + * If the Title refers to a special page alias which is not the local default, |
4117 | 4110 | * |
4118 | | - * @return Title |
| 4111 | + * @return \type{Title} A new Title which points to the local default. |
| 4112 | + * Otherwise, returns $this. |
4119 | 4113 | */ |
4120 | 4114 | public function fixSpecialName() { |
4121 | 4115 | if ( $this->getNamespace() == NS_SPECIAL ) { |
4122 | | - list( $canonicalName, /*...*/ ) = SpecialPageFactory::resolveAlias( $this->mDbkeyform ); |
| 4116 | + $canonicalName = SpecialPage::resolveAlias( $this->mDbkeyform ); |
4123 | 4117 | if ( $canonicalName ) { |
4124 | | - $localName = SpecialPageFactory::getLocalNameFor( $canonicalName ); |
| 4118 | + $localName = SpecialPage::getLocalNameFor( $canonicalName ); |
4125 | 4119 | if ( $localName != $this->mDbkeyform ) { |
4126 | 4120 | return Title::makeTitle( NS_SPECIAL, $localName ); |
4127 | 4121 | } |
— | — | @@ -4134,7 +4128,7 @@ |
4135 | 4129 | * In other words, is this a content page, for the purposes of calculating |
4136 | 4130 | * statistics, etc? |
4137 | 4131 | * |
4138 | | - * @return Boolean |
| 4132 | + * @return \type{\bool} |
4139 | 4133 | */ |
4140 | 4134 | public function isContentPage() { |
4141 | 4135 | return MWNamespace::isContent( $this->getNamespace() ); |
— | — | @@ -4143,8 +4137,9 @@ |
4144 | 4138 | /** |
4145 | 4139 | * Get all extant redirects to this Title |
4146 | 4140 | * |
4147 | | - * @param $ns Int|Null Single namespace to consider; NULL to consider all namespaces |
4148 | | - * @return Array of Title redirects to this title |
| 4141 | + * @param $ns \twotypes{\int,\null} Single namespace to consider; |
| 4142 | + * NULL to consider all namespaces |
| 4143 | + * @return \type{\arrayof{Title}} Redirects to this title |
4149 | 4144 | */ |
4150 | 4145 | public function getRedirectsHere( $ns = null ) { |
4151 | 4146 | $redirs = array(); |
— | — | @@ -4166,6 +4161,7 @@ |
4167 | 4162 | __METHOD__ |
4168 | 4163 | ); |
4169 | 4164 | |
| 4165 | + |
4170 | 4166 | foreach ( $res as $row ) { |
4171 | 4167 | $redirs[] = self::newFromRow( $row ); |
4172 | 4168 | } |
— | — | @@ -4175,7 +4171,7 @@ |
4176 | 4172 | /** |
4177 | 4173 | * Check if this Title is a valid redirect target |
4178 | 4174 | * |
4179 | | - * @return Bool |
| 4175 | + * @return \type{\bool} |
4180 | 4176 | */ |
4181 | 4177 | public function isValidRedirectTarget() { |
4182 | 4178 | global $wgInvalidRedirectTargets; |
— | — | @@ -4207,7 +4203,8 @@ |
4208 | 4204 | } |
4209 | 4205 | |
4210 | 4206 | /** |
4211 | | - * Whether the magic words __INDEX__ and __NOINDEX__ function for this page. |
| 4207 | + * Whether the magic words __INDEX__ and __NOINDEX__ function for |
| 4208 | + * this page. |
4212 | 4209 | * |
4213 | 4210 | * @return Boolean |
4214 | 4211 | */ |
— | — | @@ -4228,47 +4225,21 @@ |
4229 | 4226 | * @return array applicable restriction types |
4230 | 4227 | */ |
4231 | 4228 | public function getRestrictionTypes() { |
4232 | | - if ( $this->getNamespace() == NS_SPECIAL ) { |
4233 | | - return array(); |
4234 | | - } |
| 4229 | + global $wgRestrictionTypes; |
| 4230 | + $types = $this->exists() ? $wgRestrictionTypes : array( 'create' ); |
4235 | 4231 | |
4236 | | - $types = self::getFilteredRestrictionTypes( $this->exists() ); |
4237 | | - |
4238 | | - if ( $this->getNamespace() != NS_FILE ) { |
4239 | | - # Remove the upload restriction for non-file titles |
4240 | | - $types = array_diff( $types, array( 'upload' ) ); |
| 4232 | + if ( $this->getNamespace() == NS_FILE ) { |
| 4233 | + $types[] = 'upload'; |
4241 | 4234 | } |
4242 | 4235 | |
4243 | 4236 | wfRunHooks( 'TitleGetRestrictionTypes', array( $this, &$types ) ); |
4244 | 4237 | |
4245 | | - wfDebug( __METHOD__ . ': applicable restriction types for ' . |
4246 | | - $this->getPrefixedText() . ' are ' . implode( ',', $types ) . "\n" ); |
4247 | | - |
4248 | 4238 | return $types; |
4249 | 4239 | } |
4250 | | - /** |
4251 | | - * Get a filtered list of all restriction types supported by this wiki. |
4252 | | - * @param bool $exists True to get all restriction types that apply to |
4253 | | - * titles that do exist, False for all restriction types that apply to |
4254 | | - * titles that do not exist |
4255 | | - * @return array |
4256 | | - */ |
4257 | | - public static function getFilteredRestrictionTypes( $exists = true ) { |
4258 | | - global $wgRestrictionTypes; |
4259 | | - $types = $wgRestrictionTypes; |
4260 | | - if ( $exists ) { |
4261 | | - # Remove the create restriction for existing titles |
4262 | | - $types = array_diff( $types, array( 'create' ) ); |
4263 | | - } else { |
4264 | | - # Only the create and upload restrictions apply to non-existing titles |
4265 | | - $types = array_intersect( $types, array( 'create', 'upload' ) ); |
4266 | | - } |
4267 | | - return $types; |
4268 | | - } |
4269 | 4240 | |
4270 | 4241 | /** |
4271 | 4242 | * Returns the raw sort key to be used for categories, with the specified |
4272 | | - * prefix. This will be fed to Collation::getSortKey() to get a |
| 4243 | + * prefix. This will be fed to Language::convertToSortkey() to get a |
4273 | 4244 | * binary sortkey that can be used for actual sorting. |
4274 | 4245 | * |
4275 | 4246 | * @param $prefix string The prefix to be used, specified using |
— | — | @@ -4285,12 +4256,10 @@ |
4286 | 4257 | // in order to re-sort existing category relations. |
4287 | 4258 | wfRunHooks( 'GetDefaultSortkey', array( $this, &$unprefixed ) ); |
4288 | 4259 | if ( $prefix !== '' ) { |
4289 | | - # Separate with a line feed, so the unprefixed part is only used as |
4290 | | - # a tiebreaker when two pages have the exact same prefix. |
4291 | | - # In UCA, tab is the only character that can sort above LF |
4292 | | - # so we strip both of them from the original prefix. |
4293 | | - $prefix = strtr( $prefix, "\n\t", ' ' ); |
4294 | | - return "$prefix\n$unprefixed"; |
| 4260 | + # Separate with a null byte, so the unprefixed part is only used as |
| 4261 | + # a tiebreaker when two pages have the exact same prefix -- null |
| 4262 | + # sorts before everything else (hopefully). |
| 4263 | + return "$prefix\0$unprefixed"; |
4295 | 4264 | } |
4296 | 4265 | return $unprefixed; |
4297 | 4266 | } |
— | — | @@ -4326,37 +4295,3 @@ |
4327 | 4296 | return wfGetLangObj( $pageLang ); |
4328 | 4297 | } |
4329 | 4298 | } |
4330 | | - |
4331 | | -/** |
4332 | | - * A BadTitle is generated in MediaWiki::parseTitle() if the title is invalid; the |
4333 | | - * software uses this to display an error page. Internally it's basically a Title |
4334 | | - * for an empty special page |
4335 | | - */ |
4336 | | -class BadTitle extends Title { |
4337 | | - public function __construct(){ |
4338 | | - $this->mTextform = ''; |
4339 | | - $this->mUrlform = ''; |
4340 | | - $this->mDbkeyform = ''; |
4341 | | - $this->mNamespace = NS_SPECIAL; // Stops talk page link, etc, being shown |
4342 | | - } |
4343 | | - |
4344 | | - public function exists(){ |
4345 | | - return false; |
4346 | | - } |
4347 | | - |
4348 | | - public function getPrefixedText(){ |
4349 | | - return ''; |
4350 | | - } |
4351 | | - |
4352 | | - public function getText(){ |
4353 | | - return ''; |
4354 | | - } |
4355 | | - |
4356 | | - public function getPrefixedURL(){ |
4357 | | - return ''; |
4358 | | - } |
4359 | | - |
4360 | | - public function getPrefixedDBKey(){ |
4361 | | - return ''; |
4362 | | - } |
4363 | | -} |
Property changes on: branches/iwtransclusion/phase3v3/includes/Title.php |
___________________________________________________________________ |
Modified: svn:mergeinfo |
4364 | 4299 | Merged /branches/iwtransclusion/phase3v2/includes/Title.php:r87108 |
4365 | 4300 | Merged /branches/iwtransclusion/phase3/includes/Title.php:r70764 |
Index: branches/iwtransclusion/phase3v3/languages/messages/MessagesEn.php |
— | — | @@ -1399,6 +1399,9 @@ |
1400 | 1400 | 'templatesused' => '{{PLURAL:$1|Template|Templates}} used on this page:', |
1401 | 1401 | 'templatesusedpreview' => '{{PLURAL:$1|Template|Templates}} used in this preview:', |
1402 | 1402 | 'templatesusedsection' => '{{PLURAL:$1|Template|Templates}} used in this section:', |
| 1403 | +'distanttemplatesused' => 'Distant {{PLURAL:$1|template|templates}} used on this page:', |
| 1404 | +'distanttemplatesusedpreview' => 'Distant {{PLURAL:$1|template|templates}} used in this preview:', |
| 1405 | +'distanttemplatesusedsection' => 'Distant {{PLURAL:$1|template|templates}} used in this section:', |
1403 | 1406 | 'template-protected' => '(protected)', |
1404 | 1407 | 'template-semiprotected' => '(semi-protected)', |
1405 | 1408 | 'hiddencategories' => 'This page is a member of {{PLURAL:$1|1 hidden category|$1 hidden categories}}:', |