mirror of
https://github.com/fergalmoran/dss.git
synced 2026-02-14 03:54:03 +00:00
Fixed comments and disabled download
This commit is contained in:
@@ -117,6 +117,7 @@ TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + (
|
||||
"allauth.account.context_processors.account",
|
||||
"spa.context_processors.debug"
|
||||
)
|
||||
|
||||
AUTHENTICATION_BACKENDS = global_settings.AUTHENTICATION_BACKENDS + (
|
||||
"allauth.account.auth_backends.AuthenticationBackend",
|
||||
)
|
||||
@@ -129,6 +130,7 @@ MIDDLEWARE_CLASSES = (
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'spa.middleware.cors.XsSharingMiddleware',
|
||||
#'spa.middleware.uploadify.SWFUploadMiddleware',
|
||||
#'spa.middleware.sqlprinter.SqlPrintingMiddleware' if DEBUG else None,
|
||||
#'debug_toolbar.middleware.DebugToolbarMiddleware',
|
||||
@@ -254,7 +256,11 @@ if DEBUG:
|
||||
import mimetypes
|
||||
|
||||
mimetypes.add_type("image/png", ".png", True)
|
||||
mimetypes.add_type("font/woff", ".woff", True)
|
||||
mimetypes.add_type("image/png", ".png", True)
|
||||
mimetypes.add_type("application/x-font-woff", ".woff", True)
|
||||
mimetypes.add_type("application/vnd.ms-fontobject", ".eot", True)
|
||||
mimetypes.add_type("font/ttf", ".ttf", True)
|
||||
mimetypes.add_type("font/otf", ".otf", True)
|
||||
|
||||
# TODO(fergal.moran@gmail.com): #import localsettings - so all localsettings are part of import settings
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
from tastypie import fields
|
||||
from tastypie.authentication import Authentication
|
||||
from tastypie.authorization import Authorization
|
||||
from tastypie.exceptions import ImmediateHttpResponse
|
||||
from tastypie.http import HttpForbidden, HttpBadRequest
|
||||
from spa.api.v1.BackboneCompatibleResource import BackboneCompatibleResource
|
||||
from spa.models import Mix
|
||||
from spa.models.comment import Comment
|
||||
|
||||
|
||||
@@ -18,14 +21,20 @@ class CommentResource(BackboneCompatibleResource):
|
||||
authentication = Authentication()
|
||||
always_return_data = True
|
||||
|
||||
def obj_create(self, bundle, request=None, **kwargs):
|
||||
bundle.data['user'] = {'pk': request.user.pk}
|
||||
return super(CommentResource, self).obj_create(bundle, request, user=request.user)
|
||||
def obj_create(self, bundle, **kwargs):
|
||||
bundle.data['user'] = bundle.request.user
|
||||
|
||||
"""
|
||||
def dehydrate_date_created(self, bundle):
|
||||
return self.humanize_date(bundle.obj.date_created)
|
||||
"""
|
||||
try:
|
||||
if 'mix_id' in bundle.data:
|
||||
mix = Mix.objects.get(pk=bundle.data['mix_id'])
|
||||
if mix is not None:
|
||||
return super(CommentResource, self).obj_create(bundle, user=bundle.request.user, mix=mix)
|
||||
except Exception, e:
|
||||
self.logger.error("Error creating comment (%s)" % e.message)
|
||||
pass
|
||||
raise ImmediateHttpResponse(
|
||||
HttpBadRequest("Unable to hydrate comment from supplied data.")
|
||||
)
|
||||
|
||||
def dehydrate(self, bundle):
|
||||
bundle.data['avatar_image'] = bundle.obj.user.get_profile().get_small_profile_image()
|
||||
|
||||
@@ -20,7 +20,7 @@ from spa.models.mix import Mix
|
||||
|
||||
|
||||
class MixResource(BackboneCompatibleResource):
|
||||
comments = fields.ToManyField('spa.api.v1.CommentResource.CommentResource', 'comments', null=True)
|
||||
comments = fields.ToManyField('spa.api.v1.CommentResource.CommentResource', 'comments', null=True, full=True)
|
||||
favourites = fields.ToManyField('spa.api.v1.UserResource.UserResource', 'favourites',
|
||||
related_name='favourites', full=False, null=True)
|
||||
|
||||
@@ -37,6 +37,7 @@ class MixResource(BackboneCompatibleResource):
|
||||
'comments': ALL_WITH_RELATIONS,
|
||||
'favourites': ALL_WITH_RELATIONS,
|
||||
'likes': ALL_WITH_RELATIONS,
|
||||
'slug': ALL_WITH_RELATIONS,
|
||||
}
|
||||
authorization = Authorization()
|
||||
|
||||
|
||||
39
spa/middleware/cors.py
Normal file
39
spa/middleware/cors.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from django import http
|
||||
|
||||
try:
|
||||
import settings
|
||||
XS_SHARING_ALLOWED_ORIGINS = settings.XS_SHARING_ALLOWED_ORIGINS
|
||||
XS_SHARING_ALLOWED_METHODS = settings.XS_SHARING_ALLOWED_METHODS
|
||||
except:
|
||||
XS_SHARING_ALLOWED_ORIGINS = '*'
|
||||
XS_SHARING_ALLOWED_METHODS = ['POST','GET','OPTIONS', 'PUT', 'DELETE']
|
||||
|
||||
|
||||
class XsSharingMiddleware(object):
|
||||
"""
|
||||
This middleware allows cross-domain XHR using the html5 postMessage API.
|
||||
|
||||
|
||||
Access-Control-Allow-Origin: http://foo.example
|
||||
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE
|
||||
"""
|
||||
def process_request(self, request):
|
||||
|
||||
if 'HTTP_ACCESS_CONTROL_REQUEST_METHOD' in request.META:
|
||||
response = http.HttpResponse()
|
||||
response['Access-Control-Allow-Origin'] = XS_SHARING_ALLOWED_ORIGINS
|
||||
response['Access-Control-Allow-Methods'] = ",".join( XS_SHARING_ALLOWED_METHODS )
|
||||
|
||||
return response
|
||||
|
||||
return None
|
||||
|
||||
def process_response(self, request, response):
|
||||
# Avoid unnecessary work
|
||||
if response.has_header('Access-Control-Allow-Origin'):
|
||||
return response
|
||||
|
||||
response['Access-Control-Allow-Origin'] = XS_SHARING_ALLOWED_ORIGINS
|
||||
response['Access-Control-Allow-Methods'] = ",".join( XS_SHARING_ALLOWED_METHODS )
|
||||
|
||||
return response
|
||||
@@ -12,7 +12,7 @@ class Comment(_BaseModel):
|
||||
mix = models.ForeignKey(Mix, editable=False, null=True, blank=True, related_name='comments')
|
||||
comment = models.CharField(max_length=1024)
|
||||
date_created = models.DateTimeField(auto_now=True)
|
||||
time_index = models.IntegerField()
|
||||
time_index = models.IntegerField(default=0)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return '/comment/%i' % self.id
|
||||
|
||||
@@ -43,7 +43,10 @@ def get_dialog(request, dialog_name, **kwargs):
|
||||
|
||||
def get_javascript(request, template_name):
|
||||
localsettings.JS_SETTINGS.update({
|
||||
'CURRENT_USER_ID': request.user.get_profile().id if not request.user.is_anonymous() else -1
|
||||
'CURRENT_USER_ID': request.user.get_profile().id if not request.user.is_anonymous() else -1,
|
||||
'CURRENT_USER_NAME': request.user.get_profile().get_nice_name() if not request.user.is_anonymous() else -1,
|
||||
'CURRENT_USER_URL': request.user.get_profile().get_profile_url() if not request.user.is_anonymous() else -1,
|
||||
'AVATAR_IMAGE': request.user.get_profile().get_small_profile_image() if not request.user.is_anonymous() else ""
|
||||
})
|
||||
return render_to_response(
|
||||
'javascript/%s.js' % template_name,
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
// Generated by CoffeeScript 1.3.3
|
||||
// Generated by CoffeeScript 1.6.2
|
||||
(function() {
|
||||
var __hasProp = {}.hasOwnProperty,
|
||||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
|
||||
|
||||
define(['marionette', 'vent', 'app.lib/controller'], function(Marionette, vent, Controller) {
|
||||
var DssRouter;
|
||||
return DssRouter = (function(_super) {
|
||||
var DssRouter, _ref;
|
||||
|
||||
return DssRouter = (function(_super) {
|
||||
__extends(DssRouter, _super);
|
||||
|
||||
function DssRouter() {
|
||||
return DssRouter.__super__.constructor.apply(this, arguments);
|
||||
_ref = DssRouter.__super__.constructor.apply(this, arguments);
|
||||
return _ref;
|
||||
}
|
||||
|
||||
DssRouter.prototype.controller = new Controller;
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
define ['backbone'],
|
||||
(Backbone) ->
|
||||
class CommentItem extends Backbone.Model
|
||||
urlRoot:com.podnoms.settings.urlRoot + "comments/"
|
||||
define ['app.lib/backbone.dss.model'], \
|
||||
(DSSModel) ->
|
||||
class CommentItem extends DSSModel
|
||||
urlRoot: com.podnoms.settings.urlRoot + "comments/"
|
||||
defaults:
|
||||
avatar_image: com.podnoms.settings.avatarImage
|
||||
user_name: com.podnoms.settings.userName
|
||||
user_url: com.podnoms.settings.userUrl
|
||||
date_created: ""
|
||||
|
||||
CommentItem
|
||||
CommentItem
|
||||
@@ -1,24 +1,31 @@
|
||||
// Generated by CoffeeScript 1.3.3
|
||||
// Generated by CoffeeScript 1.6.2
|
||||
(function() {
|
||||
var __hasProp = {}.hasOwnProperty,
|
||||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
|
||||
|
||||
define(['backbone'], function(Backbone) {
|
||||
var CommentItem;
|
||||
CommentItem = (function(_super) {
|
||||
define(['app.lib/backbone.dss.model'], function(DSSModel) {
|
||||
var CommentItem, _ref;
|
||||
|
||||
CommentItem = (function(_super) {
|
||||
__extends(CommentItem, _super);
|
||||
|
||||
function CommentItem() {
|
||||
return CommentItem.__super__.constructor.apply(this, arguments);
|
||||
_ref = CommentItem.__super__.constructor.apply(this, arguments);
|
||||
return _ref;
|
||||
}
|
||||
|
||||
CommentItem.prototype.urlRoot = com.podnoms.settings.urlRoot + "comments/";
|
||||
|
||||
CommentItem.prototype.defaults = {
|
||||
avatar_image: com.podnoms.settings.avatarImage,
|
||||
user_name: com.podnoms.settings.userName,
|
||||
user_url: com.podnoms.settings.userUrl,
|
||||
date_created: ""
|
||||
};
|
||||
|
||||
return CommentItem;
|
||||
|
||||
})(Backbone.Model);
|
||||
({
|
||||
urlRoot: com.podnoms.settings.urlRoot + "comments/"
|
||||
});
|
||||
})(DSSModel);
|
||||
return CommentItem;
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
define ['backbone', 'app.lib/backbone.dss.model'], \
|
||||
(Backbone, DssModel) ->
|
||||
class MixItem extends Backbone.Model
|
||||
define ['backbone.relational', 'models/comment/commentCollection', 'models/comment/commentItem', 'app.lib/backbone.dss.model'], \
|
||||
(Backbone, CommentCollection, CommentItem, DSSModel) ->
|
||||
class MixItem extends DSSModel
|
||||
urlRoot: com.podnoms.settings.urlRoot + "mix/"
|
||||
|
||||
"""
|
||||
relations: [
|
||||
type: Backbone.HasMany
|
||||
key: "comments"
|
||||
relatedModel: CommentItem
|
||||
collectionType: CommentCollection
|
||||
reverseRelation:
|
||||
key: "hasItems"
|
||||
includeInJSON: "id"
|
||||
]
|
||||
"""
|
||||
MixItem
|
||||
@@ -3,7 +3,7 @@
|
||||
var __hasProp = {}.hasOwnProperty,
|
||||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
|
||||
|
||||
define(['backbone', 'app.lib/backbone.dss.model'], function(Backbone, DssModel) {
|
||||
define(['backbone.relational', 'models/comment/commentCollection', 'models/comment/commentItem', 'app.lib/backbone.dss.model'], function(Backbone, CommentCollection, CommentItem, DSSModel) {
|
||||
var MixItem, _ref;
|
||||
|
||||
MixItem = (function(_super) {
|
||||
@@ -16,9 +16,11 @@
|
||||
|
||||
MixItem.prototype.urlRoot = com.podnoms.settings.urlRoot + "mix/";
|
||||
|
||||
"relations: [\n type: Backbone.HasMany\n key: \"comments\"\n relatedModel: CommentItem\n collectionType: CommentCollection\n reverseRelation:\n key: \"hasItems\"\n includeInJSON: \"id\"\n ]";
|
||||
|
||||
return MixItem;
|
||||
|
||||
})(Backbone.Model);
|
||||
})(DSSModel);
|
||||
return MixItem;
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
define ['marionette', 'views/comment/commentItemView', 'text!/tpl/CommentListView'],
|
||||
(Marionette, CommentItemView, Template) ->
|
||||
define ['marionette', 'models/comment/commentItem', 'views/comment/commentItemView', 'text!/tpl/CommentListView'],
|
||||
(Marionette, CommentItem, CommentItemView, Template) ->
|
||||
class CommentListView extends Marionette.CompositeView
|
||||
|
||||
template: _.template(Template)
|
||||
@@ -8,7 +8,31 @@ define ['marionette', 'views/comment/commentItemView', 'text!/tpl/CommentListVie
|
||||
itemView: CommentItemView
|
||||
itemViewContainer: "#comment-list-container"
|
||||
|
||||
ui:
|
||||
commentText: '#comment-text'
|
||||
|
||||
events:
|
||||
"click #btn-add-comment": "addComment"
|
||||
|
||||
initialize: ->
|
||||
console.log "CommentListView: initialize"
|
||||
|
||||
addComment: ->
|
||||
console.log "CommentListView: addComment"
|
||||
@collection.create
|
||||
mix_id: @collection.mix.get("id")
|
||||
comment: @ui.commentText.val()
|
||||
,
|
||||
success: (newItem) =>
|
||||
@ui.commentText.val ""
|
||||
true
|
||||
|
||||
error: (a, b, c) ->
|
||||
console.log a
|
||||
console.log b
|
||||
console.log c
|
||||
true
|
||||
|
||||
true
|
||||
|
||||
CommentListView
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
// Generated by CoffeeScript 1.3.3
|
||||
// Generated by CoffeeScript 1.6.2
|
||||
(function() {
|
||||
var __hasProp = {}.hasOwnProperty,
|
||||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
|
||||
|
||||
define(['marionette', 'views/comment/commentItemView', 'text!/tpl/CommentListView'], function(Marionette, CommentItemView, Template) {
|
||||
var CommentListView;
|
||||
CommentListView = (function(_super) {
|
||||
define(['marionette', 'models/comment/commentItem', 'views/comment/commentItemView', 'text!/tpl/CommentListView'], function(Marionette, CommentItem, CommentItemView, Template) {
|
||||
var CommentListView, _ref;
|
||||
|
||||
CommentListView = (function(_super) {
|
||||
__extends(CommentListView, _super);
|
||||
|
||||
function CommentListView() {
|
||||
return CommentListView.__super__.constructor.apply(this, arguments);
|
||||
_ref = CommentListView.__super__.constructor.apply(this, arguments);
|
||||
return _ref;
|
||||
}
|
||||
|
||||
CommentListView.prototype.template = _.template(Template);
|
||||
@@ -23,10 +24,40 @@
|
||||
|
||||
CommentListView.prototype.itemViewContainer = "#comment-list-container";
|
||||
|
||||
CommentListView.prototype.ui = {
|
||||
commentText: '#comment-text'
|
||||
};
|
||||
|
||||
CommentListView.prototype.events = {
|
||||
"click #btn-add-comment": "addComment"
|
||||
};
|
||||
|
||||
CommentListView.prototype.initialize = function() {
|
||||
return console.log("CommentListView: initialize");
|
||||
};
|
||||
|
||||
CommentListView.prototype.addComment = function() {
|
||||
var _this = this;
|
||||
|
||||
console.log("CommentListView: addComment");
|
||||
this.collection.create({
|
||||
mix_id: this.collection.mix.get("id"),
|
||||
comment: this.ui.commentText.val()
|
||||
}, {
|
||||
success: function(newItem) {
|
||||
_this.ui.commentText.val("");
|
||||
return true;
|
||||
},
|
||||
error: function(a, b, c) {
|
||||
console.log(a);
|
||||
console.log(b);
|
||||
console.log(c);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
};
|
||||
|
||||
return CommentListView;
|
||||
|
||||
})(Marionette.CompositeView);
|
||||
|
||||
@@ -56,7 +56,7 @@ define ['moment', 'app', 'vent', 'marionette', 'utils', 'models/comment/commentC
|
||||
comments = new CommentsCollection()
|
||||
comments.url = @model.get("resource_uri") + "comments/"
|
||||
comments.mix_id = @model.id
|
||||
comments.mix = @model.get("resource_uri")
|
||||
comments.mix = @model
|
||||
comments.fetch success: (data) ->
|
||||
console.log(data)
|
||||
content = new CommentsListView(collection: comments).render()
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
comments = new CommentsCollection();
|
||||
comments.url = this.model.get("resource_uri") + "comments/";
|
||||
comments.mix_id = this.model.id;
|
||||
comments.mix = this.model.get("resource_uri");
|
||||
comments.mix = this.model;
|
||||
comments.fetch({
|
||||
success: function(data) {
|
||||
var content;
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
// Generated by CoffeeScript 1.3.3
|
||||
// Generated by CoffeeScript 1.6.2
|
||||
(function() {
|
||||
var __hasProp = {}.hasOwnProperty,
|
||||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
|
||||
|
||||
define(['marionette', 'vent', 'models/user/userItem', 'views/widgets/mixTabHeaderView', 'views/user/userItemView', 'views/mix/mixListView', 'text!/tpl/MixListLayoutView'], function(Marionette, vent, UserItem, MixTabHeaderView, UserItemView, MixListView, Template) {
|
||||
var MixListRegionView;
|
||||
MixListRegionView = (function(_super) {
|
||||
var MixListRegionView, _ref;
|
||||
|
||||
MixListRegionView = (function(_super) {
|
||||
__extends(MixListRegionView, _super);
|
||||
|
||||
function MixListRegionView() {
|
||||
return MixListRegionView.__super__.constructor.apply(this, arguments);
|
||||
_ref = MixListRegionView.__super__.constructor.apply(this, arguments);
|
||||
return _ref;
|
||||
}
|
||||
|
||||
MixListRegionView.prototype.template = _.template(Template);
|
||||
@@ -36,6 +37,7 @@
|
||||
MixListRegionView.prototype.showUserView = function(options) {
|
||||
var user,
|
||||
_this = this;
|
||||
|
||||
this.bodyRegion.show(new MixListView(options));
|
||||
user = new UserItem({
|
||||
id: options.user
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* vim: set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab: */
|
||||
/**
|
||||
* Backbone-relational.js 0.8.5
|
||||
* Backbone-relational.js 0.8.6
|
||||
* (c) 2011-2013 Paul Uithol and contributors (https://github.com/PaulUithol/Backbone-relational/graphs/contributors)
|
||||
*
|
||||
* Backbone-relational may be freely distributed under the MIT license; see the accompanying LICENSE.txt.
|
||||
@@ -17,7 +17,8 @@
|
||||
if ( typeof window === 'undefined' ) {
|
||||
_ = require( 'underscore' );
|
||||
Backbone = require( 'backbone' );
|
||||
exports = module.exports = Backbone;
|
||||
exports = Backbone;
|
||||
typeof module === 'undefined' || ( module.exports = exports );
|
||||
}
|
||||
else {
|
||||
_ = window._;
|
||||
@@ -86,9 +87,21 @@
|
||||
}
|
||||
},
|
||||
|
||||
// Some of the queued events may trigger other blocking events. By
|
||||
// copying the queue here it allows queued events to process closer to
|
||||
// the natural order.
|
||||
//
|
||||
// queue events [ 'A', 'B', 'C' ]
|
||||
// A handler of 'B' triggers 'D' and 'E'
|
||||
// By copying `this._queue` this executes:
|
||||
// [ 'A', 'B', 'D', 'E', 'C' ]
|
||||
// The same order the would have executed if they didn't have to be
|
||||
// delayed and queued.
|
||||
process: function() {
|
||||
while ( this._queue && this._queue.length ) {
|
||||
this._queue.shift()();
|
||||
var queue = this._queue;
|
||||
this._queue = [];
|
||||
while ( queue && queue.length ) {
|
||||
queue.shift()();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -298,7 +311,9 @@
|
||||
rootModel = rootModel._superModel;
|
||||
}
|
||||
|
||||
var coll = _.findWhere( this._collections, { model: rootModel } );
|
||||
var coll = _.find( this._collections, function(item) {
|
||||
return item.model === rootModel;
|
||||
});
|
||||
|
||||
if ( !coll && create !== false ) {
|
||||
coll = this._createCollection( rootModel );
|
||||
@@ -407,6 +422,7 @@
|
||||
var modelColl = model.collection;
|
||||
coll.add( model );
|
||||
this.listenTo( model, 'destroy', this.unregister, this );
|
||||
this.listenTo( model, 'relational:unregister', this.unregister, this );
|
||||
model.collection = modelColl;
|
||||
}
|
||||
},
|
||||
@@ -446,10 +462,10 @@
|
||||
* Remove a 'model' from the store.
|
||||
* @param {Backbone.RelationalModel} model
|
||||
*/
|
||||
unregister: function( model ) {
|
||||
this.stopListening( model, 'destroy', this.unregister );
|
||||
unregister: function( model, collection, options ) {
|
||||
this.stopListening( model );
|
||||
var coll = this.getCollection( model );
|
||||
coll && coll.remove( model );
|
||||
coll && coll.remove( model, options );
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -496,7 +512,12 @@
|
||||
this.keyDestination = this.options.keyDestination || this.keySource || this.key;
|
||||
|
||||
this.model = this.options.model || this.instance.constructor;
|
||||
|
||||
this.relatedModel = this.options.relatedModel;
|
||||
|
||||
if ( _.isFunction( this.relatedModel ) && !( this.relatedModel.prototype instanceof Backbone.RelationalModel ) ) {
|
||||
this.relatedModel = _.result( this, 'relatedModel' );
|
||||
}
|
||||
if ( _.isString( this.relatedModel ) ) {
|
||||
this.relatedModel = Backbone.Relational.store.getObjectByName( this.relatedModel );
|
||||
}
|
||||
@@ -528,7 +549,7 @@
|
||||
|
||||
// Explicitly clear 'keySource', to prevent a leaky abstraction if 'keySource' differs from 'key'.
|
||||
if ( this.keySource !== this.key ) {
|
||||
this.instance.unset( this.keySource, { silent: true } );
|
||||
delete this.instance.attributes[ this.keySource ];
|
||||
}
|
||||
|
||||
// Add this Relation to instance._relations
|
||||
@@ -829,6 +850,9 @@
|
||||
|
||||
// Handle a custom 'collectionType'
|
||||
this.collectionType = this.options.collectionType;
|
||||
if ( _.isFunction( this.collectionType ) && this.collectionType !== Backbone.Collection && !( this.collectionType.prototype instanceof Backbone.Collection ) ) {
|
||||
this.collectionType = _.result( this, 'collectionType' );
|
||||
}
|
||||
if ( _.isString( this.collectionType ) ) {
|
||||
this.collectionType = Backbone.Relational.store.getObjectByName( this.collectionType );
|
||||
}
|
||||
@@ -1064,6 +1088,7 @@
|
||||
_isInitialized: false,
|
||||
_deferProcessing: false,
|
||||
_queue: null,
|
||||
_attributeChangeFired: false, // Keeps track of `change` event firing under some conditions (like nested `set`s)
|
||||
|
||||
subModelTypeAttribute: 'type',
|
||||
subModelTypes: null,
|
||||
@@ -1128,7 +1153,9 @@
|
||||
// Determine if the `change` event is still valid, now that all relations are populated
|
||||
var changed = true;
|
||||
if ( eventName === 'change' ) {
|
||||
changed = dit.hasChanged();
|
||||
// `hasChanged` may have gotten reset by nested calls to `set`.
|
||||
changed = dit.hasChanged() || dit._attributeChangeFired;
|
||||
dit._attributeChangeFired = false;
|
||||
}
|
||||
else {
|
||||
var attr = eventName.slice( 7 ),
|
||||
@@ -1137,7 +1164,7 @@
|
||||
if ( rel ) {
|
||||
// If `attr` is a relation, `change:attr` get triggered from `Relation.onChange`.
|
||||
// These take precedence over `change:attr` events triggered by `Model.set`.
|
||||
// The relation set a fourth attribute to `true`. If this attribute is present,
|
||||
// The relation sets a fourth attribute to `true`. If this attribute is present,
|
||||
// continue triggering this event; otherwise, it's from `Model.set` and should be stopped.
|
||||
changed = ( args[ 4 ] === true );
|
||||
|
||||
@@ -1152,6 +1179,9 @@
|
||||
delete dit.changed[ attr ];
|
||||
}
|
||||
}
|
||||
else if ( changed ) {
|
||||
dit._attributeChangeFired = true;
|
||||
}
|
||||
}
|
||||
|
||||
changed && Backbone.Model.prototype.trigger.apply( dit, args );
|
||||
@@ -1172,7 +1202,7 @@
|
||||
this.acquire(); // Setting up relations often also involve calls to 'set', and we only want to enter this function once
|
||||
this._relations = {};
|
||||
|
||||
_.each( this.relations || [], function( rel ) {
|
||||
_.each( _.result( this, 'relations' ) || [], function( rel ) {
|
||||
Backbone.Relational.store.initializeRelation( this, rel, options );
|
||||
}, this );
|
||||
|
||||
@@ -1188,11 +1218,16 @@
|
||||
updateRelations: function( options ) {
|
||||
if ( this._isInitialized && !this.isLocked() ) {
|
||||
_.each( this._relations, function( rel ) {
|
||||
// Update from data in `rel.keySource` if set, or `rel.key` otherwise
|
||||
// Update from data in `rel.keySource` if data got set in there, or `rel.key` otherwise
|
||||
var val = this.attributes[ rel.keySource ] || this.attributes[ rel.key ];
|
||||
if ( rel.related !== val ) {
|
||||
this.trigger( 'relational:change:' + rel.key, this, val, options || {} );
|
||||
}
|
||||
|
||||
// Explicitly clear 'keySource', to prevent a leaky abstraction if 'keySource' differs from 'key'.
|
||||
if ( rel.keySource !== rel.key ) {
|
||||
delete rel.instance.attributes[ rel.keySource ];
|
||||
}
|
||||
}, this );
|
||||
}
|
||||
},
|
||||
@@ -1244,7 +1279,7 @@
|
||||
var setUrl,
|
||||
requests = [],
|
||||
rel = this.getRelation( key ),
|
||||
idsToFetch = rel && ( rel.keyIds || ( ( rel.keyId || rel.keyId === 0 ) ? [ rel.keyId ] : [] ) );
|
||||
idsToFetch = rel && ( ( rel.keyIds && rel.keyIds.slice( 0 ) ) || ( ( rel.keyId || rel.keyId === 0 ) ? [ rel.keyId ] : [] ) );
|
||||
|
||||
// On `refresh`, add the ids for current models in the relation to `idsToFetch`
|
||||
if ( refresh ) {
|
||||
@@ -1329,11 +1364,19 @@
|
||||
// Go through all splits and return the final result
|
||||
var splits = attr.split( '.' );
|
||||
var result = _.reduce(splits, function( model, split ) {
|
||||
if ( !( model instanceof Backbone.Model ) ) {
|
||||
throw new Error( 'Attribute must be an instanceof Backbone.Model. Is: ' + model + ', currentSplit: ' + split );
|
||||
if ( _.isNull(model) || _.isUndefined( model ) ) {
|
||||
// Return undefined if the path cannot be expanded
|
||||
return undefined;
|
||||
}
|
||||
else if ( model instanceof Backbone.Model ) {
|
||||
return Backbone.Model.prototype.get.call( model, split );
|
||||
}
|
||||
else if ( model instanceof Backbone.Collection ) {
|
||||
return Backbone.Collection.prototype.at.call( model, split )
|
||||
}
|
||||
else {
|
||||
throw new Error( 'Attribute must be an instanceof Backbone.Model or Backbone.Collection. Is: ' + model + ', currentSplit: ' + split );
|
||||
}
|
||||
|
||||
return Backbone.Model.prototype.get.call( model, split );
|
||||
}, this );
|
||||
|
||||
if ( originalResult !== undefined && result !== undefined ) {
|
||||
@@ -1389,30 +1432,6 @@
|
||||
return result;
|
||||
},
|
||||
|
||||
unset: function( attribute, options ) {
|
||||
Backbone.Relational.eventQueue.block();
|
||||
|
||||
var result = Backbone.Model.prototype.unset.apply( this, arguments );
|
||||
this.updateRelations( options );
|
||||
|
||||
// Try to run the global queue holding external events
|
||||
Backbone.Relational.eventQueue.unblock();
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
clear: function( options ) {
|
||||
Backbone.Relational.eventQueue.block();
|
||||
|
||||
var result = Backbone.Model.prototype.clear.apply( this, arguments );
|
||||
this.updateRelations( options );
|
||||
|
||||
// Try to run the global queue holding external events
|
||||
Backbone.Relational.eventQueue.unblock();
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
clone: function() {
|
||||
var attributes = _.clone( this.attributes );
|
||||
if ( !_.isUndefined( attributes[ this.idAttribute ] ) ) {
|
||||
@@ -1569,57 +1588,92 @@
|
||||
* @return {Backbone.Model}
|
||||
*/
|
||||
build: function( attributes, options ) {
|
||||
var model = this;
|
||||
|
||||
// 'build' is a possible entrypoint; it's possible no model hierarchy has been determined yet.
|
||||
this.initializeModelHierarchy();
|
||||
|
||||
// Determine what type of (sub)model should be built if applicable.
|
||||
// Lookup the proper subModelType in 'this._subModels'.
|
||||
if ( this._subModels && this.prototype.subModelTypeAttribute in attributes ) {
|
||||
var subModelTypeAttribute = attributes[ this.prototype.subModelTypeAttribute ];
|
||||
var subModelType = this._subModels[ subModelTypeAttribute ];
|
||||
if ( subModelType ) {
|
||||
model = subModelType;
|
||||
}
|
||||
}
|
||||
var model = this._findSubModelType(this, attributes) || this;
|
||||
|
||||
return new model( attributes, options );
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines what type of (sub)model should be built if applicable.
|
||||
* Looks up the proper subModelType in 'this._subModels', recursing into
|
||||
* types until a match is found. Returns the applicable 'Backbone.Model'
|
||||
* or null if no match is found.
|
||||
* @param {Backbone.Model} type
|
||||
* @param {Object} attributes
|
||||
* @return {Backbone.Model}
|
||||
*/
|
||||
_findSubModelType: function (type, attributes) {
|
||||
if ( type._subModels && type.prototype.subModelTypeAttribute in attributes ) {
|
||||
var subModelTypeAttribute = attributes[type.prototype.subModelTypeAttribute];
|
||||
var subModelType = type._subModels[subModelTypeAttribute];
|
||||
if ( subModelType ) {
|
||||
return subModelType;
|
||||
} else {
|
||||
// Recurse into subModelTypes to find a match
|
||||
for ( subModelTypeAttribute in type._subModels ) {
|
||||
subModelType = this._findSubModelType(type._subModels[subModelTypeAttribute], attributes);
|
||||
if ( subModelType ) {
|
||||
return subModelType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
initializeModelHierarchy: function() {
|
||||
// If we're here for the first time, try to determine if this modelType has a 'superModel'.
|
||||
if ( _.isUndefined( this._superModel ) || _.isNull( this._superModel ) ) {
|
||||
Backbone.Relational.store.setupSuperModel( this );
|
||||
|
||||
// If a superModel has been found, copy relations from the _superModel if they haven't been
|
||||
// inherited automatically (due to a redefinition of 'relations').
|
||||
// Otherwise, make sure we don't get here again for this type by making '_superModel' false so we fail
|
||||
// the isUndefined/isNull check next time.
|
||||
if ( this._superModel && this._superModel.prototype.relations ) {
|
||||
// Find relations that exist on the `_superModel`, but not yet on this model.
|
||||
// Inherit any relations that have been defined in the parent model.
|
||||
this.inheritRelations();
|
||||
|
||||
// If we came here through 'build' for a model that has 'subModelTypes' then try to initialize the ones that
|
||||
// haven't been resolved yet.
|
||||
if ( this.prototype.subModelTypes ) {
|
||||
var resolvedSubModels = _.keys(this._subModels);
|
||||
var unresolvedSubModels = _.omit(this.prototype.subModelTypes, resolvedSubModels);
|
||||
_.each( unresolvedSubModels, function( subModelTypeName ) {
|
||||
var subModelType = Backbone.Relational.store.getObjectByName( subModelTypeName );
|
||||
subModelType && subModelType.initializeModelHierarchy();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
inheritRelations: function() {
|
||||
// Bail out if we've been here before.
|
||||
if (!_.isUndefined( this._superModel ) && !_.isNull( this._superModel )) {
|
||||
return;
|
||||
}
|
||||
// Try to initialize the _superModel.
|
||||
Backbone.Relational.store.setupSuperModel( this );
|
||||
|
||||
// If a superModel has been found, copy relations from the _superModel if they haven't been inherited automatically
|
||||
// (due to a redefinition of 'relations').
|
||||
if ( this._superModel ) {
|
||||
// The _superModel needs a chance to initialize its own inherited relations before we attempt to inherit relations
|
||||
// from the _superModel. You don't want to call 'initializeModelHierarchy' because that could cause sub-models of
|
||||
// this class to inherit their relations before this class has had chance to inherit it's relations.
|
||||
this._superModel.inheritRelations();
|
||||
if ( this._superModel.prototype.relations ) {
|
||||
// Find relations that exist on the '_superModel', but not yet on this model.
|
||||
var inheritedRelations = _.select( this._superModel.prototype.relations || [], function( superRel ) {
|
||||
return !_.any( this.prototype.relations || [], function( rel ) {
|
||||
return superRel.relatedModel === rel.relatedModel && superRel.key === rel.key;
|
||||
}, this );
|
||||
}, this );
|
||||
|
||||
|
||||
this.prototype.relations = inheritedRelations.concat( this.prototype.relations );
|
||||
}
|
||||
else {
|
||||
this._superModel = false;
|
||||
}
|
||||
}
|
||||
|
||||
// If we came here through 'build' for a model that has 'subModelTypes', and not all of them have been resolved yet, try to resolve each.
|
||||
if ( this.prototype.subModelTypes && _.keys( this.prototype.subModelTypes ).length !== _.keys( this._subModels ).length ) {
|
||||
_.each( this.prototype.subModelTypes || [], function( subModelTypeName ) {
|
||||
var subModelType = Backbone.Relational.store.getObjectByName( subModelTypeName );
|
||||
subModelType && subModelType.initializeModelHierarchy();
|
||||
});
|
||||
// Otherwise, make sure we don't get here again for this type by making '_superModel' false so we fail the
|
||||
// isUndefined/isNull check next time.
|
||||
else {
|
||||
this._superModel = false;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1638,7 +1692,7 @@
|
||||
findOrCreate: function( attributes, options ) {
|
||||
options || ( options = {} );
|
||||
var parsedAttributes = ( _.isObject( attributes ) && options.parse && this.prototype.parse ) ?
|
||||
this.prototype.parse( attributes ) : attributes;
|
||||
this.prototype.parse( _.clone( attributes ) ) : attributes;
|
||||
|
||||
// Try to find an instance of 'this' model type in the store
|
||||
var model = Backbone.Relational.store.find( this, parsedAttributes );
|
||||
@@ -1647,8 +1701,9 @@
|
||||
// If not, create an instance (unless 'options.create' is false).
|
||||
if ( _.isObject( attributes ) ) {
|
||||
if ( model && options.merge !== false ) {
|
||||
// Make sure `options.collection` doesn't cascade to nested models
|
||||
// Make sure `options.collection` and `options.url` doesn't cascade to nested models
|
||||
delete options.collection;
|
||||
delete options.url;
|
||||
|
||||
model.set( parsedAttributes, options );
|
||||
}
|
||||
@@ -1658,6 +1713,22 @@
|
||||
}
|
||||
|
||||
return model;
|
||||
},
|
||||
|
||||
/**
|
||||
* Find an instance of `this` type in 'Backbone.Relational.store'.
|
||||
* - If `attributes` is a string or a number, `find` will just query the `store` and return a model if found.
|
||||
* - If `attributes` is an object and is found in the store, the model will be updated with `attributes` unless `options.update` is `false`.
|
||||
* @param {Object|String|Number} attributes Either a model's id, or the attributes used to create or update a model.
|
||||
* @param {Object} [options]
|
||||
* @param {Boolean} [options.merge=true]
|
||||
* @param {Boolean} [options.parse=false]
|
||||
* @return {Backbone.RelationalModel}
|
||||
*/
|
||||
find: function( attributes, options ) {
|
||||
options || ( options = {} );
|
||||
options.create = false;
|
||||
return this.findOrCreate( attributes, options );
|
||||
}
|
||||
});
|
||||
_.extend( Backbone.RelationalModel.prototype, Backbone.Semaphore );
|
||||
@@ -1765,14 +1836,14 @@
|
||||
return remove.apply( this, arguments );
|
||||
}
|
||||
|
||||
models = _.isArray( models ) ? models.slice() : [ models ];
|
||||
models = _.isArray( models ) ? models.slice( 0 ) : [ models ];
|
||||
options || ( options = {} );
|
||||
|
||||
var toRemove = [];
|
||||
|
||||
//console.debug('calling remove on coll=%o; models=%o, options=%o', this, models, options );
|
||||
_.each( models, function( model ) {
|
||||
model = this.get( model ) || this.get( model.cid );
|
||||
model = this.get( model ) || ( model && this.get( model.cid ) );
|
||||
model && toRemove.push( model );
|
||||
}, this );
|
||||
|
||||
@@ -1827,7 +1898,7 @@
|
||||
return trigger.apply( this, arguments );
|
||||
}
|
||||
|
||||
if ( eventName === 'add' || eventName === 'remove' || eventName === 'reset' ) {
|
||||
if ( eventName === 'add' || eventName === 'remove' || eventName === 'reset' || eventName === 'sort' ) {
|
||||
var dit = this,
|
||||
args = arguments;
|
||||
|
||||
|
||||
@@ -64,6 +64,10 @@ requirejs.config({
|
||||
exports: 'Backbone',
|
||||
deps: ['jquery', 'underscore']
|
||||
},
|
||||
'backbone.relational': {
|
||||
exports: 'Backbone',
|
||||
deps: ['backbone']
|
||||
},
|
||||
bootstrap: {
|
||||
exports: 'bootstrap',
|
||||
deps: ['jquery']
|
||||
|
||||
@@ -15,6 +15,9 @@ com.podnoms.settings = {
|
||||
staticUrl: '{{ STATIC_URL }}',
|
||||
urlArgs: {{ IS_DEBUG }} ? "" : "bust="+ (new Date()).getTime(),
|
||||
currentUser: {{ CURRENT_USER_ID }},
|
||||
userName: "{{ CURRENT_USER_NAME }}",
|
||||
userUrl: "{{ CURRENT_USER_URL }}",
|
||||
avatarImage: "{{ AVATAR_IMAGE }}",
|
||||
/** simple helper to take an api JSON object and initialise a player item */
|
||||
setupPlayerWrapper: function (id, stream_url, el) {
|
||||
com.podnoms.player.setupPlayer({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% if request.user.is_authenticated %}
|
||||
<div class="comment-submit-area well">
|
||||
<textarea style="width: 85%" id="id-comment-text" cols="80" rows="3" name="new-comment"></textarea>
|
||||
<button class="btn btn-primary" id="id-btn-add-comment">Add comment (don't be a dick)</button>
|
||||
<textarea style="width: 85%" id="comment-text" cols="80" rows="3" name="new-comment"></textarea>
|
||||
<button class="btn btn-primary" id="btn-add-comment">Add comment (don't be a dick)</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
<h3 class="bordered">Comments</h3>
|
||||
|
||||
@@ -111,14 +111,19 @@
|
||||
class="icon-edit"></i>Edit</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<% if (download_allowed) { %>
|
||||
{% if user.is_authenticated %}
|
||||
<% if (download_allowed) { %>
|
||||
<div class="download-button footer-button-right">
|
||||
<a class="btn btn-mini btn-success" data-id="<%= id %>">
|
||||
<i class="icon-download"></i> Download</a>
|
||||
</div>
|
||||
<% } %>
|
||||
{% else %}
|
||||
<div class="footer-button-right">
|
||||
<a class="btn btn-mini btn-success disabled" data-id="<%= id %>">
|
||||
<i class="icon-download"></i> Login to download</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user