Harej has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/317115

Change subject: [WIP] Creating basic editing interface for 
CollaborationHubContent.
......................................................................

[WIP] Creating basic editing interface for CollaborationHubContent.

Change-Id: Ia2d3a18272bf20d691e0038daabe4c6d44ef802f
---
M CollaborationKit.hooks.php
M extension.json
M i18n/en.json
M i18n/qqq.json
A includes/CollaborationHubContentEditor.php
M includes/content/CollaborationHubContent.php
M includes/content/CollaborationHubContentHandler.php
7 files changed, 447 insertions(+), 25 deletions(-)


  git pull 
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/CollaborationKit 
refs/changes/15/317115/1

diff --git a/CollaborationKit.hooks.php b/CollaborationKit.hooks.php
index e2d439e..fc28f01 100644
--- a/CollaborationKit.hooks.php
+++ b/CollaborationKit.hooks.php
@@ -13,7 +13,7 @@
                $title = $sktemplate->getTitle();
                $request = $sktemplate->getRequest();
                if ( isset( $links['views']['edit'] ) ) {
-                       if ( $title->hasContentModel( 
'CollaborationListContent' ) ) {
+                       if ( $title->hasContentModel( 
'CollaborationListContent' ) || $title->hasContentModel( 
'CollaborationHubContent' ) ) {
                                // Edit as JSON
                                $active = in_array( $request->getVal( 'action' 
), [ 'edit', 'submit' ] )
                                        && $request->getVal( 'format' ) === 
'application/json';
@@ -29,7 +29,8 @@
                                        // Make it not be selected when editing 
json.
                                        $links['views']['edit']['class'] = 
false;
                                }
-                       } elseif ( $title->hasContentModel( 
'CollaborationHubContent' ) ) {
+                       }
+                       if ( $title->hasContentModel( 'CollaborationHubContent' 
) ) {
                                // Add feature
                                $links['actions']['addnewfeature'] = [
                                        'class' => '',
diff --git a/extension.json b/extension.json
index 335ff7e..ebc1cac 100644
--- a/extension.json
+++ b/extension.json
@@ -20,13 +20,14 @@
                "CollaborationKitHooks": "CollaborationKit.hooks.php",
                "CollaborationHubContent": 
"includes/content/CollaborationHubContent.php",
                "CollaborationHubContentHandler": 
"includes/content/CollaborationHubContentHandler.php",
+               "CollaborationHubContentEditor": 
"includes/CollaborationHubContentEditor.php",
                "CollaborationHubTOC": 
"includes/content/CollaborationHubTOC.php",
                "CollaborationKitIcon": 
"includes/content/CollaborationKitIcon.php",
                "CollaborationListContent": 
"includes/content/CollaborationListContent.php",
                "CollaborationListContentHandler": 
"includes/content/CollaborationListContentHandler.php",
+               "CollaborationListContentEditor": 
"includes/CollaborationListContentEditor.php",
                "SpecialCreateCollaborationHub": 
"includes/SpecialCreateCollaborationHub.php",
                "SpecialCreateHubFeature": 
"includes/SpecialCreateHubFeature.php",
-               "CollaborationListContentEditor": 
"includes/CollaborationListContentEditor.php",
                "ResourceLoaderListStyleModule": 
"includes/ResourceLoaderListStyleModule.php"
        },
        "ContentHandlers": {
@@ -63,7 +64,8 @@
                        "CollaborationListContent::onArticleViewHeader"
                ],
                "CustomEditor": [
-                       "CollaborationListContent::onCustomEditor"
+                       "CollaborationListContent::onCustomEditor",
+                       "CollaborationHubContent::onCustomEditor"
                ],
                "OutputPageBeforeHTML": [
                        "CollaborationKitHooks::onOutputPageBeforeHTML"
diff --git a/i18n/en.json b/i18n/en.json
index 806b387..2e9f509 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -61,6 +61,12 @@
        "collaborationkit-list-newitem-description": "Description (optional)",
        "collaborationkit-listedit-description": "Description",
        "collaborationkit-listedit-list": "List items",
+       "collaborationkit-hubedit-displayname": "Name to display",
+       "collaborationkit-hubedit-introduction": "Introduction",
+       "collaborationkit-hubedit-footer": "Footer",
+       "collaborationkit-hubedit-image": "Hub image",
+       "collaborationkit-hubedit-colour": "Color",
+       "collaborationkit-hubedit-content": "Features",
        "collaborationkit-editjsontab": "Edit as JSON",
        "collaborationkit-hub-announcements-initial": "A new collaboration hub 
has been set up!",
        "collaborationkit-hub-members-description": "Our members are below. 
Those who have not edited in over a month are moved to the inactive section.",
diff --git a/i18n/qqq.json b/i18n/qqq.json
index 5fb5b83..815b4a5 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -61,6 +61,12 @@
        "collaborationkit-list-newitem-description": "Label for description 
field on new item dialog for CollaborationKit lists",
        "collaborationkit-listedit-description": "Header on edit page for the 
list description text box",
        "collaborationkit-listedit-list": "Header on edit page for the main 
body of the list",
+       "collaborationkit-hubedit-displayname": "Field label for the display 
name field on the edit page for hubs",
+       "collaborationkit-hubedit-introduction": "Field label for the 
introduction text on the edit page for hubs",
+       "collaborationkit-hubedit-footer": "Field label for the footer text on 
the edit page for hubs",
+       "collaborationkit-hubedit-image": "Field label for the image field on 
the edit page for hubs",
+       "collaborationkit-hubedit-colour": "Field label for the color field on 
the edit page for hubs",
+       "collaborationkit-hubedit-content": "Field label for the features field 
on the edit page for hubs",
        "collaborationkit-editjsontab": "Text of the tab used to edit the page 
as JSON.",
        "collaborationkit-hub-announcements-initial": "The initial announcement 
posted when a Collaboration Hub is created.",
        "collaborationkit-hub-members-description": "The default description 
for the member list generated on a new Collaboration Hub.",
diff --git a/includes/CollaborationHubContentEditor.php 
b/includes/CollaborationHubContentEditor.php
new file mode 100644
index 0000000..4d35d42
--- /dev/null
+++ b/includes/CollaborationHubContentEditor.php
@@ -0,0 +1,210 @@
+<?php
+/**
+ * @todo Unicode unsafe browsers?
+ */
+class CollaborationHubContentEditor extends EditPage {
+
+       function __construct( $page ) {
+               parent::__construct( $page );
+               // Make human readable the default format for editing, but still
+               // save as json. Can be overriden by url 
?format=application/json param.
+               $this->contentFormat = 
CollaborationHubContentHandler::FORMAT_WIKI;
+       }
+
+       /**
+        * Build and return the aossociative array for the content source field.
+        * @param $mapping array
+        * @return array
+        */
+       protected function getOptions( $mapping ) {
+               $options = [];
+               foreach ( $mapping as $msgKey => $option ) {
+                       $options[] = [ 'label' => wfMessage( $msgKey 
)->escaped(), 'data' => $option ];
+               }
+               return $options;
+       }
+
+       /**
+        * @param $parts array
+        * @return array
+        */
+       protected function getFormFields( $parts ) {
+
+               $fields = [
+                       // Display name can be different from page title
+                       'display_name' => new OOUI\FieldLayout(
+                               new OOUI\TextInputWidget( [
+                                       'name' => 'wpCollabHubDisplayName',
+                                       'id' => 'wpCollabHubDisplayName',
+                                       'type' => 'text',
+                                       'class' => 'mw-ck-displayinput',
+                                       'value' => $parts[0]
+                                       ] ),
+                               [
+                                       'label' => wfMessage( 
'collaborationkit-hubedit-displayname' )->text(),
+                                       'align' => 'top'
+                               ] ),
+                       'introduction' => new OOUI\FieldLayout(
+                               new OOUI\TextInputWidget( [
+                                       'multiline' => true,
+                                       'name' => 'wpCollabHubIntroduction',
+                                       'id' => 'wpCollabHubIntroduction',
+                                       'type' => 'textarea',
+                                       'rows' => 5,
+                                       'class' => 'mw-ck-introductioninput',
+                                       'value' => $parts[1]
+                                       ] ),
+                               [
+                                       'label' => wfMessage( 
'collaborationkit-hubedit-introduction' )->text(),
+                                       'align' => 'top'
+                               ] ),
+                       'footer' => new OOUI\FieldLayout(
+                               new OOUI\TextInputWidget( [
+                                       'multiline' => true,
+                                       'name' => 'wpCollabHubFooter',
+                                       'id' => 'wpCollabHubFooter',
+                                       'type' => 'textarea',
+                                       'rows' => 5,
+                                       'class' => 'mw-ck-introductioninput',
+                                       'value' => $parts[2]
+                                       ] ),
+                               [
+                                       'label' => wfMessage( 
'collaborationkit-hubedit-footer' )->text(),
+                                       'align' => 'top'
+                               ] ),
+                       // Hub image/icon thing
+                       'image' => new OOUI\FieldLayout(
+                               new OOUI\TextInputWidget( [
+                                       'name' => 'wpCollabHubImage',
+                                       'id' => 'wpCollabHubImage',
+                                       'type' => 'text',
+                                       'class' => 'mw-ck-iconinput',
+                                       'value' => $parts[3]
+                                       ] ),
+                               [
+                                       'label' => wfMessage( 
'collaborationkit-hubedit-image' )->text(),
+                                       'align' => 'top'
+                               ] ),
+               ];
+               // Colours for the hub styles
+               $colours = [];
+               foreach ( CollaborationHubContent::getThemeColours() as $colour 
) {
+                       $colours[ 'collaborationkit-' . $colour ] = $colour;
+               }
+
+               if ( $parts[4] == '' ) {
+                       $selectedColour = 'blue5';
+               } else {
+                       $selectedColour = $parts[4];
+               }
+
+               $fields['colour'] = new OOUI\FieldLayout(
+                       new OOUI\DropdownInputWidget( [
+                               'name' => 'wpCollabHubColour',
+                               'id' => 'wpCollabHubColour',
+                               'type' => 'select',
+                               'options' => $this->getOptions( $colours ),
+                               'class' => 'mw-ck-colourinput',
+                               'value' => $selectedColour
+                               ] ),
+                       [
+                               'label' => wfMessage( 
'collaborationkit-hubedit-colour' )->text(),
+                               'align' => 'top'
+                       ] );
+
+               if ( $parts[5] == '' ) {
+                       $includedContent = '';
+               } else {
+                       $includedContent = $parts[5];
+               }
+
+               $fields['content'] = new OOUI\FieldLayout(
+                       new OOUI\TextInputWidget( [
+                               'multiline' => true,
+                               'name' => 'wpCollabHubContent',
+                               'id' => 'wpCollabHubContent',
+                               'type' => 'textarea',
+                               'rows' => 10,
+                               'class' => 'mw-ck-introductioninput',
+                               'value' => $includedContent
+                               ] ),
+                       [
+                               'label' => wfMessage( 
'collaborationkit-hubedit-content' )->text(),
+                               'align' => 'top'
+                       ] );
+
+               return $fields;
+       }
+
+       /**
+        * Renders and adds the editing form to the parser output.
+        */
+       protected function showContentForm() {
+               if ( $this->contentFormat !== 
CollaborationHubContentHandler::FORMAT_WIKI ) {
+                       return parent::showContentForm();
+               }
+
+               $parts = explode( CollaborationHubContent::HUMAN_DESC_SPLIT, 
$this->textbox1, 6 );
+               if ( count( $parts ) !== 6 ) {
+                       return parent::showContentForm();
+               }
+
+               $out = RequestContext::getMain()->getOutput();
+               $pageLang = $this->getTitle()->getPageLanguage();
+
+               $formFields = $this->getFormFields( $parts );
+
+               $htmlForm = new OOUI\FieldsetLayout( [ 'items' => $formFields ] 
);
+               $out->enableOOUI();
+               $out->addHtml( $htmlForm );
+       }
+
+       /**
+        * Required as a callback by the parent class, but not used as
+        * we have validation logic elsewhere that works just fine.
+        * @param $formData
+        */
+       static function trySubmit( $formData ) {
+               return true;
+       }
+
+       /**
+        * Converts input from the editing form into the text/x-collabkit
+        * serialization used for processing the edit.
+        * @param &$request WebRequest
+        * @return string|null
+        */
+       protected function importContentFormData( &$request ) {
+               $format = $request->getVal( 'format', 
CollaborationListContentHandler::FORMAT_WIKI );
+               if ( $format !== CollaborationListContentHandler::FORMAT_WIKI ) 
{
+                       return parent::importContentFormData( $request );
+               }
+               $displayname = trim( $request->getText( 
'wpCollabHubDisplayName' ) );
+               if ( $displayname === null ) {
+                       // Only 1 textbox?
+                       return parent::importContentFormData( $request );
+               }
+
+               $introduction = trim( $request->getText( 
'wpCollabHubIntroduction', '' ) );
+               $footer = trim( $request->getText( 'wpCollabHubFooter', '' ) );
+               $image = trim( $request->getText( 'wpCollabHubImage', '' ) );
+               $colour = trim( $request->getText( 'wpCollabHubColour', '' ) );
+               $content = trim( $request->getText( 'wpCollabHubContent', '' ) 
);
+
+               return $displayname
+                       . CollaborationHubContent::HUMAN_DESC_SPLIT
+                       . $introduction
+                       . CollaborationHubContent::HUMAN_DESC_SPLIT
+                       . $footer
+                       . CollaborationHubContent::HUMAN_DESC_SPLIT
+                       . $image
+                       . CollaborationHubContent::HUMAN_DESC_SPLIT
+                       . $colour
+                       . CollaborationHubContent::HUMAN_DESC_SPLIT
+                       . $content;
+       }
+
+       protected function getDisplayFormat() {
+               return 'ooui';
+       }
+}
diff --git a/includes/content/CollaborationHubContent.php 
b/includes/content/CollaborationHubContent.php
index 5197af0..c75beed 100644
--- a/includes/content/CollaborationHubContent.php
+++ b/includes/content/CollaborationHubContent.php
@@ -3,26 +3,12 @@
 /**
  * Structured hub pages!
  *
- * Json structure is as follows:
- * {
- *     "display_name": "Display name for the page/project",
- *     "image": "A file on-wiki or an id that matches one of the canned icons",
- *     "colour": "One of the 23 preset theme colours",
- *     "introduction": "Some arbitrary wikitext to appear at the top",
- *     "content": [
- *             {
- *                     "title": "The title, generally a subpage; we'll force 
this later",
- *                     "image": "Image or icon to use",
- *                     "display_title": "What to show on the page (defaults to 
{{SUBPAGENAME}} otherwise)",
- *                     ...
- *             },
- *             ...
- *     ],
- *     "footer": "Some more arbitrary wikitext to appear at the bottom"
- * }
+ * Json structure is defined in CollaborationHubContentSchema.php.
  *
  */
 class CollaborationHubContent extends JsonContent {
+
+       const HUMAN_DESC_SPLIT = "\n-----------------------\n";
 
        /** @var string */
        protected $displayName;
@@ -732,4 +718,183 @@
                // Nothing was found
                return null;
        }
+
+       /**
+        * Converts content between wikitext and JSON.
+        *
+        * @param $toModel string
+        * @param $lossy string
+        */
+       public function convert( $toModel, $lossy = '' ) {
+               if ( $toModel === CONTENT_MODEL_WIKITEXT && $lossy === 'lossy' 
) {
+                       global $wgContLang;
+                       // using wgContLang is kind of icky. Maybe we should 
transclude
+                       // from mediawiki namespace, or give up on not 
splitting the
+                       // parser cache and just use {{int:... (?)
+                       $renderOpts = $this->getFullRenderListOptions();
+                       $text = $this->convertToWikitext( $wgContLang, 
$renderOpts );
+                       return ContentHandler::makeContent( $text, null, 
$toModel );
+               } elseif ( $toModel === CONTENT_MODEL_JSON ) {
+                       return ContentHandler::makeContent( 
$this->getNativeData(), null, $toModel );
+               }
+               return parent::convert( $toModel, $lossy );
+       }
+
+       /**
+        * Convert JSON to markup that's easier for humans.
+        */
+       public function convertToHumanEditable() {
+               $this->decode();
+
+               $output = $this->displayName;
+               $output .= self::HUMAN_DESC_SPLIT;
+               $output .= $this->introduction;
+               $output .= self::HUMAN_DESC_SPLIT;
+               $output .= $this->footer;
+               $output .= self::HUMAN_DESC_SPLIT;
+               $output .= $this->image;
+               $output .= self::HUMAN_DESC_SPLIT;
+               $output .= $this->themeColour;
+               $output .= self::HUMAN_DESC_SPLIT;
+               $output .= $this->getHumanEditableContent();
+               return $output;
+       }
+
+       /**
+        * Get the list of items in human editable form.
+        *
+        * @todo Should this be i18n-ized?
+        */
+       public function getHumanEditableContent() {
+               $this->decode();
+
+               $out = '';
+               foreach ( $this->content as $item ) {
+                       $out .= $this->escapeForHumanEditable( $item['title'] );
+                       if ( isset ( $item['image'] ) ) {
+                               $out .= "|image=" . 
$this->escapeForHumanEditable( $item['image'] );
+                       }
+                       if ( isset( $item['displayTitle'] ) ) {
+                               $out .= "|display_title=" . 
$this->escapeForHumanEditable( $item['displayTitle'] );
+                       }
+                       if ( substr( $out, -1 ) === '|' ) {
+                               $out = substr( $out, 0, strlen( $out ) - 1 );
+                       }
+                       $out .= "\n";
+               }
+               return $out;
+       }
+
+       /**
+        * Escape characters used as separators in human editable mode.
+        *
+        * @todo Unclear if this is best approach. Alternative might be
+        *  to use &#xA; Or an obscure unicode character like ␊ (U+240A).
+        */
+       private function escapeForHumanEditable( $text ) {
+               if ( strpos( $text, '{{!}}' ) !== false ) {
+                       // Maybe we should use \| too, but that's not MW like.
+                       throw new MWContentSerializationException( "{{!}} in 
content" );
+               }
+               if ( strpos( $text, "\\\n" ) !== false ) {
+                       // @todo We don't currently handle this properly.
+                       throw new MWContentSerializationException( "Line ending 
with a \\" );
+               }
+               $text = strtr( $text, [
+                       "\n" => '\n',
+                       '\n'=> '\\\\n',
+                       '|' => '{{!}}'
+               ] );
+               return $text;
+       }
+
+       /**
+        * Removes escape characters inserted in human editable mode.
+        *
+        * @param $text string
+        * @return string
+        */
+       private static function unescapeForHumanEditable( $text ) {
+               $text = strtr( $text, [
+                       '\\\\n'=> "\\n",
+                       '\n' => "\n",
+                       '{{!}}' => '|'
+               ] );
+               return $text;
+       }
+
+       /**
+        * Convert from human editable form into a (php) array
+        *
+        * @param $text String text to convert
+        * @return Array Result of converting it to native form
+        */
+       public static function convertFromHumanEditable( $text ) {
+               $res = [];
+               $split = explode( self::HUMAN_DESC_SPLIT, $text );
+
+               $res['display_name'] = $split[0];
+               $res['introduction'] = $split[1];
+               $res['footer'] = $split[2];
+               $res['image'] = $split[3];
+               $res['colour'] = $split[4];
+               $content = $split[5];
+               if ( trim( $content ) == '' ) {
+                       $res['content'] = [];
+               } else {
+                       $listLines = explode( "\n", $content );
+                       foreach ( $listLines as $line ) {
+                               $res['content'][] = 
self::convertFromHumanEditableItemLine( $line );
+                       }
+               }
+               return $res;
+       }
+
+       /**
+        * Helper function that converts individual lines from 
convertFromHumanEditable.
+        *
+        * @param $line string
+        * @return array
+        */
+       private static function convertFromHumanEditableItemLine( $line ) {
+               $parts = explode( "|", $line );
+               $parts = array_map( [ __CLASS__, 'unescapeForHumanEditable' ], 
$parts );
+               $itemRes = [ 'title' => $parts[0] ];
+               if ( count( $parts ) > 1 ) {
+                       $parts = array_slice( $parts, 1 );
+                       foreach ( $parts as $part ) {
+                               list( $key, $value ) = explode( '=', $part );
+                               switch ( $key ) {
+                               case 'image':
+                               case 'display_title':
+                                       $itemRes[$key] = $value;
+                                       break;
+                               default:
+                                       $context = wfEscapeWikiText( substr( 
$part, 30 ) );
+                                       if ( strlen( $context ) === 30 ) {
+                                               $context .= '...';
+                                       }
+                                       throw new 
MWContentSerializationException(
+                                               "Unrecognized option for list 
item:" .
+                                               wfEscapeWikiText( $key )
+                                       );
+                               }
+                       }
+               }
+               return $itemRes;
+       }
+
+       /**
+        * Hook to use custom edit page for lists
+        *
+        * @param $page Page
+        * @param $user User
+        */
+       public static function onCustomEditor( Page $page, User $user ) {
+               if ( $page->getContentModel() === __CLASS__ ) {
+                       $editor = new CollaborationHubContentEditor( $page );
+                       $editor->edit();
+                       return false;
+               }
+       }
 }
diff --git a/includes/content/CollaborationHubContentHandler.php 
b/includes/content/CollaborationHubContentHandler.php
index 118c380..b334213 100644
--- a/includes/content/CollaborationHubContentHandler.php
+++ b/includes/content/CollaborationHubContentHandler.php
@@ -2,8 +2,19 @@
 
 class CollaborationHubContentHandler extends TextContentHandler {
 
-       public function __construct( $modelId = 'CollaborationHubContent' ) {
-               parent::__construct( $modelId );
+       const FORMAT_WIKI = 'text/x-collabkit';
+
+       public function __construct(
+               $modelId = 'CollaborationHubContent',
+               $formats = [ CONTENT_FORMAT_JSON, CONTENT_FORMAT_TEXT, 
self::FORMAT_WIKI ]
+       ) {
+               // text/x-collabkit is a format for lists similar to <gallery>.
+               // CONTENT_FORMAT_TEXT is for back-compat with old revs. Could 
be removed.
+
+               // @todo Ideally, we'd have the preferred format for editing be 
self::FORMAT_WIKI
+               // and the preferred format for db be CONTENT_FORMAT_JSON. 
Unclear if that's
+               // possible.
+               parent::__construct( $modelId, $formats );
        }
 
        /**
@@ -23,19 +34,40 @@
        }
 
        /**
+        * Constructs a CollaborationHubContent object. Does not perform any 
validation,
+        * as that is done at a later step (to allow for outputting of invalid 
content for
+        * debugging purposes.)
+        *
         * @param $text string
-        * @param $format string
+        * @param $format string|null
         * @return CollaborationHubContent
-        * @throws MWContentSerializationException
         */
        public function unserializeContent( $text, $format = null ) {
                $this->checkFormat( $format );
+               if ( $format === self::FORMAT_WIKI ) {
+                       $data = 
CollaborationHubContent::convertFromHumanEditable( $text );
+                       $text = FormatJson::encode( $data );
+               }
                $content = new CollaborationHubContent( $text );
                // Deliberately not validating at this step; validation is done 
later.
                return $content;
        }
 
        /**
+        * Serializes the CollaborationHubContent object.
+        *
+        * @param $content Content
+        * @param $format string|null
+        * @return mixed
+        */
+       public function serializeContent( Content $content, $format = null ) {
+               if ( $format === self::FORMAT_WIKI ) {
+                       return $content->convertToHumanEditable();
+               }
+               return parent::serializeContent( $content, $format );
+       }
+
+       /**
         * @return CollaborationHubContent
         */
        public function makeEmptyContent() {

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ia2d3a18272bf20d691e0038daabe4c6d44ef802f
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/CollaborationKit
Gerrit-Branch: master
Gerrit-Owner: Harej <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to