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 
 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