From 91e4c6742d95e95790030bd25fd0afbdab4755ab Mon Sep 17 00:00:00 2001 From: Max Goodman Date: Thu, 13 Jun 2013 01:18:51 -0700 Subject: [PATCH] Multi renaming. Adds an api call for renaming a multi while preserving metadata. Abstracts MultiCreateForm to handle renaming. --- r2/r2/config/routing.py | 1 + r2/r2/controllers/multi.py | 28 +++++++ r2/r2/lib/pages/pages.py | 1 + r2/r2/models/subreddit.py | 7 ++ r2/r2/public/static/css/reddit.less | 21 +++++- r2/r2/public/static/js/multi.js | 111 +++++++++++++++++++++------- r2/r2/templates/multiinfobar.html | 11 +++ 7 files changed, 150 insertions(+), 30 deletions(-) diff --git a/r2/r2/config/routing.py b/r2/r2/config/routing.py index 9e61ab242..d30891d01 100644 --- a/r2/r2/config/routing.py +++ b/r2/r2/config/routing.py @@ -321,6 +321,7 @@ def make_map(): mc('/api/:action', controller='api') mc("/api/multi/mine", controller="multiapi", action="my_multis") + mc("/api/multi/*multipath/rename", controller="multiapi", action="multi_rename") mc("/api/multi/*multipath/r/:srname", controller="multiapi", action="multi_subreddit") mc("/api/multi/*multipath", controller="multiapi", action="multi") diff --git a/r2/r2/controllers/multi.py b/r2/r2/controllers/multi.py index 90cba9043..d8d24e86e 100644 --- a/r2/r2/controllers/multi.py +++ b/r2/r2/controllers/multi.py @@ -190,6 +190,34 @@ class MultiApiController(RedditController, OAuth2ResourceController): """Delete a multi.""" multi.delete() + @require_oauth2_scope("subscribe") + @api_doc( + api_section.multis, + uri="/api/multi/{multipath}/rename", + ) + @validate( + VUser(), + VModhash(), + from_multi=VMultiByPath("multipath", require_edit=True), + to_path_info=VMultiPath("to", + docs={"to": "destination multireddit url path"}, + ), + ) + def POST_multi_rename(self, multi, to_path_info): + """Rename a multi.""" + + self._check_new_multi_path(to_path_info) + + try: + LabeledMulti._byID(to_path_info['path']) + except tdb_cassandra.NotFound: + to_multi = LabeledMulti.copy(to_path_info['path'], multi) + else: + raise RedditError('MULTI_EXISTS', code=409, fields='multipath') + + multi.delete() + return self._format_multi(to_multi) + def _get_multi_subreddit(self, multi, sr): resp = LabeledMultiJsonTemplate.sr_props(multi, [sr])[0] return self.api_wrapper(resp) diff --git a/r2/r2/lib/pages/pages.py b/r2/r2/lib/pages/pages.py index a1dd7ceaf..2b4e03c93 100755 --- a/r2/r2/lib/pages/pages.py +++ b/r2/r2/lib/pages/pages.py @@ -1843,6 +1843,7 @@ class MultiInfoBar(Templated): self.multi = wrap_things(multi)[0] self.can_edit = multi.can_edit(user) self.can_copy = c.user_is_loggedin + self.can_rename = c.user_is_loggedin and multi.owner == c.user srs.sort(key=lambda sr: sr.name.lower()) self.srs = srs diff --git a/r2/r2/models/subreddit.py b/r2/r2/models/subreddit.py index 237faebf1..37a7b8711 100644 --- a/r2/r2/models/subreddit.py +++ b/r2/r2/models/subreddit.py @@ -1422,6 +1422,13 @@ class LabeledMulti(tdb_cassandra.Thing, MultiReddit): obj._owner = owner return obj + @classmethod + def copy(cls, path, multi): + obj = cls(_id=path, **multi._t) + obj._commit() + obj._owner = multi.owner + return obj + @classmethod def sr_props_to_columns(cls, sr_props): columns = {} diff --git a/r2/r2/public/static/css/reddit.less b/r2/r2/public/static/css/reddit.less index fc784ac85..e36512964 100755 --- a/r2/r2/public/static/css/reddit.less +++ b/r2/r2/public/static/css/reddit.less @@ -5148,7 +5148,7 @@ table.calendar { cursor: pointer; } - label, .show-copy, .delete { + label, & > button { margin-right: 12px; } @@ -5163,7 +5163,7 @@ table.calendar { margin-bottom: 3px; } - form.copy-multi { + form.copy-multi, form.rename-multi { display: none; margin-bottom: 10px; @@ -5175,7 +5175,22 @@ table.calendar { button { .light-button; padding: 3px 4px; - background: #eeffdd; + } + } + + form.copy-multi button { + background: #eeffdd; + } + + form.rename-multi { + button { + background: #ffffdd; + } + + .warning { + margin-top: .5em; + font-weight: bold; + color: #c2461f; } } diff --git a/r2/r2/public/static/js/multi.js b/r2/r2/public/static/js/multi.js index 492e491d0..e14a23318 100644 --- a/r2/r2/public/static/js/multi.js +++ b/r2/r2/public/static/js/multi.js @@ -124,6 +124,26 @@ r.multi.MultiReddit = Backbone.Model.extend({ this.subreddits.getByName(name).destroy(options) }, + rename: function(newPath) { + var deferred = new $.Deferred + Backbone.ajax({ + type: 'POST', + url: this.url() + '/rename', + data: { + to: newPath + }, + success: _.bind(function(resp) { + var collection = this.collection + this.trigger('destroy', this, this.collection) + var multi = r.multi.multis.reify(resp) + r.multi.mine.add(multi) + deferred.resolve(multi) + }, this), + error: _.bind(deferred.reject, deferred) + }) + return deferred + }, + copyTo: function(newMulti) { var attrs = _.clone(this.attributes) delete attrs.path @@ -216,6 +236,7 @@ r.multi.MultiDetails = Backbone.View.extend({ 'submit .add-sr': 'addSubreddit', 'change [name="visibility"]': 'setVisibility', 'click .show-copy': 'showCopyMulti', + 'click .show-rename': 'showRenameMulti', 'confirm .delete': 'deleteMulti' }, @@ -318,6 +339,8 @@ r.multi.MultiDetails = Backbone.View.extend({ }, showCopyMulti: function() { + this.$('form.rename-multi').hide() + var $copyForm = this.$('form.copy-multi') $copyForm @@ -328,16 +351,31 @@ r.multi.MultiDetails = Backbone.View.extend({ .focus() if (!this.copyForm) { - this.copyForm = new r.multi.MultiCreateForm({ + this.copyForm = new r.multi.MultiCopyForm({ el: $copyForm, navOnCreate: true, - createMulti: _.bind(function(name) { - var newMulti = new r.multi.MultiReddit({ - path: r.multi.mine.pathByName(name) - }, {isNew: true}) - this.model.copyTo(newMulti) - return newMulti - }, this) + sourceMulti: this.model + }) + } + }, + + showRenameMulti: function() { + this.$('form.copy-multi').hide() + + var $renameForm = this.$('form.rename-multi') + + $renameForm + .show() + .find('.multi-name') + .val(this.model.name()) + .select() + .focus() + + if (!this.renameForm) { + this.renameForm = new r.multi.MultiRenameForm({ + el: $renameForm, + navOnCreate: true, + sourceMulti: this.model }) } }, @@ -453,10 +491,6 @@ r.multi.MultiCreateForm = Backbone.View.extend({ 'submit': 'createMulti' }, - initialize: function() { - this.showWorkingDeferred = _.bind(r.ui.showWorkingDeferred, this, this.$el) - }, - createMulti: function(ev) { ev.preventDefault() @@ -466,29 +500,40 @@ r.multi.MultiCreateForm = Backbone.View.extend({ return } - var newMulti - if (this.options.createMulti) { - newMulti = this.options.createMulti(name) - } else { - var newMulti = new r.multi.MultiReddit({ - path: r.multi.mine.pathByName(name) - }, {isNew: true}) - } + var deferred = this._createMulti(name) - r.multi.mine.create(newMulti, { - wait: true, - beforeSend: this.showWorkingDeferred, - success: _.bind(function(multi) { + deferred + .done(_.bind(function(multi) { this.trigger('create', multi) if (this.options.navOnCreate) { window.location = multi.get('path') + '#created' } - }, this), - error: _.bind(function(multi, xhr) { + }, this)) + .fail(_.bind(function(xhr) { var resp = JSON.parse(xhr.responseText) this.showError(resp.explanation) - }, this) + }, this)) + + r.ui.showWorkingDeferred(this.$el, deferred) + }, + + _createMulti: function(name) { + var newMulti = new r.multi.MultiReddit({ + path: r.multi.mine.pathByName(name) + }, {isNew: true}) + + this._alterMulti(newMulti) + + var deferred = new $.Deferred + r.multi.mine.create(newMulti, { + wait: true, + success: _.bind(deferred.resolve, deferred), + error: function(multi, xhr) { + deferred.reject(xhr) + } }) + + return deferred }, showError: function(error) { @@ -500,6 +545,18 @@ r.multi.MultiCreateForm = Backbone.View.extend({ } }) +r.multi.MultiCopyForm = r.multi.MultiCreateForm.extend({ + _alterMulti: function(multi) { + this.options.sourceMulti.copyTo(multi) + } +}) + +r.multi.MultiRenameForm = r.multi.MultiCopyForm.extend({ + _createMulti: function(name) { + return this.options.sourceMulti.rename(r.multi.mine.pathByName(name)) + } +}) + r.multi.ListingChooser = Backbone.View.extend({ events: { 'click .create button': 'createClick', diff --git a/r2/r2/templates/multiinfobar.html b/r2/r2/templates/multiinfobar.html index 1dcadd4f5..9bad7c250 100644 --- a/r2/r2/templates/multiinfobar.html +++ b/r2/r2/templates/multiinfobar.html @@ -42,6 +42,9 @@ %if thing.can_copy: %endif + %if thing.can_rename: + + %endif %else: %if thing.can_copy: @@ -57,6 +60,14 @@ %endif + %if thing.can_rename: +
+

${_('warning: renaming a multi will break any links and references to the old name.')}

+ +
+
+ %endif +

${unsafe(_('%(count)s subreddits in this multi:') % dict(count='%d ' % len(thing.srs)))}