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()
+ }
+
+})();
+
+