integration with Title and Revision (work in progress)
authorDaniel Kinzler <daniel.kinzler@wikimedia.de>
Mon, 5 Mar 2012 11:53:21 +0000 (11:53 +0000)
committerDaniel Kinzler <daniel.kinzler@wikimedia.de>
Wed, 4 Apr 2012 17:41:52 +0000 (19:41 +0200)
includes/Article.php
includes/Content.php [new file with mode: 0644]
includes/ContentHandler.php
includes/DefaultSettings.php
includes/Defines.php
includes/EditPage.php
includes/Revision.php
includes/Title.php
includes/WikiPage.php
includes/parser/ParserOutput.php

index 1fbcdb4..06c460d 100644 (file)
@@ -37,7 +37,7 @@ class Article extends Page {
         */
        public $mParserOptions;
 
-       var $mContent;                    // !<
+       var $mContent;                    // !<  #FIXME: use Content object!
        var $mContentLoaded = false;      // !<
        var $mOldId;                      // !<
 
@@ -557,6 +557,7 @@ class Article extends Page {
 
                                        # Pages containing custom CSS or JavaScript get special treatment
                                        if ( $this->getTitle()->isCssOrJsPage() || $this->getTitle()->isCssJsSubpage() ) {
+                        #FIXME: use Content object instead!
                                                wfDebug( __METHOD__ . ": showing CSS/JS source\n" );
                                                $this->showCssOrJsPage();
                                                $outputDone = true;
@@ -694,17 +695,18 @@ class Article extends Page {
         * This is hooked by SyntaxHighlight_GeSHi to do syntax highlighting of these
         * page views.
         */
-       protected function showCssOrJsPage() {
+       protected function showCssOrJsPage() { #FIXME: deprecate, keep for BC
                global $wgOut;
 
                $dir = $this->getContext()->getLanguage()->getDir();
                $lang = $this->getContext()->getLanguage()->getCode();
 
                $wgOut->wrapWikiMsg( "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
-                       'clearyourcache' );
+                       'clearyourcache' ); #FIXME: get this from handler
 
                // Give hooks a chance to customise the output
                if ( wfRunHooks( 'ShowRawCssJs', array( $this->mContent, $this->getTitle(), $wgOut ) ) ) {
+            #FIXME: use content object instead
                        // Wrap the whole lot in a <pre> and don't parse
                        $m = array();
                        preg_match( '!\.(css|js)$!u', $this->getTitle()->getText(), $m );
diff --git a/includes/Content.php b/includes/Content.php
new file mode 100644 (file)
index 0000000..b8e138e
--- /dev/null
@@ -0,0 +1,166 @@
+<?php
+
+/**
+ * A content object represents page content, e.g. the text to show on a page.
+ *
+ */
+abstract class Content {
+    
+    public function __construct( Title $title, $revId, $modelName ) {
+        $this->mModelName = $modelName;
+        $this->mTitle = $title;
+        $this->mRevId = $revId;
+    }
+
+    public function getModelName() {
+        return $this->mModelName;
+    }
+
+    public function getTitle() {
+        return $this->mTitle;
+    }
+
+    public function getRevId() {
+        return $this->mRevId;
+    }
+
+    public abstract function getSearchText( $obj );
+
+    public abstract function getWikitextForTransclusion( $obj );
+
+    public abstract function getParserOutput( ParserOptions $options = NULL );
+
+    public abstract function getRawData( );
+
+    public function getHtml( ParserOptions $options ) {
+        $po = $this->getParserOutput( $options );
+        return $po->getText();
+    }
+
+    public function getIndexUpdateJobs( ParserOptions $options , $recursive = true ) {
+        $po = $this->getParserOutput( $options );
+        $update = new LinksUpdate( $this->mTitle, $po, $recursive );
+        return $update;
+    }
+
+    #XXX: is the native model for wikitext a string or the parser output? parse early or parse late?
+}
+
+class TextContent extends Content {
+    public function __construct( $text, Title $title, $revId, $modelName ) {
+        parent::__construct($title, $revId, $modelName);
+
+        $this->mText = $text;
+    }
+
+    public function getSearchText( $obj ) {
+        return $this->getRawData();
+    }
+
+    public function getWikitextForTransclusion( $obj ) {
+        return $this->getRawData();
+    }
+
+
+    public function getParserOutput( ParserOptions $options = null ) {
+        # generic implementation, relying on $this->getHtml()
+
+        $html = $this->getHtml( $options );
+        $po = new ParserOutput( $html );
+
+        #TODO: cache settings, etc?
+
+        return $po;
+    }
+
+    public function getHtml( ParserOptions $options ) {
+        $html = "";
+        $html .= "<pre class=\"mw-code\" dir=\"ltr\">\n";
+        $html .= htmlspecialchars( $this->getRawData() );
+        $html .= "\n</pre>\n";
+
+        return $html;
+    }
+
+
+    public function getRawData( ) {
+        global $wgParser, $wgUser;
+
+        $text = $this->mText;
+        return $text;
+    }
+
+}
+
+class WikitextContent extends TextContent {
+    public function __construct( $text, Title $title, $revId = null) {
+        parent::__construct($text, $title, $revId, CONTENT_MODEL_WIKITEXT);
+
+        $this->mDefaultParserOptions = null;
+    }
+
+    public function getDefaultParserOptions() {
+        global $wgUser, $wgContLang;
+
+        if ( !$this->mDefaultParserOptions ) {
+            #TODO: use static member?!
+            $this->mDefaultParserOptions = ParserOptions::newFromUserAndLang( $wgUser, $wgContLang );
+        }
+
+        return $this->mDefaultParserOptions;
+    }
+
+    public function getParserOutput( ParserOptions $options = null ) {
+        global $wgParser;
+
+        #TODO: quick local cache: if $options is NULL, use ->mParserOutput!
+        #FIXME: need setParserOutput, so we can use stuff from the parser cache??
+        #FIXME: ...or we somehow need to know the parser cache key??
+
+        if ( !$options ) {
+            $options = $this->getDefaultParserOptions();
+        }
+
+        $po = $wgParser->parse( $this->mText, $this->getTitle(), $options );
+
+        return $po;
+    }
+
+}
+
+
+class JavaScriptContent extends TextContent {
+    public function __construct( $text, Title $title, $revId = null ) {
+        parent::__construct($text, $title, $revId, CONTENT_MODEL_JAVASCRIPT);
+    }
+
+    public function getHtml( ParserOptions $options ) {
+        $html = "";
+        $html .= "<pre class=\"mw-code mw-js\" dir=\"ltr\">\n";
+        $html .= htmlspecialchars( $this->getRawData() );
+        $html .= "\n</pre>\n";
+
+        return $html;
+    }
+
+}
+
+class CssContent extends TextContent {
+    public function __construct( $text, Title $title, $revId = null ) {
+        parent::__construct($text, $title, $revId, CONTENT_MODEL_CSS);
+    }
+
+    public function getHtml( ParserOptions $options ) {
+        $html = "";
+        $html .= "<pre class=\"mw-code mw-css\" dir=\"ltr\">\n";
+        $html .= htmlspecialchars( $this->getRawData() );
+        $html .= "\n</pre>\n";
+
+        return $html;
+    }
+}
+
+#TODO: MultipartMultipart < WikipageContent (Main + Links + X)
+#TODO: LinksContent < LanguageLinksContent, CategoriesContent
+#EXAMPLE: CoordinatesContent
+#EXAMPLE: WikidataContent
index 7b076de..b7761d6 100644 (file)
  *  
  */
 abstract class ContentHandler {
-    
+
+    public static function getDefaultModelFor( Title $title ) {
+        global $wgNamespaceContentModels;
+
+        # NOTE: this method must not rely on $title->getContentModelName() directly or indirectly,
+        #       because it is used to initialized the mContentModelName memebr.
+
+        $ns = $title->getNamespace();
+
+        $ext = false;
+        $m = null;
+        $model = null;
+
+        if ( !empty( $wgNamespaceContentModels[ $ns ] ) ) {
+            $model = $wgNamespaceContentModels[ $ns ];
+        }
+
+        # hook can determin default model
+        if ( !wfRunHooks( 'DefaultModelFor', array( $title, &$model ) ) ) { #FIXME: document new hook!
+            if ( $model ) return $model;
+        }
+
+        # Could this page contain custom CSS or JavaScript, based on the title?
+        $isCssOrJsPage = ( NS_MEDIAWIKI == $ns && preg_match( "!\.(css|js)$!u", $title->getText(), $m ) );
+        if ( $isCssOrJsPage ) $ext = $m[1];
+
+        # hook can force js/css
+        wfRunHooks( 'TitleIsCssOrJsPage', array( $title, &$isCssOrJsPage, &$ext ) ); #FIXME: add $ext to hook interface spec
+
+        # Is this a .css subpage of a user page?
+        $isJsCssSubpage = ( NS_USER == $ns && !$isCssOrJsPage && preg_match( "/\\/.*\\.(js|css)$/", $title->getText(), $m ) );
+        if ( $isJsCssSubpage ) $ext = $m[1];
+
+        # is this wikitext, according to $wgNamespaceContentModels or the DefaultModelFor hook?
+        $isWikitext = ( $model == CONTENT_MODEL_WIKITEXT || $model === null );
+        $isWikitext = ( $isWikitext && !$isCssOrJsPage && !$isJsCssSubpage );
+
+        # hook can override $isWikitext
+        wfRunHooks( 'TitleIsWikitextPage', array( $title, &$isWikitext ) );
+
+        if ( !$isWikitext ) {
+
+            if ( $ext == 'js' )
+                return CONTENT_MODEL_JAVASCRIPT;
+            else if ( $ext == 'css' )
+                return CONTENT_MODEL_CSS;
+
+            if ( $model )
+                return $model;
+            else
+                return CONTENT_MODEL_TEXT;
+        }
+
+        # we established that is must be wikitext
+        return CONTENT_MODEL_WIKITEXT;
+    }
+
+    public static function getForTitle( Title $title ) {
+        $modelName = $title->getContentModelName();
+        return ContenteHandler::getForModelName( $modelName );
+    }
+
+    public static function getForContent( Content $content ) {
+        $modelName = $content->getModelName();
+        return ContenteHandler::getForModelName( $modelName );
+    }
+
+    public static function getForModelName( $modelName ) {
+        global $wgContentHandlers;
+
+        if ( empty( $wgContentHandlers[$modelName] ) ) {
+            #FIXME: hook here!
+            throw new MWException( "No handler for model $modelName registered in \$wgContentHandlers" );
+        }
+
+        if ( is_string( $wgContentHandlers[$modelName] ) ) {
+            $class = $wgContentHandlers[$modelName];
+            $wgContentHandlers[$modelName] = new $class( $modelName );
+        }
+
+        return $wgContentHandlers[$modelName];
+    }
+
+
     public function __construct( $modelName, $formats ) {
         $this->mModelName = $modelName;
         $this->mSupportedFormats = $formats;
@@ -62,7 +145,7 @@ abstract class ContentHandler {
             # returns a ParserOutput instance!
             # are parser options, generic?!
 
-    public function doPreSaveTransform( $title, $obj );
+    public abstract function doPreSaveTransform( $title, $obj );
 
     # TODO: getPreloadText()
     # TODO: preprocess()
index c0ef5b3..9536258 100644 (file)
@@ -636,6 +636,17 @@ $wgMediaHandlers = array(
        'image/x-djvu' => 'DjVuHandler', // compat
 );
 
+/**
+ * Plugins for page content model handling.
+ * Each entry in the array maps a model name type to a class name
+ */
+$wgContentHandlers = array(
+    CONTENT_MODEL_WIKITEXT => 'WikitextContentHandler', // the usual case
+    CONTENT_MODEL_JAVASCRIPT => 'JavaScriptContentHandler', // dumb version, no syntax highlighting
+    CONTENT_MODEL_CSS => 'CssContentHandler', // dumb version, no syntax highlighting
+    CONTENT_MODEL_TEXT => 'TextContentHandler', // dumb plain text in <pre>
+);
+
 /**
  * Resizing can be done using PHP's internal image libraries or using
  * ImageMagick or another third-party converter, e.g. GraphicMagick.
index 26deb2b..ea05301 100644 (file)
@@ -249,3 +249,12 @@ define( 'PROTO_RELATIVE', '//' );
 define( 'PROTO_CURRENT', null );
 define( 'PROTO_CANONICAL', 1 );
 define( 'PROTO_INTERNAL', 2 );
+
+/**
+ * Content model names, used by Content and ContentHandler
+ */
+define('CONTENT_MODEL_WIKITEXT', 'wikitext');
+define('CONTENT_MODEL_JAVASCRIPT', 'javascript');
+define('CONTENT_MODEL_CSS', 'css');
+define('CONTENT_MODEL_TEXT', 'text');
+
index 29aaee8..0f32c70 100644 (file)
@@ -2552,6 +2552,7 @@ HTML
                # don't parse non-wikitext pages, show message about preview
                # XXX: stupid php bug won't let us use $this->getContextTitle()->isCssJsSubpage() here -- This note has been there since r3530. Sure the bug was fixed time ago?
 
+        #FIXME: get appropriate content handler!
                if ( $this->isCssJsSubpage || !$this->mTitle->isWikitextPage() ) {
                        if( $this->mTitle->isCssJsSubpage() ) {
                                $level = 'user';
@@ -2564,6 +2565,7 @@ HTML
                        # Used messages to make sure grep find them:
                        # Messages: usercsspreview, userjspreview, sitecsspreview, sitejspreview
                        if( $level ) {
+                           #FIXME: move this crud into ContentHandler class!
                                if (preg_match( "/\\.css$/", $this->mTitle->getText() ) ) {
                                        $previewtext = "<div id='mw-{$level}csspreview'>\n" . wfMsg( "{$level}csspreview" ) . "\n</div>";
                                        $class = "mw-code mw-css";
index b254e81..4e2a091 100644 (file)
@@ -20,6 +20,8 @@ class Revision {
        protected $mTextRow;
        protected $mTitle;
        protected $mCurrent;
+    protected $mContentModelName;
+    protected $mContentType;
 
        const DELETED_TEXT = 1;
        const DELETED_COMMENT = 2;
@@ -125,6 +127,8 @@ class Revision {
                        'deleted'    => $row->ar_deleted,
                        'len'        => $row->ar_len,
                        'sha1'       => isset( $row->ar_sha1 ) ? $row->ar_sha1 : null,
+            'content_model' => isset( $row->ar_content_model ) ? $row->ar_content_model : null,
+            'content_type'  => isset( $row->ar_content_type ) ? $row->ar_content_type : null,
                );
                if ( isset( $row->ar_text ) && !$row->ar_text_id ) {
                        // Pre-1.5 ar_text row
@@ -412,6 +416,18 @@ class Revision {
                                $this->mTitle = null;
                        }
 
+            if( !isset( $row->rev_content_model ) || is_null( $row->rev_content_model ) ) {
+                $this->mContentModelName = null; # determine on demand if needed
+            } else {
+                $this->mContentModelName = strval( $row->rev_content_model );
+            }
+
+            if( !isset( $row->rev_content_type ) || is_null( $row->rev_content_type ) ) {
+                $this->mContentType = null; # determine on demand if needed
+            } else {
+                $this->mContentType = strval( $row->rev_content_type );
+            }
+
                        // Lazy extraction...
                        $this->mText      = null;
                        if( isset( $row->old_text ) ) {
@@ -445,6 +461,15 @@ class Revision {
                        $this->mParentId  = isset( $row['parent_id']  ) ? intval( $row['parent_id']  ) : null;
                        $this->mSha1      = isset( $row['sha1']  )      ? strval( $row['sha1']  )      : null;
 
+            $this->mContentModelName = isset( $row['content_model']  )  ? strval( $row['content_model'] ) : null;
+            $this->mContentType      = isset( $row['content_type']  )   ? strval( $row['content_type'] ) : null;
+
+            if( !isset( $row->rev_content_type ) || is_null( $row->rev_content_type ) ) {
+                $this->mContentType = null; # determine on demand if needed
+            } else {
+                $this->mContentType = $row->rev_content_type;
+            }
+
                        // Enforce spacing trimming on supplied text
                        $this->mComment   = isset( $row['comment']    ) ?  trim( strval( $row['comment'] ) ) : null;
                        $this->mText      = isset( $row['text']       ) ? rtrim( strval( $row['text']    ) ) : null;
@@ -460,6 +485,9 @@ class Revision {
                        if ( $this->mSha1 === null ) {
                                $this->mSha1 = is_null( $this->mText ) ? null : self::base36Sha1( $this->mText );
                        }
+
+            $this->getContentModelName(); # force lazy init
+            $this->getContentType();      # force lazy init
                } else {
                        throw new MWException( 'Revision constructor passed invalid row format.' );
                }
@@ -723,17 +751,48 @@ class Revision {
         * @param $user User object to check for, only if FOR_THIS_USER is passed
         *              to the $audience parameter
         * @return String
+     * @deprectaed in 1.20, use getContent() instead
         */
-       public function getText( $audience = self::FOR_PUBLIC, User $user = null ) {
-               if( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_TEXT ) ) {
-                       return '';
-               } elseif( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_TEXT, $user ) ) {
-                       return '';
-               } else {
-                       return $this->getRawText();
-               }
+       public function getText( $audience = self::FOR_PUBLIC, User $user = null ) { #FIXME: deprecated, replace usage!
+        wfDeprecated( __METHOD__, '1.20' );
+        $content = $this->getContent();
+
+        if ( $content == null ) {
+            return ""; # not allowed
+        }
+
+        if ( $content instanceof TextContent ) {
+            #FIXME: or check by model name? or define $content->allowRawData()?
+            return $content->getRawData();
+        }
+
+        #TODO: log this failure!
+        return null;
        }
 
+    /**
+     * Fetch revision content if it's available to the specified audience.
+     * If the specified audience does not have the ability to view this
+     * revision, null will be returned.
+     *
+     * @param $audience Integer: one of:
+     *      Revision::FOR_PUBLIC       to be displayed to all users
+     *      Revision::FOR_THIS_USER    to be displayed to $wgUser
+     *      Revision::RAW              get the text regardless of permissions
+     * @param $user User object to check for, only if FOR_THIS_USER is passed
+     *              to the $audience parameter
+     * @return Content
+     */
+    public function getContent( $audience = self::FOR_PUBLIC, User $user = null ) {
+        if( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_TEXT ) ) {
+            return null;
+        } elseif( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_TEXT, $user ) ) {
+            return null;
+        } else {
+            return $this->getContentInternal();
+        }
+    }
+
        /**
         * Alias for getText(Revision::FOR_THIS_USER)
         *
@@ -750,14 +809,55 @@ class Revision {
         *
         * @return String
         */
-       public function getRawText() {
-               if( is_null( $this->mText ) ) {
-                       // Revision text is immutable. Load on demand:
-                       $this->mText = $this->loadText();
-               }
-               return $this->mText;
+       public function getRawText() { #FIXME: deprecated, replace usage!
+               return $this->getText( self::RAW );
        }
 
+    protected function getContentInternal() {
+        if( is_null( $this->mContent ) ) {
+            // Revision is immutable. Load on demand:
+
+            $handler = $this->getContentHandler();
+            $type = $this->getContentType();
+
+            if( is_null( $this->mText ) ) {
+                // Load text on demand:
+                $this->mText = $this->loadText();
+            }
+
+            $this->mContent = $handler->unserialize( $this->mText, $type );
+        }
+
+        return $this->mContent;
+    }
+
+    public function getContentModelName() {
+        if ( !$this->mContentModelName ) {
+            $title = $this->getTitle(); #XXX: never null?
+            $this->mContentModelName = $title->getContentModelName();
+        }
+
+        return $this->mContentModelName;
+    }
+
+    public function getContentType() {
+        if ( !$this->mContentType ) {
+            $handler = $this->getContentHandler();
+            $this->mContentType = $handler->getDefaultFormat();
+        }
+
+        return $this->mContentType;
+    }
+
+    public function getContentHandler() {
+        if ( !$this->mContentHandler ) {
+            $m = $this->getModelName();
+            $this->mContentHandler = ContentHandler::getForModelName( $m );
+        }
+
+        return $this->mContentHandler;
+    }
+
        /**
         * @return String
         */
@@ -996,7 +1096,9 @@ class Revision {
                                        : $this->mParentId,
                                'rev_sha1'       => is_null( $this->mSha1 )
                                        ? Revision::base36Sha1( $this->mText )
-                                       : $this->mSha1
+                                       : $this->mSha1,
+                'rev_content_model'       => $this->getContentModelName(),
+                'rev_content_type'        => $this->getContentType(),
                        ), __METHOD__
                );
 
@@ -1096,7 +1198,8 @@ class Revision {
 
                $current = $dbw->selectRow(
                        array( 'page', 'revision' ),
-                       array( 'page_latest', 'rev_text_id', 'rev_len', 'rev_sha1' ),
+                       array( 'page_latest', 'rev_text_id', 'rev_len', 'rev_sha1',
+                    'rev_content_model', 'rev_content_type' ),
                        array(
                                'page_id' => $pageId,
                                'page_latest=rev_id',
@@ -1111,7 +1214,9 @@ class Revision {
                                'text_id'    => $current->rev_text_id,
                                'parent_id'  => $current->page_latest,
                                'len'        => $current->rev_len,
-                               'sha1'       => $current->rev_sha1
+                               'sha1'       => $current->rev_sha1,
+                               'content_model'  => $current->rev_content_model,
+                               'content_type'   => $current->rev_content_type
                                ) );
                } else {
                        $revision = null;
index b91c55c..a52f353 100644 (file)
@@ -272,12 +272,17 @@ class Title {
                        if ( isset( $row->page_is_redirect ) )
                                $this->mRedirect = (bool)$row->page_is_redirect;
                        if ( isset( $row->page_latest ) )
-                               $this->mLatestID = (int)$row->page_latest;
+                               $this->mLatestID = (int)$row->page_latest; # FIXME: whene3ver page_latest is updated, also update page_content_model
+            if ( isset( $row->page_content_model ) )
+                $this->mContentModelName = $row->page_content_model;
+            else
+                $this->mContentModelName = null; # initialized lazily in getContentModelName()
                } else { // page not found
                        $this->mArticleID = 0;
                        $this->mLength = 0;
                        $this->mRedirect = false;
                        $this->mLatestID = 0;
+            $this->mContentModelName = null; # initialized lazily in getContentModelName()
                }
        }
 
@@ -303,6 +308,7 @@ class Title {
                $t->mArticleID = ( $ns >= 0 ) ? -1 : 0;
                $t->mUrlform = wfUrlencode( $t->mDbkeyform );
                $t->mTextform = str_replace( '_', ' ', $title );
+        $t->mContentModelName = null; # initialized lazily in getContentModelName()
                return $t;
        }
 
@@ -688,6 +694,29 @@ class Title {
                return $this->mNamespace;
        }
 
+    /**
+     * Get the page's content model name
+     *
+     * @return Integer: Namespace index
+     */
+    public function getContentModelName() {
+        if ( empty( $this->mContentModelName ) ) {
+            $this->mContentModelName = ContentHandler::getDefaultModelFor( $this );
+        }
+
+        return $this->mContentModelName;
+    }
+
+    /**
+     * Conveniance method for checking a title's content model name
+     *
+     * @param $name
+     * @return true if $this->getContentModelName() == $name
+     */
+    public function hasContentModel( $name ) {
+        return $this->getContentModelName() == $name;
+    }
+
        /**
         * Get the namespace text
         *
@@ -937,9 +966,7 @@ class Title {
         * @return Bool
         */
        public function isWikitextPage() {
-               $retval = !$this->isCssOrJsPage() && !$this->isCssJsSubpage();
-               wfRunHooks( 'TitleIsWikitextPage', array( $this, &$retval ) );
-               return $retval;
+               return $this->hasContentModel( CONTENT_MODEL_WIKITEXT );
        }
 
        /**
@@ -949,10 +976,8 @@ class Title {
         * @return Bool
         */
        public function isCssOrJsPage() {
-               $retval = $this->mNamespace == NS_MEDIAWIKI
-                       && preg_match( '!\.(?:css|js)$!u', $this->mTextform ) > 0;
-               wfRunHooks( 'TitleIsCssOrJsPage', array( $this, &$retval ) );
-               return $retval;
+               return $this->hasContentModel( CONTENT_MODEL_CSS )
+            || $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT );
        }
 
        /**
@@ -960,7 +985,8 @@ class Title {
         * @return Bool
         */
        public function isCssJsSubpage() {
-               return ( NS_USER == $this->mNamespace and preg_match( "/\\/.*\\.(?:css|js)$/", $this->mTextform ) );
+               return ( NS_USER == $this->mNamespace && $this->isSubpage()
+            && $this->isCssOrJsPage() );
        }
 
        /**
@@ -983,7 +1009,8 @@ class Title {
         * @return Bool
         */
        public function isCssSubpage() {
-               return ( NS_USER == $this->mNamespace && preg_match( "/\\/.*\\.css$/", $this->mTextform ) );
+        return ( NS_USER == $this->mNamespace && $this->isSubpage()
+            && $this->hasContentModel( CONTENT_MODEL_CSS ) );
        }
 
        /**
@@ -992,7 +1019,8 @@ class Title {
         * @return Bool
         */
        public function isJsSubpage() {
-               return ( NS_USER == $this->mNamespace && preg_match( "/\\/.*\\.js$/", $this->mTextform ) );
+        return ( NS_USER == $this->mNamespace && $this->isSubpage()
+            && $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT ) );
        }
 
        /**
index 468538f..a0709b8 100644 (file)
@@ -800,7 +800,7 @@ class WikiPage extends Page {
                        && $parserOptions->getStubThreshold() == 0
                        && $this->mTitle->exists()
                        && ( $oldid === null || $oldid === 0 || $oldid === $this->getLatest() )
-                       && $this->mTitle->isWikitextPage();
+                       && $this->mTitle->isWikitextPage(); #FIXME: ask ContentHandler if cachable!
        }
 
        /**
index 2877dcb..beeeb30 100644 (file)
@@ -30,7 +30,7 @@ class CacheTime {
         */
        function setCacheTime( $t )          { return wfSetVar( $this->mCacheTime, $t ); }
 
-       /**
+       /**abstract
         * Sets the number of seconds after which this object should expire.
         * This value is used with the ParserCache.
         * If called with a value greater than the value provided at any previous call,