Sebschlicht2 has submitted this change and it was merged. ( 
https://gerrit.wikimedia.org/r/340732 )

Change subject: resource models added
......................................................................


resource models added

ContentModels for resource files have been added, to allow resource files
(e.g. scripts, quizzes) to be placed in the MOOC's course namespace.
Three hooks have been implemented to limit the users view to the resource
file content while editing - but preview and save this content wrapped
inside a MoocResource behind the scenes.

Change-Id: Icd2ef6b6325e0c060eeb82be969c10c66bbc57d7
---
M MOOC.hooks.php
M extension.json
M includes/content/MoocContent.php
A includes/model/MoocEntity.php
M includes/model/MoocItem.php
M includes/model/MoocLesson.php
A includes/model/MoocQuiz.php
A includes/model/MoocResource.php
A includes/model/MoocScript.php
M includes/model/MoocUnit.php
M includes/rendering/MoocContentRenderer.php
M includes/structure/MoocContentStructureProvider.php
12 files changed, 315 insertions(+), 99 deletions(-)



diff --git a/MOOC.hooks.php b/MOOC.hooks.php
index 2459659..8aa1e00 100644
--- a/MOOC.hooks.php
+++ b/MOOC.hooks.php
@@ -6,15 +6,99 @@
  * @file
  * @ingroup Extensions
  *
- * @author Rene Pickhardt ([User:renepick]), Sebastian Schlicht 
(sebast...@jablab.de [User:sebschlicht])
+ * @author Sebastian Schlicht (sebast...@jablab.de [User:sebschlicht]), Rene 
Pickhardt ([User:renepick])
  * @license GPLv2
  */
 class MOOCHooks {
 
     /**
-     * Registers parser functions for magic keywords.
+     * Transforms a MOOC resource into Wikitext to allow users to edit its 
content.
      *
-     * @param Parser $parser            
+     * @param EditPage $editPage edit page
      */
-    public static function onParserFirstCallInit(Parser &$parser) {}
+    public static function onEditFormInitialText( $editPage ) {
+        if ( $editPage->contentModel === MoocContent::CONTENT_MODEL_MOOC_ITEM 
) {
+            $pageContent = 
$editPage->getArticle()->getPage()->getContent()->serialize();
+            $moocContent = new MoocContent( $pageContent );
+            if ( $moocContent->isValid() && ( $moocContent->entity instanceof 
MoocResource ) ) {
+                $entity = $moocContent->entity;
+                if ( $entity !== null ) {
+                    $editPage->textbox1 = $entity->content;
+                }
+            }
+        }
+    }
+
+    /**
+     * Transforms the resource file content into a MOOC resource when 
previewing an edit.
+     * Behind the scenes, the original MOOC resource is loaded and its content 
field is overwritten.
+     *
+     * @param EditPage $editPage edit page
+     * @param Content $content previewed content
+     */
+    public static function onEditPageGetPreviewContent( $editPage, &$content ) 
{
+        if ( $editPage->contentModel === MoocContent::CONTENT_MODEL_MOOC_ITEM 
) {
+            if ( $editPage->getArticle()->getPage()->exists() ) {
+                // existing page: inject edit-content into MOOC resource
+                $pageContent = 
$editPage->getArticle()->getPage()->getContent()->serialize();
+                $moocContent = new MoocContent( $pageContent );
+                $newMoocContent = self::mergeResourceFileIntoMoocContent( 
$moocContent, $content );
+                if ( $newMoocContent === null ) {
+                    // invalid MOOC entity or not a MoocResource
+                } else {
+                    $content = $newMoocContent;
+                }
+            } else {
+                // new page: TODO unclear whether MoocItem or MoocResource
+                // maybe decide depending on JSON validity but invalid 
MoocItem == MoocResource
+                // so this is a modelling choice
+            }
+        }
+    }
+
+    /**
+     * Transforms the resource file content into a MOOC resource when the user 
finishes editing.
+     * Behind the scenes, the original MOOC resource is loaded and its content 
field is overwritten.
+     *
+     * @param WikiPage $wikiPage wiki page being saved
+     * @param User $user user saving the article
+     * @param Content $content new article content
+     * @param string $summary edit summary
+     * @param bool $isMinor minor flag
+     * @param bool $isWatch <i>null</i>
+     * @param bool $section <i>null</i>
+     * @param $flags
+     * @param Status $status edit status
+     */
+    public static function onPageContentSave( &$wikiPage, &$user, &$content, 
&$summary,
+                                              $isMinor, $isWatch, $section, 
&$flags, &$status ) {
+        // limit hook to saves (not creates) of MOOC entities
+        if ( $wikiPage->getContentModel() === 
MoocContent::CONTENT_MODEL_MOOC_ITEM && $wikiPage->exists() ) {
+            $pageContent = $wikiPage->getContent()->serialize();
+            $moocContent = new MoocContent( $pageContent );
+            $newMoocContent = self::mergeResourceFileIntoMoocContent( 
$moocContent, $content );
+            if ( $newMoocContent === null ) {
+                // invalid MOOC entity or not a MoocResource
+            } else {
+                $content = $newMoocContent;
+            }
+        }
+    }
+
+    /**
+     * Creates a new MOOC content by injecting the given resource file content 
into an existing MOOC content.
+     *
+     * @param MoocContent $moocResourceContent current MOOC content (from 
existing page)
+     * @param Content $resourceFileContent new resource file content (edit 
text)
+     * @return MoocContent|null MOOC content with given resource file content 
or <i>null</i> on error
+     */
+    private static function 
mergeResourceFileIntoMoocContent($moocResourceContent, $resourceFileContent ) {
+        if ( $moocResourceContent->isValid() && $moocResourceContent->entity 
instanceof MoocResource ) {
+            // inject resource file content into valid MOOC resource
+            $moocResourceContent->entity->content = 
$resourceFileContent->getNativeData();
+            // create new MOOC content from resource entity
+            return new MoocContent( json_encode( 
$moocResourceContent->entity->toJson() ) );
+        }
+        return null;
+    }
 }
diff --git a/extension.json b/extension.json
index 5cf9d98..15d3cac 100644
--- a/extension.json
+++ b/extension.json
@@ -13,6 +13,10 @@
     "MOOCHooks": "MOOC.hooks.php",
     "MoocContent": "includes/content/MoocContent.php",
     "MoocContentHandler": "includes/content/MoocContentHandler.php",
+    "MoocEntity": "includes/model/MoocEntity.php",
+    "MoocResource": "includes/model/MoocResource.php",
+    "MoocScript": "includes/model/MoocScript.php",
+    "MoocQuiz": "includes/model/MoocQuiz.php",
     "MoocItem": "includes/model/MoocItem.php",
     "MoocLesson": "includes/model/MoocLesson.php",
     "MoocUnit": "includes/model/MoocUnit.php",
@@ -35,8 +39,14 @@
     "MOOCNamespaces": "MOOC.namespaces.php"
   },
   "Hooks": {
-    "ParserFirstCallInit": [
-      "MOOCHooks::onParserFirstCallInit"
+    "EditFormInitialText": [
+      "MOOCHooks::onEditFormInitialText"
+    ],
+    "EditPageGetPreviewContent": [
+      "MOOCHooks::onEditPageGetPreviewContent"
+    ],
+    "PageContentSave": [
+      "MOOCHooks::onPageContentSave"
     ]
   },
   "namespaces": [
@@ -45,7 +55,8 @@
       "constant": "NS_MOOC",
       "name": "Mooc",
       "subpages": true,
-      "content": true
+      "content": true,
+      "defaultcontentmodel": "mooc-item"
     },
     {
       "id": 351,
diff --git a/includes/content/MoocContent.php b/includes/content/MoocContent.php
index ce2eeed..fc5d17f 100644
--- a/includes/content/MoocContent.php
+++ b/includes/content/MoocContent.php
@@ -16,12 +16,12 @@
     const CONTENT_MODEL_MOOC_ITEM = 'mooc-item';
 
     /**
-     * @var MoocItem MOOC item being loaded
+     * @var MoocItem|MoocResource MOOC entity being loaded
      */
-    public $item;
+    public $entity;
 
     /**
-     * @param string $text MOOC item JSON
+     * @param string $text MOOC entity JSON
      * @param string $modelId identifier of the page's content model
      */
     public function __construct($text, $modelId = 
self::CONTENT_MODEL_MOOC_ITEM) {
@@ -29,26 +29,28 @@
     }
 
     /**
-     * Decodes the page content JSON into a MOOC item.
+     * Decodes the page content JSON into a MOOC entity.
      *
-     * @return MoocItem MOOC item loaded from the content
+     * @return MoocEntity MOOC entity loaded from the content
      */
     public function loadItem() {
-        //return MoocItem::loadItemFromJson(null, 
parent::getData()->getValue());
-        return MoocItem::loadItemFromJson(null, parent::getJsonData());
+        return MoocEntity::loadFromJson( null, (array) 
parent::getData()->getValue() );
     }
 
     /**
-     * @return bool whether content is valid
+     * Checks whether the page content is valid JSON and a valid MOOC entity.
+     * If the page content is valid, the MOOC entity will be loaded into the 
<i>entity</i> field.
+     *
+     * @return bool whether the content is valid or not
      */
     public function isValid() {
         // check for valid JSON
         if (parent::isValid()) {
-            // load MOOC item if not loaded yet
-            if (!isset($this->item)) {
-                $this->item = $this->loadItem();
+            // load MOOC entity if not loaded yet
+            if (!isset($this->entity)) {
+                $this->entity = $this->loadItem();
             }
-            return true;
+            return ( $this->entity != null );
         }
         return false;
     }
@@ -66,28 +68,36 @@
         ParserOutput &$output) {
         // FIXME: WikiPage::doEditContent generates parser output before 
validation.
         // As such, native data may be invalid (though output is discarded 
later in that case).
-        if ($generateHtml && $this->isValid()) {
-            $this->item->setTitle($title);
+        if ( $generateHtml && $this->isValid() ) {
+            $output->setTOCEnabled( false );
 
-            $output->setEnableOOUI(true);
-            $output->setTOCEnabled(false);
-            $output->setText(MoocContentRenderer::renderItem($output, 
$this->item));
+            if ( $this->entity instanceof MoocResource ) {
+                // MOOC resource: render content as Wikitext
+                $tmpOut = new OutputPage();
+                $tmpOut->addWikiText( $this->entity->content );
+                $output->setText( $tmpOut->getHTML() );
+            } else {
+                // MOOC item: let content renderer decide
+                $this->entity->setTitle( $title );
+                $output->setEnableOOUI( true );
+                $output->addModuleScripts( 'ext.mooc' );
+                $output->addModuleStyles( 'ext.mooc' );
 
-            $output->addModuleScripts('ext.mooc');
-            $output->addModuleStyles('ext.mooc');
+                $output->setText( MoocContentRenderer::renderItem( $output, 
$this->entity ) );
 
-            // pass data to JS
-            $output->addJsConfigVars([
-                'moocAgentData' => [
-                    'userAgentName' => 'MoocBot',
-                    'userAgentUrl' => 
'https://en.wikiversity.org/wiki/User:Sebschlicht',
-                    'userAgentMailAddress' => 'sebschli...@uni-koblenz.de',
-                    'version' => '0.1'
-                ],
-                'moocItem' => $this->item->toJson()
-            ]);
+                // pass data to JS
+                $output->addJsConfigVars( [
+                    'moocAgentData' => [
+                        'userAgentName' => 'MoocBot',
+                        'userAgentUrl' => 
'https://en.wikiversity.org/wiki/User:Sebschlicht',
+                        'userAgentMailAddress' => 'sebschli...@uni-koblenz.de',
+                        'version' => '0.1'
+                    ],
+                    'moocItem' => $this->entity->toJson()
+                ] );
+            }
         } else {
-            $output->setText('');
+            $output->setText( '' );
         }
     }
 }
diff --git a/includes/model/MoocEntity.php b/includes/model/MoocEntity.php
new file mode 100644
index 0000000..d33bf88
--- /dev/null
+++ b/includes/model/MoocEntity.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * Basic class for all pages/entities related to a MOOC.
+ * Known entity types are:
+ * <ol>
+ * <li>MOOC item (unit, lesson, MOOC)</li>
+ * <li>MOOC resource (script, quiz)</li>
+ * </ol>
+ */
+abstract class MoocEntity {
+
+    /**
+     * JSON field identifier for the MOOC entity type
+     */
+    const JFIELD_TYPE = 'type';
+
+    /**
+     * @var Title page title
+     */
+    public $title;
+
+    /**
+     * @var string type of the MOOC entity
+     */
+    public $type;
+
+    /**
+     * Creates a new MOOC entity from JSON.
+     *
+     * @param Title $title page title
+     * @param array $moocContentJson JSON (associative array) representing a 
MOOC entity
+     */
+    public function __construct( $title, $moocContentJson ) {
+        // TODO completely separate page Title from MOOC entities?
+        $this->title = $title;
+        $this->type = $moocContentJson[self::JFIELD_TYPE];
+    }
+
+    /**
+     * Converts this MOOC entity into JSON content.
+     *
+     * @return array JSON (associative array) representing a MOOC entity
+     */
+    abstract function toJson();
+
+    /**
+     * Loads a MOOC entity from JSON content.
+     *
+     * @param Title $title title of the MOOC entity page
+     * @param array $moocContentJson JSON (associative array) representing a 
MOOC entity
+     * @return MoocEntity MOOC entity instance or null on error
+     */
+    public static function loadFromJson( $title, $moocContentJson ) {
+        if ( !array_key_exists( self::JFIELD_TYPE, $moocContentJson ) ) {
+            return null;
+        }
+
+        $type = $moocContentJson[self::JFIELD_TYPE];
+        switch ( $type ) {
+            case MoocUnit::ENTITY_TYPE_UNIT:
+                return new MoocUnit( $title, $moocContentJson );
+
+            case MoocLesson::ENTITY_TYPE_LESSON:
+                return new MoocLesson( $title, $moocContentJson );
+
+            case MoocScript::ENTITY_TYPE_SCRIPT:
+                return new MoocScript( $title, $moocContentJson );
+
+            case MoocQuiz::ENTITY_TYPE_QUIZ:
+                return new MoocQuiz( $title, $moocContentJson );
+
+            // unknown MOOC entity type
+            default:
+                return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/includes/model/MoocItem.php b/includes/model/MoocItem.php
index 91265a5..3f5861e 100644
--- a/includes/model/MoocItem.php
+++ b/includes/model/MoocItem.php
@@ -7,12 +7,7 @@
  *
  * @file
  */
-abstract class MoocItem {
-
-    /**
-     * JSON field identifier for the MOOC item type
-     */
-    const JFIELD_TYPE = 'type';
+abstract class MoocItem extends MoocEntity {
 
     /**
      * JSON field identifier for learning goals
@@ -47,16 +42,6 @@
      * @var MoocItem base item of the MOOC this item belongs to
      */
     public $baseItem;
-
-    /**
-     * @var Title page title
-     */
-    public $title;
-
-    /**
-     * @var string type of the MOOC item
-     */
-    public $type;
 
     /**
      * @var string[] learning goals that should be fulfilled at the end of 
this item
@@ -96,17 +81,17 @@
      */
     public function __construct( $title, $moocContentJson ) {
         // TODO completely separate Title from MoocItem?
-        $this->setTitle($title);
+        parent::__construct( $title, $moocContentJson );
+        $this->setTitle( $title );
 
         // common MOOC item fields
-        $this->type = $moocContentJson[self::JFIELD_TYPE];
-        if (array_key_exists(self::JFIELD_VIDEO, $moocContentJson)) {
+        if ( array_key_exists( self::JFIELD_VIDEO, $moocContentJson ) ) {
             $this->video = $moocContentJson[self::JFIELD_VIDEO];
         }
-        if (array_key_exists(self::JFIELD_LEARNING_GOALS, $moocContentJson)) {
+        if ( array_key_exists( self::JFIELD_LEARNING_GOALS, $moocContentJson ) 
) {
             $this->learningGoals = 
$moocContentJson[self::JFIELD_LEARNING_GOALS];
         }
-        if (array_key_exists(self::JFIELD_FURTHER_READING, $moocContentJson)) {
+        if ( array_key_exists( self::JFIELD_FURTHER_READING, $moocContentJson 
) ) {
             $this->furtherReading = 
$moocContentJson[self::JFIELD_FURTHER_READING];
         }
     }
@@ -114,7 +99,7 @@
     /**
      * @param $title Title page title
      */
-    public function setTitle($title) {
+    public function setTitle( $title ) {
         $this->title = $title;
         if ( $title != null ) {
             $this->scriptTitle = Title::newFromText( $title . '/script' );
@@ -136,37 +121,6 @@
         return isset( $this->children ) && !empty( $this->children );
     }
 
-    /**
-     * Loads a MOOC item from JSON content.
-     *
-     * @param Title $title title of the MOOC item page
-     * @param array $moocContentJson JSON (associative array) representing a 
MOOC item
-     * @return MoocItem MOOC item instance or null on error
-     */
-    public static function loadItemFromJson( $title, $moocContentJson ) {
-        if ( !array_key_exists( self::JFIELD_TYPE, $moocContentJson ) ) {
-            return null;
-        }
-
-        $type = $moocContentJson[self::JFIELD_TYPE];
-        switch ( $type ) {
-            case MoocUnit::ITEM_TYPE_UNIT:
-                return new MoocUnit( $title, $moocContentJson );
-
-            case MoocLesson::ITEM_TYPE_LESSON:
-                return new MoocLesson( $title, $moocContentJson );
-
-            // unknown MOOC item type
-            default:
-                return null;
-        }
-    }
-
-    /**
-     * Converts this MOOC item into JSON content.
-     *
-     * @return array JSON (associative array) representing a MOOC item
-     */
     public function toJson() {
         return [
             self::JFIELD_TYPE => $this->type,
diff --git a/includes/model/MoocLesson.php b/includes/model/MoocLesson.php
index edbffbf..e3ede7b 100644
--- a/includes/model/MoocLesson.php
+++ b/includes/model/MoocLesson.php
@@ -10,16 +10,18 @@
 class MoocLesson extends MoocItem {
 
     /**
-     * MOOC item type for lessons
+     * MOOC entity type for lessons
      */
-    const ITEM_TYPE_LESSON = 'lesson';
+    const ENTITY_TYPE_LESSON = 'lesson';
 
     /**
      * @param Title $title page title
-     * @param mixed $moocContentJson JSON (associative array) representing a 
MOOC item
+     * @param mixed $moocContentJson JSON (associative array) representing a 
MOOC lesson
      */
     public function __construct($title, $moocContentJson) {
         parent::__construct($title, $moocContentJson);
+
+        // child units
         if (array_key_exists(self::JFIELD_CHILDREN, $moocContentJson)) {
             $this->childNames = $moocContentJson[self::JFIELD_CHILDREN];
         }
diff --git a/includes/model/MoocQuiz.php b/includes/model/MoocQuiz.php
new file mode 100644
index 0000000..cdf1afc
--- /dev/null
+++ b/includes/model/MoocQuiz.php
@@ -0,0 +1,16 @@
+<?php
+
+/**
+ * Quiz resource used in a MOOC. The content itself is stored in Wikitext 
format.
+ *
+ * @author Sebastian Schlicht <sebast...@jablab.de>
+ *
+ * @file
+ */
+class MoocQuiz extends MoocResource {
+
+    /**
+     * MOOC entity type for quizzes
+     */
+    const ENTITY_TYPE_QUIZ = 'quiz';
+}
\ No newline at end of file
diff --git a/includes/model/MoocResource.php b/includes/model/MoocResource.php
new file mode 100644
index 0000000..bbb1f2e
--- /dev/null
+++ b/includes/model/MoocResource.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * Basic model for a resource used in a MOOC. This model is necessary to allow 
resource files to be saved within the
+ * MOOC namespace without content model changes.
+ * This is achieved by storing the resource file content within the content 
field of this entity.
+ *
+ * @author Sebastian Schlicht <sebast...@jablab.de>
+ *
+ * @file
+ */
+abstract class MoocResource extends MoocEntity {
+
+    /**
+     * JSON field identifier for the resource file content
+     */
+    const JFIELD_CONTENT = 'content';
+
+    /**
+     * @var string resource file content
+     */
+    public $content;
+
+    /**
+     * @param Title $title resource page title
+     * @param mixed $moocContentJson JSON (associative array) representing a 
MOOC resource
+     */
+    public function __construct( $title, $moocContentJson ) {
+        parent::__construct( $title, $moocContentJson );
+
+        // resource file content - if any
+        if ( array_key_exists( self::JFIELD_CONTENT, $moocContentJson ) ) {
+            $this->content = $moocContentJson[self::JFIELD_CONTENT];
+        }
+    }
+
+    public function toJson() {
+        return [
+            self::JFIELD_TYPE => $this->type,
+            self::JFIELD_CONTENT => $this->content
+        ];
+    }
+}
\ No newline at end of file
diff --git a/includes/model/MoocScript.php b/includes/model/MoocScript.php
new file mode 100644
index 0000000..c62e592
--- /dev/null
+++ b/includes/model/MoocScript.php
@@ -0,0 +1,16 @@
+<?php
+
+/**
+ * Script resource used in a MOOC. The content itself is stored in Wikitext 
format.
+ *
+ * @author Sebastian Schlicht <sebast...@jablab.de>
+ *
+ * @file
+ */
+class MoocScript extends MoocResource {
+
+    /**
+     * MOOC entity type for scripts
+     */
+    const ENTITY_TYPE_SCRIPT = 'script';
+}
\ No newline at end of file
diff --git a/includes/model/MoocUnit.php b/includes/model/MoocUnit.php
index e1de390..fc64524 100644
--- a/includes/model/MoocUnit.php
+++ b/includes/model/MoocUnit.php
@@ -10,7 +10,7 @@
 class MoocUnit extends MoocItem {
 
     /**
-     * MOOC item type for units
+     * MOOC entity type for units
      */
-    const ITEM_TYPE_UNIT = 'unit';
+    const ENTITY_TYPE_UNIT = 'unit';
 }
diff --git a/includes/rendering/MoocContentRenderer.php 
b/includes/rendering/MoocContentRenderer.php
index 14a8366..51a052a 100644
--- a/includes/rendering/MoocContentRenderer.php
+++ b/includes/rendering/MoocContentRenderer.php
@@ -69,10 +69,10 @@
     protected static function getRenderer($type) {
         // TODO use some registration process for flexibility
         switch($type) {
-            case MoocUnit::ITEM_TYPE_UNIT:
+            case MoocUnit::ENTITY_TYPE_UNIT:
                 return new MoocUnitRenderer();
 
-            case MoocLesson::ITEM_TYPE_LESSON:
+            case MoocLesson::ENTITY_TYPE_LESSON:
                 return new MoocLessonRenderer();
 
             default:
diff --git a/includes/structure/MoocContentStructureProvider.php 
b/includes/structure/MoocContentStructureProvider.php
index 6be5311..d1cefd6 100644
--- a/includes/structure/MoocContentStructureProvider.php
+++ b/includes/structure/MoocContentStructureProvider.php
@@ -53,8 +53,10 @@
             $contentModel = new MoocContent( $row->old_text );
             if ( $contentModel->isValid() ) {
                 $item = $contentModel->loadItem();
-                $item->setTitle( Title::newFromText( $row->page_title, 
$namespace ) );
-                array_push( $items, $item );
+                if ( $item instanceof MoocItem ) {
+                    $item->setTitle( Title::newFromText( $row->page_title, 
$namespace ) );
+                    array_push( $items, $item );
+                }
             }
         }
 

-- 
To view, visit https://gerrit.wikimedia.org/r/340732
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Icd2ef6b6325e0c060eeb82be969c10c66bbc57d7
Gerrit-PatchSet: 2
Gerrit-Project: mediawiki/extensions/MOOC
Gerrit-Branch: master
Gerrit-Owner: Sebschlicht2 <sebschli...@uni-koblenz.de>
Gerrit-Reviewer: Sebschlicht2 <sebschli...@uni-koblenz.de>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to