Commit 6c3234a3 authored by Marcel's avatar Marcel
Browse files

Re-open extensions when connecting to a workspace.

parent 15cff25c
......@@ -35,9 +35,12 @@ class AbstractController(object):
setattr(self, key, value)
self._object_cache = {}
def success(self, data={}, encode_json=False):
def success(self, data=None, encode_json=False):
if encode_json:
cherrypy.response.headers['Content-Type'] = 'application/json'
if data is None:
result = {'success': True}
return result if not encode_json else json.dumps(result)
if isinstance(data, dict):
result = {'success': True}
result.update(data)
......@@ -87,7 +90,6 @@ class AbstractController(object):
# a little unicode helper
def convert(self, value, flag):
flag = flag.lower()
# convet lists, tuples and dicts by pseudo recursion
if isinstance(value, list):
return map(lambda elem: self.convert(elem, flag), value)
......@@ -99,10 +101,10 @@ class AbstractController(object):
return dict(zip(keys, values))
# string
if flag in ['s', 'str', 'string']:
if flag == str or str(flag).lower() in ['s', 'str', 'string']:
return str(value)
# boolean
if flag in ['b', 'bool', 'boolean']:
if flag == bool or str(flag).lower() in ['b', 'bool', 'boolean']:
value = str(value).lower()
if value == 'true':
return True
......@@ -112,14 +114,14 @@ class AbstractController(object):
raise Exception("TypeError: boolean conversion expects 'true' or 'false' (string).")
return None
# integer
if flag in ['i', 'int', 'integer', 'l', 'long']:
if flag == int or str(flag).lower() in ['i', 'int', 'integer', 'l', 'long']:
value = str(value)
try:
return int(value)
except:
raise Exception('TypeError: conversion to integer received bad argument.')
# float
if flag in ['f', 'float', 'd', 'double']:
if flag == float or str(flag).lower() in ['f', 'float', 'd', 'double']:
value = str(value)
try:
return float(value)
......
......@@ -7,12 +7,13 @@ from string import Template
from vispa.controller import AbstractController
from vispa.controller.filesystem import FSAjaxController
from vispa.models.user import User
from vispa.models.workspace import Workspace
from vispa.models.workspace import Workspace, WorkspaceState
from vispa.models.profile import Profile
from vispa.models.preference import VispaPreference, ExtensionPreference
from vispa.helpers import browser
import vispa
import logging
import json
logger = logging.getLogger(__name__)
......@@ -210,27 +211,6 @@ class AjaxController(AbstractController):
except Exception, e:
return self.fail(msg=str(e))
@cherrypy.expose
@cherrypy.tools.user()
@cherrypy.tools.workspace(on=False)
@cherrypy.tools.allow(methods=['POST'])
@cherrypy.tools.json_out()
def selectworkspace(self, wid, preload=True):
try:
# is workspace owned by the user?
workspace = Workspace.get_by_id(cherrypy.request.db, wid)
if not isinstance(workspace, Workspace):
raise Exception('Unknown Workspace!')
if workspace.user_id and workspace.user_id != self.get('user_id'):
raise Exception('No permission to select this Workspace!')
# pre-load the fs which is a connection test at the same time
if self.convert(preload, 'boolean'):
self.get('fs', workspace)
cherrypy.session['workspace_id'] = wid
return self.success()
except Exception, e:
return self.fail(msg=str(e))
@cherrypy.expose
@cherrypy.tools.user()
@cherrypy.tools.workspace(on=False)
......@@ -258,3 +238,42 @@ class AjaxController(AbstractController):
return self.success()
except Exception, e:
return self.fail(msg=str(e))
@cherrypy.expose
@cherrypy.tools.user()
@cherrypy.tools.workspace(on=False)
@cherrypy.tools.allow(methods=['POST'])
def selectworkspace(self, wid, preload=True, state=True):
try:
db = cherrypy.request.db
uid = self.get('user_id')
# is workspace owned by the user?
workspace = Workspace.get_by_id(db, wid)
if not isinstance(workspace, Workspace):
raise Exception('Unknown Workspace!')
if workspace.user_id and workspace.user_id != uid:
raise Exception('No permission to select this Workspace!')
# pre-load the fs which is a connection test at the same time
if self.convert(preload, bool):
self.get('fs', workspace)
cherrypy.session['workspace_id'] = wid
state_data = None
if state:
state_data = WorkspaceState.get_state(db, wid, uid)
return self.success(data=state_data, encode_json=True)
except Exception, e:
return self.fail(msg=str(e), encode_json=True)
@cherrypy.expose
@cherrypy.tools.user()
@cherrypy.tools.allow(methods=['POST'])
def updateworkspacestate(self, wid, state):
try:
db = cherrypy.request.db
uid = self.get('user_id')
success = WorkspaceState.update_state(db, wid, uid, json.loads(state))
if not success:
raise Exception('Unknown Workspace!')
return self.success(encode_json=True)
except Exception, e:
return self.fail(msg=str(e), encode_json=True)
......@@ -10,13 +10,13 @@ import json as JSON
class VispaPreference(Base):
__tablename__ = 'vispa_preference'
profile_id = Column(Integer, schema.ForeignKey('profile.id', ondelete='CASCADE', onupdate='CASCADE'), nullable=False, primary_key=True)
section = Column(Unicode(64), nullable=False, primary_key=True)
value = Column(Unicode(300), nullable=False, default=u'{}')
timestamp = Column(DateTime, nullable=False, default=datetime.now)
created = Column(DateTime, nullable=False, default=datetime.now)
schema.PrimaryKeyConstraint(profile_id, section)
__tablename__ = 'vispa_preference'
profile_id = Column(Integer, schema.ForeignKey('profile.id', ondelete='CASCADE', onupdate='CASCADE'), nullable=False, primary_key=True)
section = Column(Unicode(64), nullable=False, primary_key=True)
value = Column(Unicode(300), nullable=False, default=u'{}')
timestamp = Column(DateTime, nullable=False, default=datetime.now)
created = Column(DateTime, nullable=False, default=datetime.now)
__table_args__ = (schema.PrimaryKeyConstraint(profile_id, section),)
@staticmethod
def get_by_profile_id(session, profile_id, section=None):
......@@ -67,13 +67,13 @@ class VispaPreference(Base):
class ExtensionPreference(Base):
__tablename__ = 'extension_preference'
profile_id = Column(Integer, schema.ForeignKey('profile.id', ondelete='CASCADE', onupdate='CASCADE'), nullable=False, primary_key=True)
key = Column(Unicode(64), nullable=False, primary_key=True)
value = Column(Unicode(300), nullable=False, default=u'{}')
timestamp = Column(DateTime, nullable=False, default=datetime.now)
created = Column(DateTime, nullable=False, default=datetime.now)
schema.PrimaryKeyConstraint(profile_id, key)
__tablename__ = 'extension_preference'
profile_id = Column(Integer, schema.ForeignKey('profile.id', ondelete='CASCADE', onupdate='CASCADE'), nullable=False, primary_key=True)
key = Column(Unicode(64), nullable=False, primary_key=True)
value = Column(Unicode(300), nullable=False, default=u'{}')
timestamp = Column(DateTime, nullable=False, default=datetime.now)
created = Column(DateTime, nullable=False, default=datetime.now)
__table_args__ = (schema.PrimaryKeyConstraint(profile_id, key),)
@staticmethod
def get_by_profile_id(session, profile_id, key=None):
......
......@@ -10,12 +10,12 @@ from vispa.models import Base
class AccessStats(Base):
__tablename__ = 'access_statistics'
user_ip = Column(Unicode(60), nullable=False, primary_key=True)
user_agent = Column(Unicode(200), nullable=False, primary_key=True)
date = Column(Date, nullable=False, default=date.today, primary_key=True)
pis = Column(Integer, nullable=False, default=0)
schema.PrimaryKeyConstraint(user_ip, user_agent, date)
__tablename__ = 'access_statistics'
user_ip = Column(Unicode(60), nullable=False, primary_key=True)
user_agent = Column(Unicode(200), nullable=False, primary_key=True)
date = Column(Date, nullable=False, default=date.today, primary_key=True)
pis = Column(Integer, nullable=False, default=0)
__table_args__ = (schema.PrimaryKeyConstraint(user_ip, user_agent, date),)
@staticmethod
def click(session, ip, agent):
......@@ -30,11 +30,11 @@ class AccessStats(Base):
class PageStats(Base):
__tablename__ = 'page_statistics'
page = Column(Unicode(50), nullable=False, primary_key=True)
date = Column(Date, nullable=False, default=date.today, primary_key=True)
pis = Column(Integer, nullable=False, default=0)
schema.PrimaryKeyConstraint(page, date)
__tablename__ = 'page_statistics'
page = Column(Unicode(50), nullable=False, primary_key=True)
date = Column(Date, nullable=False, default=date.today, primary_key=True)
pis = Column(Integer, nullable=False, default=0)
__table_args__ = (schema.PrimaryKeyConstraint(page, date),)
@staticmethod
def click(session, page):
......
......@@ -6,6 +6,7 @@ from sqlalchemy import Column, schema
from sqlalchemy.types import Unicode, Integer, DateTime, Boolean, Text
from vispa.models import Base
from vispa.helpers.db import insertion_safe
import json
class Workspace(Base):
......@@ -112,3 +113,50 @@ class Workspace(Base):
setattr(workspace, key, value)
session.commit()
return True
class WorkspaceState(Base):
__tablename__ = 'workspace_state'
workspace_id = Column(Integer, schema.ForeignKey('workspace.id', ondelete='CASCADE', onupdate='CASCADE'), nullable=False, primary_key=True)
user_id = Column(Integer, schema.ForeignKey('user.id', ondelete='CASCADE', onupdate='CASCADE'), nullable=False, primary_key=True)
state = Column(Text, nullable=True, default=u'{}')
timestamp = Column(DateTime, nullable=True, default=datetime.now)
__table_args__ = (schema.PrimaryKeyConstraint(workspace_id, user_id),)
@staticmethod
def get_by_ids(session, workspace_id, user_id):
workspace_state = session.query(WorkspaceState).filter_by(workspace_id=workspace_id, user_id=user_id).first()
if not isinstance(workspace_state, WorkspaceState):
# create an entry
workspace_state = WorkspaceState(workspace_id=workspace_id, user_id=user_id)
session.add(workspace_state)
session.commit()
return workspace_state
@staticmethod
def get_state(session, workspace_id, user_id, decode_json=False, workspace_state=None):
workspace_state = workspace_state or WorkspaceState.get_by_ids(session, workspace_id, user_id)
if not isinstance(workspace_state, WorkspaceState):
return None
state = workspace_state.state
return state if not decode_json else json.loads(state)
@staticmethod
def update_state(session, workspace_id, user_id, state, workspace_state=None):
workspace_state = workspace_state or WorkspaceState.get_by_ids(session, workspace_id, user_id)
if not isinstance(workspace_state, WorkspaceState):
return False
if isinstance(state, dict):
current_state = json.loads(workspace_state.state)
for id, identifier in state.items():
if identifier is None and id in current_state.keys():
del current_state[id]
elif identifier:
current_state[id] = identifier
workspace_state.state = json.dumps(current_state)
workspace_state.timestamp = datetime.now()
session.commit()
else:
workspace_state.state = unicode(state)
return True
......@@ -345,8 +345,14 @@ var ExtensionManager = VispaModule.extend({
if (instance instanceof ExtensionContentFull) {
// is it visible?
if (Vispa.extensionView.workflow.currentFullInstance == instance._id) {
$.History().push(Vispa.urlHandler.dynamic('/'));
this.updateUrl();
}
// tell the workspace manager
var state = {};
state[instance._id] = null;
Vispa.workspaceManager.updateState(state);
// empty its shortcuts
instance._shortcuts.disable().empty();
Vispa.extensionView.removeFullInstance(instance);
......@@ -473,7 +479,7 @@ var ExtensionManager = VispaModule.extend({
return this;
},
handleUrlParameters: function(params) {
handleParameters: function(params) {
var _this = this;
// procedure for each key-value pair:
// 1. check if an extension can receive the key
......@@ -537,6 +543,8 @@ var ExtensionManager = VispaModule.extend({
var url = '/';
if (instance) {
url = instance._getUrlParameters(true);
// tell the workspace manager about that change
Vispa.workspaceManager.updateState(instance._getUrlParameters());
}
// the third parameter is the 'fire' flag
$.History().push(Vispa.urlHandler.dynamic(url), undefined, fire);
......
......@@ -201,7 +201,7 @@ var Vispa = Class.extend({
// no global url operations yet, pass all to the ExtMan
// plan: add global url channels with callbacks to listen to, once!
}
this.extensionManager.handleUrlParameters(params);
this.extensionManager.handleParameters(params);
return this;
},
......
......@@ -25,6 +25,7 @@ var WorkspaceManager = VispaModule.extend({
};
this.workflow = {};
this.workspaces = data;
var state = {};
this.workspaceConfigDefaults = {
name: {
type: 'string',
......@@ -305,6 +306,11 @@ var WorkspaceManager = VispaModule.extend({
Vispa.pathBar.setValue(workspace.basedir);
// close all full instances
Vispa.extensionManager.removeAllFullInstances();
// set the state
_this.state = response.data;
// apply the state
Vispa.extensionManager.handleParameters(_this.state);
// call the wrapped history callback
Vispa.urlHandler.workflow.wrappedCallback();
Vispa.setLoading(false);
} else {
......@@ -346,6 +352,25 @@ var WorkspaceManager = VispaModule.extend({
return this;
},
updateState: function(obj) {
var _this = this;
// update the state object
$.each(obj, function(id, identifier) {
if (identifier === null || identifier === undefined) {
// the instance is closed
delete _this.state[id];
} else {
// the instance had an update
_this.state[id] = identifier;
}
});
// tell the server
$.post(Vispa.urlHandler.dynamic('/ajax/updateworkspacestate'), {
wid: this.getWorkspace().id,
state: JSON.stringify(obj)
});
},
setupTopics: function() {
var _this = this;
$.Topic('ws.apply').subscribe(function() {
......
......@@ -111,7 +111,8 @@ var Vispa = Class.extend({
type: 'POST',
data: {
wid: wid,
preload: false
preload: false,
state: false
}
});
// notification
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment