Multi renaming.

Adds an api call for renaming a multi while preserving metadata.
Abstracts MultiCreateForm to handle renaming.
This commit is contained in:
Max Goodman
2013-06-13 01:18:51 -07:00
parent 90c38d9902
commit 91e4c6742d
7 changed files with 150 additions and 30 deletions

View File

@@ -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")

View File

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

View File

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

View File

@@ -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 = {}

View File

@@ -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;
}
}

View File

@@ -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',

View File

@@ -42,6 +42,9 @@
%if thing.can_copy:
<button class="show-copy">${_('copy')}</button>
%endif
%if thing.can_rename:
<button class="show-rename">${_('rename')}</button>
%endif
<button class="delete">${_('delete')}</button>
%else:
%if thing.can_copy:
@@ -57,6 +60,14 @@
</form>
%endif
%if thing.can_rename:
<form class="rename-multi">
<p class="warning">${_('warning: renaming a multi will break any links and references to the old name.')}</p>
<input type="text" class="multi-name" placeholder="${_('new name')}"><button class="rename">${_('rename')} &rsaquo;</button>
<div class="error rename-error"></div>
</form>
%endif
<h3>${unsafe(_('%(count)s subreddits in this multi:') % dict(count='<span class="count">%d</span>&#32;' % len(thing.srs)))}</h3>
<ul class="subreddits">
%for sr in thing.srs: