Author: mfranklin
Date: Thu Mar 14 18:36:35 2013
New Revision: 1456608

URL: http://svn.apache.org/r1456608
Log:
Applying the rest of Erin's patch for RAVE-891

Added:
    
rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_backbone.js
    
rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_models.js

Added: 
rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_backbone.js
URL: 
http://svn.apache.org/viewvc/rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_backbone.js?rev=1456608&view=auto
==============================================================================
--- 
rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_backbone.js 
(added)
+++ 
rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_backbone.js 
Thu Mar 14 18:36:35 2013
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var rave = rave || {};
+
+/*
+ Extend backbone's standard model and collection with some
+ */
+rave.Model = Backbone.Model.extend({
+    get: function(attr){
+        //tweak model get so that array / object members are passed by value 
instead of reference
+        //needed for managing deep objects
+        return _.clone(this.attributes[attr]);
+    },
+
+    /*
+     Overridable function that models can implement for serializing themselves 
for view rendering,
+     since often a handlebars template needs explicit keys or booleans that 
don't make sense
+     in a normal json representation of the model. By default will just return 
toJSON().
+     */
+    toViewModel: function () {
+        return this.toJSON();
+    }
+});
+
+rave.Collection = Backbone.Collection.extend({
+    toViewModel: function () {
+        return this.map(function (model) {
+            return model.toViewModel();
+        });
+    }
+})
+
+
+/*
+ rave.View is an extension of Backbone's view with some scaffolding put in 
place. The expectation is that a view
+ will be declared with a hash of models (models or collections) that will be 
merged and fed to the view at render
+ time. By default on any change to the models the view will be re-rendered. 
Also provides an implementation of
+ render that probably will not need to be overrridden.
+ */
+rave.View = Backbone.View.extend({
+    initialize: function () {
+        var self = this;
+        _.bindAll(this);
+
+        _.each(this.models, function (model) {
+            model.on('change', self.render);
+            model.on('reset', self.render);
+        });
+    },
+    render: function () {
+        var template = this.template;
+
+        var viewData = {};
+        _.each(this.models, function (model, key) {
+            viewData[key] = model.toViewModel();
+        });
+
+        this.$el.html(template(viewData));
+        return this;
+    }
+});
\ No newline at end of file

Added: 
rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_models.js
URL: 
http://svn.apache.org/viewvc/rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_models.js?rev=1456608&view=auto
==============================================================================
--- 
rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_models.js 
(added)
+++ 
rave/trunk/rave-portal-resources/src/main/webapp/static/script/rave_models.js 
Thu Mar 14 18:36:35 2013
@@ -0,0 +1,277 @@
+var rave = rave || {};
+
+rave.models = (function () {
+
+    /*
+     User model. Further implementation pending.
+     */
+    var User = rave.Model.extend({
+
+    });
+
+    /*
+     Collection of users. Currently used for the share page users search.
+     */
+    var Users = rave.Collection.extend({
+        model: User,
+        pageSize: 10,
+
+        //empty pagination data, is filled in on requests
+        paginationData: {
+            start:0,
+            finish: 0,
+            total: 0,
+            prevLink: {},
+            nextLink: {},
+            pages: []
+        },
+
+        initialize: function(){
+            //ensure that parse is always invoked in the context of this object
+            _.bindAll(this, 'parse');
+        },
+
+        //filter collection with a search term
+        filter: function (term) {
+            this.searchTerm = term;
+
+            if (this.searchTerm) {
+                rave.api.rpc.searchUsers({searchTerm: this.searchTerm, offset: 
0, successCallback: this.parse });
+            }
+            else {
+                rave.api.rpc.getUsers({offset: 0, successCallback: this.parse 
});
+            }
+        },
+
+        //used for pagination
+        fetchPage: function (page) {
+            var self = this;
+
+            var offset = page?(page-1):0;
+            offset *= this.pageSize;
+
+            if (this.searchTerm) {
+                rave.api.rpc.searchUsers({searchTerm: this.searchTerm, offset: 
offset, successCallback: this.parse });
+            }
+            else {
+                rave.api.rpc.getUsers({offset: offset, successCallback: 
this.parse });
+            }
+        },
+
+        //parse return data from the rpc call into a usable data model
+        parse: function (data) {
+            var result = data.result;
+            this.pageSize = result.pageSize || 10;
+
+            this.paginationData = {
+                start: result.offset + 1,
+                finish: result.resultSet.length + result.offset,
+                total: result.totalResults,
+                pageSize: result.pageSize,
+                prevLink: {
+                    show: result.currentPage > 1 ? true : false,
+                    pageNumber: result.currentPage - 1
+                },
+                nextLink: {
+                    show: result.currentPage < result.numberOfPages ? true : 
false,
+                    pageNumber: result.currentPage + 1
+                },
+                //pages will be an array of objects from 1 to number of pages
+                pages: _.map(_.range(1, result.numberOfPages + 1), function 
(pageNumber) {
+                    return {
+                        pageNumber: pageNumber,
+                        current: pageNumber == result.currentPage
+                    }
+                })
+            }
+
+            this.reset(result.resultSet);
+        },
+
+        //When toViewModel is invoked, also provide pagination and filter data
+        toViewModel: function () {
+            return {
+                searchTerm: this.searchTerm,
+                pagination: this.paginationData,
+                users: this.constructor.__super__.toViewModel.apply(this)
+            }
+        }
+    });
+
+    /*
+     Page model. Used for managing most of the sharing functionality.
+     */
+    var Page = rave.Model.extend({
+
+        defaults: {
+            members: {}
+        },
+
+        /*
+         TODO: currently this is used to silently bootstrap the page model 
from the page view. Once
+         the jsp views are lightened up we should be able to provide a full 
representation of the page
+         model to pass to .set() and this should not be needed.
+         */
+        addInitData: function (userId, isEditor) {
+            var members = this.get('members');
+
+            members[userId] = {
+                userId: userId,
+                editor: isEditor
+            }
+
+            this.set('members', members, {silent:true});
+        },
+
+        isUserOwner: function (userId) {
+            return userId == this.get('ownerId');
+        },
+
+        isUserView: function(userId) {
+            return userId == this.get('viewerId');
+        },
+
+        isUserMember: function (userId) {
+            return this.get('members')[userId] ? true : false;
+        },
+
+        isUserEditor: function (userId) {
+            var member = this.get('members')[userId];
+            return member && member.editor;
+        },
+
+        addMember: function (userId) {
+            var self = this;
+
+            rave.api.rpc.addMemberToPage({pageId: self.get('id'), userId: 
userId,
+                successCallback: function (result) {
+                    var members = self.get('members');
+
+                    members[userId] = {
+                        userId: userId,
+                        editor: false
+                    }
+
+                    self.set('members', members);
+                    /*
+                     The model does not manage or care about views. Instead it 
fires events
+                     that views can subscribe to for ui representation.
+                     TODO: solidify and document eventing model
+                     */
+                    self.trigger('share', 'member:add', userId);
+                }
+            });
+
+        },
+
+        removeMember: function (userId) {
+            var self = this;
+
+            rave.api.rpc.removeMemberFromPage({pageId: self.get('id'), userId: 
userId,
+                successCallback: function (result) {
+                    var members = self.get('members');
+
+                    delete members[userId];
+
+                    self.set('members', members);
+                    self.trigger('share', 'member:remove', userId);
+                }
+            });
+        },
+
+        removeForSelf: function(){
+            var self = this;
+
+            rave.api.rpc.removeMemberFromPage({pageId: self.get('id'), userId: 
self.get('viewerId'),
+                successCallback: function () {
+                    self.trigger('declineShare', self.get('id'));
+                }
+            });
+        },
+
+        addEditor: function (userId) {
+            var self = this;
+            //updatePageEditingStatus
+            rave.api.rpc.updatePageEditingStatus({pageId: self.get('id'), 
userId: userId, isEditor: true,
+                successCallback: function () {
+                    var members = self.get('members');
+
+                    members[userId] = {
+                        userId: userId,
+                        editor: true
+                    }
+
+                    self.set('members', members);
+                    self.trigger('share', 'editor:add', userId);
+                }
+            });
+        },
+
+        removeEditor: function (userId) {
+            var self = this;
+            rave.api.rpc.updatePageEditingStatus({pageId: self.get('id'), 
userId: userId, isEditor: false,
+                successCallback: function () {
+
+                    var members = self.get('members');
+
+                    members[userId] = {
+                        userId: userId,
+                        editor: false
+                    }
+
+                    self.set('members', members);
+                    self.trigger('share', 'editor:remove', userId);
+                }
+            });
+        },
+
+        cloneForUser: function (userId, pageName) {
+            pageName = pageName || null;
+            var self =this;
+            rave.api.rpc.clonePageForUser({pageId: this.get('id'), userId: 
userId, pageName: pageName,
+                successCallback: function(result){
+                    if(result.error) {
+                        /*
+                         TODO: this is a weird error handling condition used 
by clone to catch duplicate
+                         named pages. Firing an event and letting the view 
handle for now, but the api
+                         should be managing errors better.
+                         */
+                        return self.trigger('error', result.errorCode, userId);
+                    }
+                    self.trigger('share', 'clone', userId);
+                }
+            });
+        },
+
+        acceptShare: function(){
+            var self = this;
+            rave.api.rpc.updateSharedPageStatus({pageId: this.get('id'), 
shareStatus: 'accepted',
+                successCallback: function (result) {
+                    self.trigger('acceptShare', self.get('id'));
+                }
+            });
+        },
+
+        declineShare: function(){
+            var self = this;
+
+            rave.api.rpc.updateSharedPageStatus({pageId: this.get('id'), 
shareStatus: 'refused',
+                successCallback: function (result) {
+                    rave.api.rpc.removeMemberFromPage({pageId: self.get('id'), 
userId: self.get('viewerId'),
+                        successCallback: function (result) {
+                            self.trigger('declineShare', self.get('id'));
+                        }
+                    });
+                }
+            })
+        }
+    });
+
+    return {
+        currentPage: new Page(),
+        users: new Users()
+    }
+
+})();
+
+


Reply via email to