jenkins-bot has submitted this change and it was merged. Change subject: A user can delete their own collection ......................................................................
A user can delete their own collection * Add delete button to a removable collection view * Adds a ContentOverlay for deleting a collection * Add api method for removing a collection * On delete, redirect to Special:Gather as collection ** No longer will exist Bug: T91776 Change-Id: Ic561292d483502e4a44dfa0aa00acba83bf43f8f --- M extension.json M i18n/en.json M i18n/qqq.json M includes/views/Collection.php M resources/Resources.php A resources/ext.gather.collection.delete/CollectionDeleteOverlay.js A resources/ext.gather.collection.delete/content.hogan A resources/ext.gather.collection.delete/deleteOverlay.less M resources/ext.gather.special/init.js M resources/ext.gather.styles/collections.less M resources/ext.gather.watchstar/CollectionsApi.js 11 files changed, 249 insertions(+), 9 deletions(-) Approvals: Jdlrobson: Looks good to me, approved jenkins-bot: Verified diff --git a/extension.json b/extension.json index 209a1e9..4fbaafe 100644 --- a/extension.json +++ b/extension.json @@ -168,7 +168,8 @@ "gather-edit-collection-label-name", "gather-edit-collection-label-description", "gather-edit-collection-label-privacy", - "gather-edit-collection-save-label" + "gather-edit-collection-save-label", + "gather-error-unknown-collection" ], "templates": { "content.hogan": "ext.gather.collection.editor/content.hogan" @@ -180,6 +181,36 @@ "ext.gather.collection.editor/editOverlay.less" ] }, + "ext.gather.collection.delete": { + "targets": [ + "mobile", + "desktop" + ], + "dependencies": [ + "mobile.overlays", + "mobile.toast", + "ext.gather.api", + "mediawiki.util" + ], + "messages": [ + "gather-delete-collection-confirm", + "gather-delete-collection-heading", + "gather-delete-collection-delete-label", + "gather-delete-collection-cancel-label", + "gather-delete-collection-success", + "gather-delete-collection-failed-error", + "gather-error-unknown-collection" + ], + "templates": { + "content.hogan": "ext.gather.collection.delete/content.hogan" + }, + "scripts": [ + "ext.gather.collection.delete/CollectionDeleteOverlay.js" + ], + "styles": [ + "ext.gather.collection.delete/deleteOverlay.less" + ] + }, "ext.gather.special": { "targets": [ "mobile", @@ -187,7 +218,8 @@ ], "group": "other", "dependencies": [ - "ext.gather.collection.editor" + "ext.gather.collection.editor", + "ext.gather.collection.delete" ], "scripts": [ "ext.gather.special/init.js" diff --git a/i18n/en.json b/i18n/en.json index d18f52d..94976b7 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -8,6 +8,12 @@ "gather-edit-collection-label-privacy": "Private collection", "gather-edit-collection-save-label": "Done", "gather-edit-collection-failed-error": "There was a problem saving the changes.", + "gather-delete-collection-confirm": "Are you sure you want to delete this collection?", + "gather-delete-collection-heading": "Delete collection", + "gather-delete-collection-delete-label": "Delete", + "gather-delete-collection-cancel-label": "Cancel", + "gather-delete-collection-success": "Collection was successfully deleted.", + "gather-delete-collection-failed-error": "There was a problem deleting this collection.", "gather-error-unknown-collection": "Cannot find the requested collection to edit.", "gather-collection-member": "Is member of collection.", "gather-collection-non-member": "Is not member of collection.", @@ -32,6 +38,7 @@ "gather-empty": "Nothing in this collection yet...", "gather-empty-footer": "I don't know how you got here but this is a sad place.", "gather-edit-button": "Edit", + "gather-delete-button": "Delete", "apihelp-gather-description": "List and edit gather collections.", "apihelp-gather-param-gather": "Action to perform on collections.", "apihelp-gather-param-owner": "Owner of the collections to search for. If omitted, defaults to current user.", diff --git a/i18n/qqq.json b/i18n/qqq.json index 97cc048..a97bd5e 100644 --- a/i18n/qqq.json +++ b/i18n/qqq.json @@ -12,6 +12,12 @@ "gather-edit-collection-save-label": "Label for save button in collection editor.\n{{Identical|Done}}", "gather-edit-collection-failed-error": "There was a problem saving the changes.", "gather-error-unknown-collection": "Error message test when you try to edit a collection you do not own or that does not exist.", + "gather-delete-collection-heading": "Heading for collection delete overlay", + "gather-delete-collection-confirm": "Text under the heading asking the user if they would like to delete a collection.", + "gather-delete-collection-delete-label": "Label for delete button in delete overlay.\n{{Identical|Delete}}", + "gather-delete-collection-cancel-label": "Label for cancel button in delete overlay.\n{{Identical|Cancel}}", + "gather-delete-collection-success": "Toast message indicating that deletion was successful.", + "gather-delete-collection-failed-error": "Toast error indicating there was a problem deleting the collection.", "gather-collection-member": "Alternative text displayed next to collection name when page is a member.", "gather-collection-non-member": "Alternative text displayed next to collection name when page is not a member.", "gather-anon-cta": "Message that shows to anonymous users when they click the add to collection button.", @@ -35,6 +41,7 @@ "gather-empty": "Note: Experimental feature. Messages and UI may change radically at any time. Translate at your own risk.\n Message shown on an empty rendered collection on [[Special:Gather]].", "gather-empty-footer": "Note: Experimental feature. Messages and UI may change radically at any time. Translate at your own risk.\n Footnote shown on an empty rendered collection on [[Special:Gather]].", "gather-edit-button": "Label for a button that enables editing a collection.\n{{Identical|Edit}}", + "gather-delete-button": "Label for a button that enables deleting a collection.\n{{Identical|Delete}}", "apihelp-gather-description": "{{doc-apihelp-description|gather}}", "apihelp-gather-param-gather": "{{doc-apihelp-param|gather|gather}}", "apihelp-gather-param-owner": "{{doc-apihelp-param|gather|owner}}", diff --git a/includes/views/Collection.php b/includes/views/Collection.php index 13a04fc..9af70ba 100644 --- a/includes/views/Collection.php +++ b/includes/views/Collection.php @@ -84,6 +84,7 @@ ) ) . $this->getEditButtonHtml() . + $this->getDeleteButtonHtml() . Html::closeElement( 'div' ); } @@ -104,6 +105,24 @@ } } + + /** + * Gets the delete button html if the user can delete + * Restricted to collection owner and does not apply to watchlist + */ + public function getDeleteButtonHtml() { + $id = $this->collection->getId(); + if ( $this->collection->isOwner( $this->user ) && $id !== 0 ) { + return Html::element( 'a', array( + // FIXME: This should work without JavaScript + 'href' => '#/collection/delete/' . $id, + 'class' => CSS::buttonClass( 'destructive', 'collection-action-button delete-collection' ) + ), wfMessage( 'gather-delete-button' )->text() ); + } else { + return ''; + } + } + /** * Returns the html for an empty collection * diff --git a/resources/Resources.php b/resources/Resources.php index 5e782d0..e36c54e 100644 --- a/resources/Resources.php +++ b/resources/Resources.php @@ -131,6 +131,7 @@ 'gather-edit-collection-label-description', 'gather-edit-collection-label-privacy', 'gather-edit-collection-save-label', + 'gather-error-unknown-collection', ), 'templates' => array( 'content.hogan' => 'ext.gather.collection.editor/content.hogan', @@ -143,9 +144,37 @@ ), ), + 'ext.gather.collection.delete' => $wgGatherResourceFileModuleBoilerplate + array( + 'dependencies' => array( + 'mobile.overlays', + 'mobile.toast', + 'ext.gather.api', + 'mediawiki.util' + ), + 'messages' => array( + 'gather-delete-collection-confirm', + 'gather-delete-collection-heading', + 'gather-delete-collection-delete-label', + 'gather-delete-collection-cancel-label', + 'gather-delete-collection-success', + 'gather-delete-collection-failed-error', + 'gather-error-unknown-collection', + ), + 'templates' => array( + 'content.hogan' => 'ext.gather.collection.delete/content.hogan', + ), + 'scripts' => array( + 'ext.gather.collection.delete/CollectionDeleteOverlay.js', + ), + 'styles' => array( + 'ext.gather.collection.delete/deleteOverlay.less', + ), + ), + 'ext.gather.special' => $wgGatherMobileSpecialPageResourceBoilerplate + array( 'dependencies' => array( 'ext.gather.collection.editor', + 'ext.gather.collection.delete', ), 'scripts' => array( 'ext.gather.special/init.js', diff --git a/resources/ext.gather.collection.delete/CollectionDeleteOverlay.js b/resources/ext.gather.collection.delete/CollectionDeleteOverlay.js new file mode 100644 index 0000000..e8487e1 --- /dev/null +++ b/resources/ext.gather.collection.delete/CollectionDeleteOverlay.js @@ -0,0 +1,88 @@ +( function ( M, $ ) { + + var CollectionDeleteOverlay, + toast = M.require( 'toast' ), + icons = M.require( 'icons' ), + CollectionsApi = M.require( 'ext.gather.watchstar/CollectionsApi' ), + ContentOverlay = M.require( 'modules/tutorials/ContentOverlay' ); + + /** + * Overlay for deleting a collection + * @extends ContentOverlay + * @class CollectionDeleteOverlay + */ + CollectionDeleteOverlay = ContentOverlay.extend( { + /** @inheritdoc */ + className: 'collection-delete-overlay content-overlay position-fixed', + /** @inheritdoc */ + hasFixedHeader: false, + /** @inheritdoc */ + defaults: $.extend( {}, ContentOverlay.prototype.defaults, { + fixedHeader: false, + collection: null, + spinner: icons.spinner().toHtmlString(), + deleteSuccessMsg: mw.msg( 'gather-delete-collection-success' ), + deleteFailedError: mw.msg( 'gather-delete-collection-failed-error' ), + unknownCollectionError: mw.msg( 'gather-error-unknown-collection' ), + subheadingDeleteCollection: mw.msg( 'gather-delete-collection-heading' ), + confirmMessage: mw.msg( 'gather-delete-collection-confirm' ), + deleteButtonLabel: mw.msg( 'gather-delete-collection-delete-label' ), + cancelButtonLabel: mw.msg( 'gather-delete-collection-cancel-label' ) + } ), + /** @inheritdoc */ + events: $.extend( {}, ContentOverlay.prototype.events, { + 'click .delete-collection': 'onDeleteClick', + 'click .cancel-delete': 'onCancelClick' + } ), + /** @inheritdoc */ + templatePartials: $.extend( {}, ContentOverlay.prototype.templatePartials, { + content: mw.template.get( 'ext.gather.collection.delete', 'content.hogan' ) + } ), + /** @inheritdoc */ + initialize: function ( options ) { + var collection = options.collection; + if ( !collection ) { + // use toast + toast.show( options.unknownCollectionError, 'toast error' ); + } else { + this.id = collection.id; + this.api = new CollectionsApi(); + ContentOverlay.prototype.initialize.apply( this, arguments ); + } + }, + postRender: function () { + this.$( '.spinner' ).hide(); + }, + /** + * Event handler when the save button is clicked. + */ + onDeleteClick: function () { + var self = this; + this.$( '.spinner' ).show(); + // disable button and inputs + this.$( '.delete-collection, .cancel-delete' ).prop( 'disabled', true ); + this.api.removeCollection( this.id ).done( function () { + // Show toast + self.$( '.spinner' ).hide(); + toast.show( self.options.deleteSuccessMsg, 'toast' ); + + // Go to the collections list page as collection will no longer exist + window.location.href = mw.util.getUrl( 'Special:Gather' ); + + } ).fail( function () { + toast.show( self.options.deleteFailedError, 'toast error' ); + // Make it possible to try again. + self.$( '.delete-collection, .cancel-delete' ).prop( 'disabled', false ); + } ); + }, + /** + * Event handler when the cancel button is clicked. + */ + onCancelClick: function () { + this.hide(); + } + } ); + + M.define( 'ext.gather.delete/CollectionDeleteOverlay', CollectionDeleteOverlay ); + +}( mw.mobileFrontend, jQuery ) ); diff --git a/resources/ext.gather.collection.delete/content.hogan b/resources/ext.gather.collection.delete/content.hogan new file mode 100644 index 0000000..2870119 --- /dev/null +++ b/resources/ext.gather.collection.delete/content.hogan @@ -0,0 +1,9 @@ +{{{spinner}}} +<div class="delete-collection-content"> + <h3>{{subheadingDeleteCollection}}</h3> + <span>{{confirmMessage}}</span> + <div class="collection-delete-actions"> + <button class='mw-ui-button mw-ui-destructive delete-collection'>{{deleteButtonLabel}}</button> + <button class='mw-ui-button mw-ui-progressive cancel-delete'>{{cancelButtonLabel}}</button> + </div> +</div> diff --git a/resources/ext.gather.collection.delete/deleteOverlay.less b/resources/ext.gather.collection.delete/deleteOverlay.less new file mode 100644 index 0000000..b1912b8 --- /dev/null +++ b/resources/ext.gather.collection.delete/deleteOverlay.less @@ -0,0 +1,27 @@ +@import "minerva.variables"; +@import "minerva.mixins"; + +.content-overlay.collection-delete-overlay { + font-size: .9em; + text-align: center; + + h3 { + padding: 0.25em; + } + + span { + display: block; + padding: 0.5em; + } + + .collection-delete-actions { + padding: 0.5em; + } + + &.content-overlay { + background-color: white; + top: @headerHeight * 1.2; + color: @grayDark; + width: auto; + } +} \ No newline at end of file diff --git a/resources/ext.gather.special/init.js b/resources/ext.gather.special/init.js index da370f7..698b699 100644 --- a/resources/ext.gather.special/init.js +++ b/resources/ext.gather.special/init.js @@ -1,11 +1,12 @@ ( function ( M, $ ) { var CollectionEditOverlay = M.require( 'ext.gather.edit/CollectionEditOverlay' ), + CollectionDeleteOverlay = M.require( 'ext.gather.delete/CollectionDeleteOverlay' ), overlayManager = M.require( 'overlayManager' ); - /** Add routes to the overlay manager */ - function addOverlayManagerEditing() { - overlayManager.add( /^\/collection\/edit\/(.*)$/, function ( id ) { + /** Add routes for editing and deleting to the overlay manager */ + function addOverlayManagerRoutes() { + overlayManager.add( /^\/collection\/(.*)\/(.*)$/, function ( action, id ) { id = parseInt( id, 10 ); var collection; $.each( mw.config.get( 'wgGatherCollections' ), function () { @@ -14,15 +15,23 @@ } } ); if ( collection ) { - return new CollectionEditOverlay( { - collection: collection - } ); + if ( action === 'edit' ) { + return new CollectionEditOverlay( { + collection: collection + } ); + } else if ( action === 'delete' ) { + return new CollectionDeleteOverlay( { + collection: collection + } ); + } + } else { + return null; } } ); } $( function () { - addOverlayManagerEditing(); + addOverlayManagerRoutes(); $( '.collection-actions' ).show(); } ); }( mw.mobileFrontend, jQuery ) ); diff --git a/resources/ext.gather.styles/collections.less b/resources/ext.gather.styles/collections.less index 7f9c4ad..db7c480 100644 --- a/resources/ext.gather.styles/collections.less +++ b/resources/ext.gather.styles/collections.less @@ -73,6 +73,7 @@ .collection-action-button { padding: 0.5em 2em; + margin: 0 0.25em; } } diff --git a/resources/ext.gather.watchstar/CollectionsApi.js b/resources/ext.gather.watchstar/CollectionsApi.js index e9cefcc..26e31ce 100644 --- a/resources/ext.gather.watchstar/CollectionsApi.js +++ b/resources/ext.gather.watchstar/CollectionsApi.js @@ -75,6 +75,18 @@ } ); }, /** + * Removes a collection + * @method + * @param {Number} id unique identifier of collection + */ + removeCollection: function( id ) { + return this.postWithToken( 'watch', { + action: 'editlist', + deletelist: 1, + id: id + } ); + }, + /** * Edits a collection * @method * @param {Number} id unique identifier of collection -- To view, visit https://gerrit.wikimedia.org/r/196055 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: Ic561292d483502e4a44dfa0aa00acba83bf43f8f Gerrit-PatchSet: 7 Gerrit-Project: mediawiki/extensions/Gather Gerrit-Branch: master Gerrit-Owner: Robmoen <rm...@wikimedia.org> Gerrit-Reviewer: Jdlrobson <jrob...@wikimedia.org> Gerrit-Reviewer: Legoktm <legoktm.wikipe...@gmail.com> Gerrit-Reviewer: Robmoen <rm...@wikimedia.org> Gerrit-Reviewer: Siebrand <siebr...@kitano.nl> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits