class WS_Plasmoids(object): def __init__(self, dispatcher): self.infinote_pool = InfinotePool(self) dispatcher.signals.subscribe('ws_disconnect', self.disconnect_plasmoid_editor) dispatcher.signals.subscribe('view_changed', self.disconnect_plasmoid_editor, filters = [(r'/plasmoids/(?P<plasmoid_uuid>[^/]+)/edit/',True),(r'/plasmoids/(?P<plasmoid_uuid>[^/]+)/edit/',False)]) def list_plasmoids(self, client): plasmoids = Plasmoid.objects.all() main = render_to_string("plasmoids/read_plasmoids.html", {'plasmoids':plasmoids}) return {'data':{'dom':{'main':main}}} def create_plasmoid(self, client): ''' Creates a new plasmoid ''' form = EditPlasmoidForm() plasmoid = Plasmoid() plasmoid.uuid = str(uuid.uuid4()) main = render_to_string("plasmoids/create_plasmoid.html",{'plasmoid':plasmoid,'form': form}) return { 'data':{ 'dom':{'main':main}, 'plasmoid':{'uuid':plasmoid.uuid} } } def edit_plasmoid(self, client, plasmoid_uuid): ''' Creates a new plasmoid, or subscribes to an existing plasmoid using uuid. Includes layout. ''' try: plasmoid = Plasmoid.objects.get(pk = plasmoid_uuid) except ObjectDoesNotExist: plasmoid = Plasmoid() client.role = 'edit' form = EditPlasmoidForm(initial={'slug':plasmoid.slug,'type':plasmoid.type,'target':plasmoid.target,'visible':plasmoid.visible}) main = render_to_string("plasmoids/edit_plasmoid.html",{'plasmoid':plasmoid, 'form': form}) subscriber = self.infinote_pool.subscribe(client, plasmoid_uuid, plasmoid.script, 'plasmoids', self.signal_presence) publish_activity(client.profile, _('Plasmoid editing'),'/plasmoids/%s/edit/' % plasmoid_uuid,[0,0,4,0,0]) return { 'data':{ 'page':subscriber, 'uid': client.profile.pk, 'online':subscriber['online'], 'dom':{'main':main} } } def save_plasmoid(self, client, plasmoid_uuid, form): _content = form['content'] del form['content'] form = EditPlasmoidForm(form) if form.is_valid(): try: plasmoid = Plasmoid.objects.get(pk = plasmoid_uuid) except Plasmoid.DoesNotExist: plasmoid = Plasmoid() publish_activity(client.profile, _('Plasmoid created'),'/plasmoids/%s/edit/' % plasmoid_uuid,[0,0,4,0,0]) plasmoid.slug = form.cleaned_data['slug'] plasmoid.script = _content plasmoid.type = form.cleaned_data['type'] plasmoid.target = form.cleaned_data['target'] plasmoid.visible = int(form.cleaned_data['visible']) plasmoid.last_modified = datetime.now() plasmoid.save() client_response, tpl_params = self._get_plasmoids(client) #UPDATE ROUTES HWIOS.plasmoids.get_routes() client_response.update({ 'status':{ 'code':'PLASMOID_EDIT_OK', 'i18n':_('Plasmoid %(slug)s stored...') % {'slug':plasmoid.slug}, 'type': HWIOS.ws_realm._t['notify-info'], } }) notify_others(client, client_response,'/plasmoids/modified/', '^/plasmoids/$', tpl_params) publish_activity(client.profile, _('Plasmoid saved'),'/plasmoids/%s/edit/' % plasmoid_uuid,[0,0,4,0,0]) return client_response else: try: plasmoid = Plasmoid.objects.get(pk = plasmoid_uuid) except ObjectDoesNotExist: plasmoid = Plasmoid() main = render_to_string("plasmoids/edit_plasmoid.html", {'plasmoid':plasmoid, "form":form}) response = { 'status':{ 'code':'FORM_INVALID', 'i18n':_('Invalid Form!'), 'type': HWIOS.ws_realm._t['notify-warning'] }, 'data':{'dom':{'main':main}} } return response def delete_plasmoids(self, client, params = None): if params == None: dialog = render_to_string("plasmoids/delete_plasmoid_confirmation.html") return {'data':{'dom':{'dialog':dialog}}} else: _count = 0 regex_modifier = '' for slug in params: plasmoid = Plasmoid.objects.get(pk=slug) plasmoid.delete() if regex_modifier != '': regex_modifier = '%s|%s' % (regex_modifier, slug) else: regex_modifier = '%s' % slug _count +=1 client_response, tpl_params = self._get_plasmoids(client) _target_state = '/plasmoids/' client_response.update({ 'status':{ 'code':'DELETE_OK', 'i18n':_('%s plasmoid(s) deleted...' % _count), 'type': HWIOS.ws_realm._t['notify-info'], 'state': _target_state, } }) notify_others(client, client_response,'/plasmoids/modified/', '^/plasmoids/(%s)/edit/$' % regex_modifier, tpl_params, _target_state) notify_others(client, client_response,'/plasmoids/modified/', '^/plasmoids/$', tpl_params, _target_state) publish_activity(client.profile, _('Plasmoid(s) deleted'),'/plasmoids/',[0,0,4,0,0]) return client_response def connect_plasmoid_editor(self, client, plasmoid_uuid): ''' Synchronizes a layer's processing.js information ''' try: hdjs_text = HDJS.objects.get(slug=plasmoid_uuid) except ObjectDoesNotExist: hdjs_text = '' client.role = 'edit' subscriber = self.infinote_pool.subscribe(client, plasmoid_uuid, hdjs_text, 'plasmoids', self.signal_presence) return {'data':{'page':subscriber, 'uid': client.profile.pk,'online':subscriber['online']}} def disconnect_plasmoid_editor(self, client, plasmoid_uuid = None): self.infinote_pool.unsubscribe(client, 'plasmoids', self.signal_presence) def request_plasmoid_insert(self, client, plasmoid_uuid, params): self.infinote_pool.request_insert(client,'plasmoids', plasmoid_uuid, params, self.signal_operation) def request_plasmoid_delete(self, client, plasmoid_uuid, params): self.infinote_pool.request_delete(client,'plasmoids', plasmoid_uuid, params, self.signal_operation) def request_plasmoid_undo(self, client, plasmoid_uuid, params): self.infinote_pool.request_undo(client,'plasmoids', plasmoid_uuid, params, self.signal_operation) def update_remote_caret(self, client, plasmoid_uuid, params): self.infinote_pool.update_caret(client,'plasmoids', plasmoid_uuid, params, self.signal_caret) def signal_presence(self, client, online, app_pool, item_id): client.remote('/data/%s/%s/online/update/' % (app_pool, item_id),{'online':online}) def signal_operation(self, client, app_pool, item_id, operation_type, params): client.remote('/data/%s/%s/%s/' % (app_pool, item_id, operation_type), params) def signal_caret(self, client, app_pool, item_id, params): client.remote('/data/%s/%s/caret/' % (app_pool, item_id), params) def play_plasmoid_script(self, client, plasmoid_uuid): state = self.infinote_pool.get_state('plasmoids',plasmoid_id) others = self.infinote_pool.get_other_clients(client,'plasmoids',plasmoid_id) return {'data':{'script':state[1]}} def _get_plasmoids(self, client): plasmoids = Plasmoid.objects.all() tpl_params = {"plasmoids":plasmoids} main = render_to_string("plasmoids/read_plasmoids.html", {'plasmoids':plasmoids}) return [ {'data':{'dom':{'main':main}}}, {'main':{'tpl':'plasmoids/read_plasmoids.html','params':tpl_params}} ]
class WS_Hyki(object): ''' Websocket handler for the hyki. Mainly involves infinote operations. ''' def __init__(self, dispatcher): self.infinote_pool = InfinotePool(self) self.diff_mp = diff_match_patch() self._update_hyki_graph() dispatcher.signals.subscribe('ws_disconnect', self.leave_hyki_article) dispatcher.signals.subscribe('view_changed', self.leave_hyki_article, filters = [(r'/hyki/(?P<slug>[^/]+)/',True),(r'/hyki/(?P<slug>[^/]+)/edit/$',False)]) def view_hyki_article(self, client, slug): """Infinote client wants to join the subscription log based on the content-id(cid). In this case within the hyki, the client requests the cid from the page-slug, subscribes to the channel and starts sending operation requests out, with the cid included. When the client disconnects(other page view or websocket closes), the client gets unsubscribed from the log. :param Client client: The requesting client :param str slug: The wiki slug reference :return: dict - wiki article state information or html-layout data response """ try: article = HykiArticle.objects.get(slug=slug) article_text = self._get_revision_text(article) revisions = len(HykiRevision.objects.filter(article = article)) client.role = 'view' subscriber = self.infinote_pool.subscribe(client, slug, article_text, 'hyki', self._signal_presence) main = render_to_string("hyki/read_article.html", {"profile":client.profile,'page':article}) return { 'data':{ 'dom':{'main':main}, 'page':{ 'title':article.slug, 'state':subscriber['state'], 'log':subscriber['log'], 'revisions':revisions }, 'online':subscriber['online'], 'uid':client.profile.pk } } except ObjectDoesNotExist: main = render_to_string("hyki/create_article.html", {"profile":profile,"page":{'slug':slug}}) return {'data':{'dom':{'main':main},'page':{'title':slug, 'state':False}}} @WSAuth.is_authenticated def edit_hyki_article(self, client, slug): """Joins an infinote subscription pool and returns document state information to sync client-side infinote editor with, or return a new-article template view :param Client client: The requesting client :param str slug: The wiki slug reference :return: dict - wiki article state information or html-layout data response """ try: article = HykiArticle.objects.get(slug=slug) article_text = self._get_revision_text(article) revisions = len(HykiRevision.objects.filter(article = article)) client.role = 'edit' subscriber = self.infinote_pool.subscribe(client, slug, article_text, 'hyki', self._signal_presence) form = EditArticleForm(initial={'id':article.pk, 'slug':slug}) main = render_to_string('hyki/edit_article.html', {'form': form,"page":article}) if len(client.transport.view_history) > 1: if '/hyki/%s/edit/' % slug not in client.transport.view_history[-2]: publish_activity(client.profile, _('Wiki article editing'),'/hyki/%s/edit/' % slug,[0,2,2,0,0]) else: publish_activity(client.profile, _('Wiki article editing'),'/hyki/%s/edit/' % slug,[0,2,2,0,0]) return { 'data':{ 'dom':{'main':main}, 'page':{ 'title':article.slug, 'state':subscriber['state'], 'log':subscriber['log'], 'revisions':revisions }, 'online':subscriber['online'], 'uid':client.profile.pk } } except ObjectDoesNotExist: main = render_to_string("hyki/create_article.html", {"profile":client.profile,"page":{'slug':slug}}) return {'data':{'dom':{'main':main},'page':{'title':slug, 'state':False}}} def _signal_presence(self, client, online, app_pool, item_id): """Callback function when someone subscribes or unsubscribes to the infinote pool""" client.remote('/data/%s/%s/online/update/' % (app_pool, item_id),{'online':online}) def _signal_operation(self, client, app_pool, item_id, operation_type, params): """Client callback function for an infinote operation""" client.remote('/data/%s/%s/%s/' % (app_pool, item_id, operation_type), params) def _signal_caret(self, client, app_pool, item_id, params): """Client callback function for a caret operation""" client.remote('/data/%s/%s/caret/' % (app_pool, item_id), {'id': params['id'],'cursor':params['cursor']}) def leave_hyki_article(self, client): """Remove client page subscription after client left the wiki page or disconnected. This is tracked by signal observers defined in the controller constructor. :param Client client: The requesting client """ self.infinote_pool.unsubscribe(client, 'hyki', self._signal_presence) @WSAuth.is_authenticated def request_hyki_insert(self, client, slug, params): """Infinote client insert operation request on the subscription log :param Client client: The requesting client :param str slug: The wiki slug reference :param dict params: Contains the operation parameters :return: dict - status data response """ self.infinote_pool.request_insert(client,'hyki', slug, params, self._signal_operation) return {'status':{'code':'OP_OK'}} @WSAuth.is_authenticated def request_hyki_remove(self, client, slug, params): """Infinote client delete operation request on the subscription log :param Client client: The requesting client :param str slug: The wiki slug reference :param dict params: Contains the operation parameters :return: dict - status data response """ self.infinote_pool.request_delete(client,'hyki', slug, params, self._signal_operation) return {'status':{'code':'OP_OK'}} @WSAuth.is_authenticated def request_hyki_undo(self, client, slug, params): """Infinote client undo operation request on the subscription log :param Client client: The requesting client :param str slug: The wiki slug reference :param dict params: Contains the operation parameters :return: dict - status data response """ self.infinote_pool.request_undo(client,'hyki', slug, params, self._signal_operation) return {'status':{'code':'OP_OK'}} def update_remote_caret(self, client, slug, params): """Infinote client caret move broadcast :param Client client: The requesting client :param str slug: The wiki slug reference :param dict params: Contains the operation parameters """ self.infinote_pool.update_caret(client,'hyki', slug, params, self._signal_caret) @WSAuth.is_authenticated def edit_history(self, client, slug): """Render the wiki page history view and return an overview of all page revisions for time-warp slider :param Client client: The requesting client :param str slug: The wiki slug reference :return: dict - Revision data and html-layout data response """ article = HykiArticle.objects.get(slug = slug) revisions = HykiRevision.objects.filter(article = article) rev_data = [] for revision in revisions: rev_data.append({'submit_comments':revision.submit_comments,'submit_date':str(revision.submit_date),'patch':revision.patch, 'submit_profile':'%s %s' % (revision.submit_profile.first_name,revision.submit_profile.last_name)}) rev_data.reverse() form = EditArticleHistoryForm(initial={'undo':len(revisions)}) main = render_to_string("hyki/edit_history.html", {'page':article,'form':form}) return { 'data':{ 'dom':{'main':main} }, 'page':{ 'slug':article.slug, 'revisions':rev_data } } @WSAuth.is_authenticated def save_page(self, client, slug, params): """Saves an infinote document's state to the database and notifies participants of the action :param Client client: The requesting client :param str slug: The wiki slug reference :return: dict - Revision data and html-layout data response """ _content = params['content'] del params['content'] form = EditArticleForm(params) if form.is_valid(): try: article = HykiArticle.objects.get(slug = slug) except HykiArticle.DoesNotExist: article = HykiArticle() article.slug = slug article.save() self._update_hyki_graph() publish_activity(client.profile, _('Wiki article created'),'/hyki/%s/' % slug,[0,1,1,0,0]) for _client in HWIOS.ws_realm.pool.get_clients(): #this client is watching our attempt to create a new article. Send it a request to resubscribe to the article if client != _client and '/hyki/' in _client.transport.view_history[-1]: _client.remote('/hyki/%s/' % slug,{ 'status':{ 'code':'HYKI_PAGE_CREATED', 'i18n':_('%(first_name)s %(last_name)s just created article %(slug)s!') % {'first_name':client.profile.first_name,'last_name': client.profile.last_name,'slug':slug}, 'type': HWIOS.ws_realm._t['notify-info'] } }) return self.edit_hyki_article(client,slug) article.last_modified = datetime.now() #construct a revision previous_text = self._get_revision_text(article) patch = self.diff_mp.patch_make(previous_text, _content) #if the patch applied, save a revision if len(patch) > 0: revision = HykiRevision() revision.article = article revision.patch = self.diff_mp.patch_toText(patch) revision.submit_profile = client.profile revision.submit_comments = form.cleaned_data["submit_comments"] revision.save() revisions = HykiRevision.objects.filter(article = article) #evaluate queryset revision_count = len(revisions) last_revision = revisions[0] '''send our latest rev with the notification to other users, just in case we have someone fooling around in history''' rev_data = {'submit_comments':last_revision.submit_comments,'submit_date':str(last_revision.submit_date),'patch':last_revision.patch, 'submit_profile':'%s %s' % (last_revision.submit_profile.first_name,last_revision.submit_profile.last_name)} article.content = _content article.save() publish_activity(client.profile, _('Wiki article saved'),'/hyki/%s/' % slug,[0,1,1,0,0]) response = { 'status':{ 'code':'HYKI_EDIT_OK', 'i18n':_('Hyki article %(slug)s stored...') % {'slug':slug}, 'type': HWIOS.ws_realm._t['notify-info'], 'state': '/hyki/%s/' % slug, } } main = render_to_string('hyki/read_article.html',{"profile":client.profile, "page":article}) response.update({'data':{'dom':{'main':main},'page':{'title':article.slug, 'slug':slug}}}) else: response = { 'status':{ 'code':'HYKI_EDIT_NO_CHANGE', 'i18n':_('No changes found in hyki article. Page was not stored...'), 'type': HWIOS.ws_realm._t['notify-warning'], 'state': '/hyki/%s/' % slug, } } main = render_to_string('hyki/read_article.html',{"profile":client.profile, "page":article}) response.update({'data':{'dom':{'main':main},'page':{'title':article.slug, 'slug':slug}}}) return response #Inform all users that are editing this article. Update history views with the latest revision for target_client in HWIOS.ws_realm.pool.subscription['hyki'][slug]['clients']: if client != target_client and target_client.role == 'edit': target_client.remote("/data/hyki/%s/saved/" % slug,{ 'status':{ 'code':'HYKI_PAGE_SAVED', 'type': HWIOS.ws_realm._t['notify-info'], 'i18n':_('Hyki article was stored by %(first_name)s %(last_name)s as revision %(revision)s: %(comments)s') % {'first_name':client.profile.first_name, 'last_name':client.profile.last_name,'revision':revision_count,'comments':last_revision.submit_comments} }, 'data':{'revision':rev_data} }) return response else: try: article = HykiArticle.objects.get(slug=slug) main = render_to_string('hyki/edit_article.html', {'form': form,"page":article}) except HykiArticle.DoesNotExist: main = render_to_string("hyki/create_article.html", {"slug":slug,"form":form}) response = { 'status':{ 'code':'FORM_INVALID', 'i18n':_('Invalid Form!'), 'type': HWIOS.ws_realm._t['notify-warning'] }, 'data':{'dom':{'main':main}} } return response @WSAuth.is_authenticated def delete_page(self, client, slug, params = None): """Deletes an infinote document from the infinote pool and database, and notifies participants of the action :param Client client: The requesting client :param str slug: The wiki slug reference :return: dict - Status and html-layout data response """ if params == None: try: article = HykiArticle.objects.get(slug=slug) except HykiArticle.DoesNotExist: return False dialog = render_to_string("hyki/delete_article_confirm.html", {"page":article}) return { 'data':{ 'dom':{'dialog':dialog} } } try: article = HykiArticle.objects.get(slug=slug) except HykiArticle.DoesNotExist: return { 'status':{ 'code':'HYKI_INVALID_PAGE', 'i18n':_('Invalid article'), 'type': HWIOS.ws_realm._t['notify-error'] } } main = render_to_string("hyki/create_article.html", {"profile":profile,"page":{'slug':slug}}) for _client in HWIOS.ws_realm.pool.get_clients(): if client != _client and '/hyki/' in _client.transport.view_history[-1]: _client.remote('/data/hyki/%s/deleted/' % slug,{ 'status':{ 'code':'HYKI_PAGE_DELETED', 'i18n':_('%(first_name)s %(last_name)s removed the current article...') % {'first_name':client.profile.first_name,'last_name':client.profile.last_name}, 'type': HWIOS.ws_realm._t['notify-info'] }, 'data':{ 'dom':{'main':main}, 'page':{'title':slug, 'content':False,'slug':slug} } }) del HWIOS.ws_realm.pool.subscription['hyki'][slug] article.delete() publish_activity(client.profile, _('Wiki article deleted'),'/hyki/%s/' % slug,[0,1,4,0,0]) self._update_hyki_graph() return { 'status':{ 'code':'HYKI_DELETE_OK', 'i18n':_('Hyki article deleted successfully...'), 'type': HWIOS.ws_realm._t['notify-info'] }, 'data':{ 'dom':{'main':main} } } @WSAuth.is_authenticated def notify_editors(self, client, slug, revision): '''Relies on correct chronical ordering of revisions in the view''' article = HykiArticle.objects.get(slug=slug) _rev = HykiRevision.objects.filter(article = article) _revision = _rev[len(_rev) - revision] for _client in HWIOS.ws_realm.pool.subscription['hyki'][slug]['clients']: if _client.profile.uuid != client.profile.uuid: _client.remote('/data/hyki/'+slug+'/edit/notify/',{ 'status':{ 'code':'HYKI_HISTORY_RESTORE_OK', 'i18n':_('Client %(first_name)s %(last_name)s restored article text to revision %(revision)s: \'%(comments)s\'') % {'first_name':client.profile.first_name, 'last_name':client.profile.last_name, 'revision':revision, 'comments':_revision.submit_comments}, 'type': HWIOS.ws_realm._t['notify-info'] }}) publish_activity(client.profile, _('Wiki revision restored'),'/hyki/%s/' % slug,[0,1,3,0,0]) return {} def _get_revision_text(self, article, revision = None): """Retrieve the page's current persistent revision text from the stored patches""" revisions = HykiRevision.objects.filter(article = article) patches = [] for revision in revisions: patches.append(self.diff_mp.patch_fromText(revision.patch)) patches.reverse() text = '' for patch in patches: text = self.diff_mp.patch_apply(patch, text)[0] return text def _get_editing_clients(self, slug): editing_clients = [] for _client in HWIOS.ws_realm.pool.subscription['hyki'][slug]['clients']: if _client.role == 'edit': editing_clients.append('%s %s' % (_client.profile.first_name, _client.profile.last_name)) return editing_clients def plasmoid_get_hyki_tree(self, client): jit_graph = self.get_hyki_graph() response = {'data':{'tree':jit_graph}} return response #write a python-graph of hyki articles(nodes) and their links(edges) to other articles def _update_hyki_graph(self): import re from pygraph.classes.graph import graph pattern = re.compile(LINK_RE) articles = HykiArticle.objects.all() gr = graph() if len(articles) > 0: for article in articles: if article.id not in gr.nodes(): self.create_node(gr, article) article_text = self._get_revision_text(article) results = pattern.findall(article_text) for link in results: _source = link[7].split('/')[1] _link = link[7].split('/')[2] if _source == 'hyki': #adjacency article links for _article in articles: #match link with article if _article.slug == _link: #doesnt point to itself if _article != article: #target node doesn't exist yet if _article.id not in gr.nodes(): self.create_node(gr, _article) gr.add_edge((article.id, _article.id)) else: if not gr.has_edge((_article.id,article.id)): gr.add_edge((article.id, _article.id)) self.graph_info = self.format_graph_to_thejit_tree(gr) else: self.graph_info = False def create_node(self, graph, article): graph.add_node(article.id) graph.add_node_attribute(article.id,{ 'name':article.slug, 'data':{} }) def format_graph_to_thejit_graph(self,graph): thejit_graph = [] for node in graph.nodes(): jit_node = graph.node_attr[node][0] jit_node['id'] = node jit_node['adjacencies'] = graph.node_neighbors[node] thejit_graph.append(jit_node) return thejit_graph def format_graph_to_thejit_tree(self, graph): """ If you have a 'disconnected' graph, this will fail """ thejit_graph = self.make_node(graph, graph.nodes()[0], []) return thejit_graph def make_node(self, graph, node, all_nodes): """ Are there any 'data' attrs inside `graph` we can use? """ jit_node = {} jit_node['id'] = node jit_node['name'] = graph.node_attr[node][0]['name'] jit_children = [] for child in graph.node_neighbors[node]: if child in all_nodes: # Avoid infinite recursion! continue jit_children.append(self.make_node(graph, child, all_nodes + [child])) jit_node['children'] = jit_children return jit_node def get_hyki_graph(self): return self.graph_info
class WS_Pages(object): """ Websocket controller class for the plasmoid module """ def __init__(self, dispatcher): self.infinote_pool = InfinotePool(self) dispatcher.signals.subscribe('ws_disconnect', self.disconnect_entity_editor) dispatcher.signals.subscribe('view_changed', self.disconnect_entity_editor, filters = [(r'/pages/entities/(?P<uuid>[^/]+)/edit/',True),(r'/pages/entities/(?P<uuid>[^/]+)/edit/',False)]) @WSAuth.is_staff def list_pages(self, client): """Render the view that shows an overview of all plasmoids :param Client client: The requesting client :return: dict - Html-layout data response """ anchors = PageAnchor.objects.all() entities = PageEntity.objects.all() main = render_to_string("pages/list_pages.html", {'anchors':anchors,'entities':entities}) return {'data':{'dom':{'main':main}}} @WSAuth.is_staff def create_anchor(self, client, form = None): """Render and returns the create plasmoid view :param Client client: The requesting client :return: dict - Data and html-layout response """ if form == None: form = CreateAnchorForm() page = PageAnchor() main = render_to_string("pages/create_anchor.html",{'page':page,'form': form}) return { 'data':{ 'dom':{'main':main}, } } else: _uuid = str(uuid.uuid4()) form = CreateAnchorForm(form) response = self._try_save_anchor(client, _uuid, form) return response @WSAuth.is_staff def edit_anchor(self, client, uuid, form = None): """ Edit an existing or a new plasmoid. In both cases, the infinote subscription pool defines the plasmoid view, not the model. This makes it possible to edit a new plasmoid, that's not yet in the database. :param Client client: The requesting client :param str plasmoid_uuid: The uuid of the plasmoid to edit :return: dict - Data and html-layout response """ if form == None: try: page = PageAnchor.objects.get(pk = uuid) except ObjectDoesNotExist: return { 'status':{ 'code':'PAGE_INVALID', 'i18n':_('Invalid page!'), 'type': HWIOS.ws_realm._t['notify-error'], } } client.role = 'edit' form = EditAnchorForm(initial={'slug':page.slug,'target':page.target,'access':page.access}) main = render_to_string("pages/edit_anchor.html",{'page':page, 'form': form}) #subscriber = self.infinote_pool.subscribe(client, plasmoid_uuid, plasmoid.script, 'plasmoids', self._signal_presence) publish_activity(client.profile, _('Page editing'),'/pages/%s/edit/' % uuid,[0,0,4,0,0]) return { 'data':{ 'dom':{'main':main} } } else: form = EditAnchorForm(form) response = self._try_save_anchor(client, uuid, form) return response @WSAuth.is_staff def delete_anchors(self, client, params = None): """ Delete an existing plasmoid from the database and subscription pool, render/show the general plasmoid overview and notify others. :param Client client: The requesting client :param str plasmoid_uuid: The uuid of the plasmoid to delete :return: dict - Status and html-layout response """ if params == None: dialog = render_to_string("pages/delete_anchor_confirmation.html") return {'data':{'dom':{'dialog':dialog}}} else: _count = 0 regex_modifier = '' for slug in params: page = PageAnchor.objects.get(pk=slug) page.delete() if regex_modifier != '': regex_modifier = '%s|%s' % (regex_modifier, slug) else: regex_modifier = '%s' % slug _count +=1 HWIOS.anchors.get_routes() client_response, tpl_params = self._get_pages(client) _target_state = '/pages/' client_response.update({ 'status':{ 'code':'DELETE_OK', 'i18n':_('%s page(s) deleted...' % _count), 'type': HWIOS.ws_realm._t['notify-info'], 'state': _target_state, } }) notify_others(client, client_response,'/pages/modified/', '^/pages/(%s)/edit/$' % regex_modifier, tpl_params, _target_state) notify_others(client, client_response,'/pages/modified/', '^/pages/$', tpl_params, _target_state) publish_activity(client.profile, _('Page(s) deleted'),'/pages/',[0,0,4,0,0]) return client_response @WSAuth.is_staff def _try_save_anchor(self, client, uuid, form): """ Save an existing or a new page, render/show the general plasmoid overview and notify others. :param Client client: The requesting client :param str plasmoid_uuid: The uuid of the plasmoid to save :return: dict - Status and html-layout response """ if form.is_valid(): try: page = PageAnchor.objects.get(pk = uuid) except PageAnchor.DoesNotExist: page = PageAnchor() publish_activity(client.profile, _('Page created'),'/pages/%s/edit/' % uuid,[0,0,4,0,0]) page.slug = form.cleaned_data['slug'] page.target = form.cleaned_data['target'] page.access = int(form.cleaned_data['access']) page.last_modified = datetime.now() page.save() client_response, tpl_params = self._get_pages(client) #UPDATE ROUTES #HWIOS.pages.get_routes() client_response.update({ 'status':{ 'code':'PAGE_EDIT_OK', 'i18n':_('Page %(slug)s stored...') % {'slug':page.slug}, 'type': HWIOS.ws_realm._t['notify-info'], } }) #notify_others(client, client_response,'/plasmoids/modified/', '^/plasmoids/$', tpl_params) #publish_activity(client.profile, _('Plasmoid saved'),'/plasmoids/%s/edit/' % plasmoid_uuid,[0,0,4,0,0]) return client_response else: try: page = PageAnchor.objects.get(pk = uuid) main = render_to_string("pages/edit_anchor.html", {'page':page, "form":form}) #new except ObjectDoesNotExist: page = PageAnchor() main = render_to_string("pages/create_anchor.html", {'form':form}) response = { 'status':{ 'code':'FORM_INVALID', 'i18n':_('Invalid Form!'), 'type': HWIOS.ws_realm._t['notify-warning'] }, 'data':{'dom':{'main':main}} } return response @WSAuth.is_staff def create_entity(self, client, uuid, form = None): """Render and returns the create plasmoid view :param Client client: The requesting client :return: dict - Data and html-layout response """ if form == None: form = EntityForm() entity = PageEntity() entity.uuid = uuid subscriber = self.infinote_pool.subscribe(client, uuid, '', 'entities', self._signal_presence) main = render_to_string("pages/create_entity.html",{'entity':entity,'form': form}) return { 'data':{ 'ce':subscriber, 'uid': client.profile.pk, 'dom':{'main':main}, } } else: print "PROCESS NEW!" _content = form['content'] del form['content'] form = EntityForm(form) form.content = _content response = self._try_save_entity(client, uuid, form) return response @WSAuth.is_staff def edit_entity(self, client, uuid, form = None): """ Edit an existing or a new plasmoid. In both cases, the infinote subscription pool defines the plasmoid view, not the model. This makes it possible to edit a new plasmoid, that's not yet in the database. :param Client client: The requesting client :param str plasmoid_uuid: The uuid of the plasmoid to edit :return: dict - Data and html-layout response """ if form == None: try: entity = PageEntity.objects.get(pk = uuid) except ObjectDoesNotExist: entity = PageEntity() entity.code = '' client.role = 'edit' form = EntityForm(initial={'slug':entity.slug,'anchor':entity.anchor,'type':entity.type}) main = render_to_string("pages/edit_entity.html",{'entity':entity, 'form': form}) subscriber = self.infinote_pool.subscribe(client, uuid, entity.code, 'entities', self._signal_presence) publish_activity(client.profile, _('Entity editing'),'/pages/entities/%s/edit/' % uuid,[0,0,4,0,0]) return { 'data':{ 'ce':subscriber, 'uid': client.profile.pk, 'dom':{'main':main} } } else: print "PROCESS NEW!" _content = form['content'] del form['content'] form = EntityForm(form) form.content = _content response = self._try_save_entity(client, uuid, form) return response @WSAuth.is_staff def _try_save_entity(self, client, uuid, form): """ Save an existing or a new plasmoid, render/show the general plasmoid overview and notify others. :param Client client: The requesting client :param str plasmoid_uuid: The uuid of the plasmoid to save :return: dict - Status and html-layout response """ if form.is_valid(): try: entity = PageEntity.objects.get(pk = uuid) entity.slug = form.cleaned_data['slug'] entity.anchor = form.cleaned_data['anchor'] entity.code = form.content entity.type = form.cleaned_data['type'] entity.last_modified = datetime.now() entity.save() client_response, tpl_params = self._get_pages(client) client_response.update({ 'status':{ 'code':'ENTITY_EDIT_OK', 'i18n':_('Page entity %(slug)s updated...') % {'slug':entity.slug}, 'type': HWIOS.ws_realm._t['notify-info'], 'state': '/pages/', } }) except PageEntity.DoesNotExist: entity = PageEntity() publish_activity(client.profile, _('Page entity created'),'/pages/entities/%s/edit/' % uuid,[0,0,4,0,0]) entity.slug = form.cleaned_data['slug'] entity.anchor = form.cleaned_data['anchor'] entity.code = form.content entity.type = form.cleaned_data['type'] entity.last_modified = datetime.now() entity.save() client_response, tpl_params = self._get_pages(client) client_response.update({ 'status':{ 'code':'ENTITY_CREATE_OK', 'i18n':_('Page entity %(slug)s created...') % {'slug':entity.slug}, 'type': HWIOS.ws_realm._t['notify-info'], 'state': '/pages/', } }) #UPDATE ROUTES HWIOS.anchors.get_routes() notify_others(client, client_response,'/pages/entities/modified/', '^/pages/$', tpl_params) publish_activity(client.profile, _('Page entity saved'),'/pages/entities/%s/edit/' % uuid,[0,0,4,0,0]) return client_response else: try: entity = PageEntity.objects.get(pk = uuid) main = render_to_string("pages/edit_entity.html", {'entity':entity, "form":form}) except ObjectDoesNotExist: entity = PageEntity() entity.slug = _('New Entity') entity.uuid = uuid main = render_to_string("pages/create_entity.html", {'entity':entity, "form":form}) response = { 'status':{ 'code':'FORM_INVALID', 'i18n':_('Invalid Form!'), 'type': HWIOS.ws_realm._t['notify-warning'] }, 'data':{'dom':{'main':main}} } return response @WSAuth.is_staff def delete_entities(self, client, params = None): """ Delete an existing plasmoid from the database and subscription pool, render/show the general plasmoid overview and notify others. :param Client client: The requesting client :param str plasmoid_uuid: The uuid of the plasmoid to delete :return: dict - Status and html-layout response """ if params == None: dialog = render_to_string("pages/delete_entity_confirmation.html") return {'data':{'dom':{'dialog':dialog}}} else: _count = 0 regex_modifier = '' for slug in params: entity = PageEntity.objects.get(pk=slug) entity.delete() if regex_modifier != '': regex_modifier = '%s|%s' % (regex_modifier, slug) else: regex_modifier = '%s' % slug _count +=1 HWIOS.anchors.get_routes() client_response, tpl_params = self._get_pages(client) _target_state = '/pages/' client_response.update({ 'status':{ 'code':'DELETE_OK', 'i18n':_('%s page(s) deleted...' % _count), 'type': HWIOS.ws_realm._t['notify-info'], 'state': _target_state, } }) notify_others(client, client_response,'/pages/modified/', '^/pages/entities/(%s)/edit/$' % regex_modifier, tpl_params, _target_state) notify_others(client, client_response,'/pages/modified/', '^/pages/$', tpl_params, _target_state) publish_activity(client.profile, _('Entities deleted'),'/pages/',[0,0,4,0,0]) return client_response @WSAuth.is_staff def connect_entity_editor(self, client, uuid): """ Legacy code that's a bit redundant now. Problem was that we didn't want regular users to edit shared js-code. Will be revived later to add some interactivity to the pad' presentation functionality :param Client client: The requesting client :param str plasmoid_uuid: The uuid of the plasmoid to connect to :return: dict - Data and html-layout response """ try: hdjs_text = HDJS.objects.get(slug=plasmoid_uuid) except ObjectDoesNotExist: hdjs_text = '' client.role = 'edit' subscriber = self.infinote_pool.subscribe(client, plasmoid_uuid, hdjs_text, 'plasmoids', self._signal_presence) return {'data':{'page':subscriber, 'uid': client.profile.pk,'online':subscriber['online']}} def disconnect_entity_editor(self, client, uuid = None): """Unsubscribe from the infinote plasmoid pool on certain events like disconnect and view_changed :param Client client: The requesting client :param str plasmoid_uuid: The uuid of the plasmoid to disconnect from the infinote pool """ self.infinote_pool.unsubscribe(client, 'entities', self._signal_presence) def request_entity_insert(self, client, uuid, params): """Insert operation for a specific plasmoid in the infinote pool :param Client client: The requesting client :param str uuid: The uuid of the plasmoid to operate on in the infinote pool :param dict params: The params that are used to succesfully perform the infinote operation """ self.infinote_pool.request_insert(client,'entities', uuid, params, self._signal_operation) def request_entity_delete(self, client, uuid, params): """Delete operation for a specific plasmoid in the infinote pool :param Client client: The requesting client :param str uuid: The uuid of the plasmoid to operate on in the infinote pool :param dict params: The params that are used to succesfully perform the infinote operation """ self.infinote_pool.request_delete(client,'entities', uuid, params, self._signal_operation) def request_entity_undo(self, client, uuid, params): """Uno operation for a specific plasmoid in the infinote pool :param Client client: The requesting client :param str uuid: The uuid of the plasmoid to operate on in the infinote pool :param dict params: The params that are used to succesfully perform the infinote operation """ self.infinote_pool.request_undo(client,'entities', uuid, params, self._signal_operation) def update_remote_caret(self, client, uuid, params): """Move caret operation for a specific plasmoid in the infinote pool :param Client client: The requesting client :param str uuid: The uuid of the plasmoid to operate on in the infinote pool :param dict params: The params that are used to succesfully perform the infinote operation """ self.infinote_pool.update_caret(client,'entities', uuid, params, self._signal_caret) def _signal_presence(self, client, online, app_pool, item_id): client.remote('/data/pages/%s/%s/online/update/' % (app_pool, item_id),{'online':online}) def _signal_operation(self, client, app_pool, item_id, operation_type, params): client.remote('/data/pages/%s/%s/%s/' % (app_pool, item_id, operation_type), params) def _signal_caret(self, client, app_pool, item_id, params): client.remote('/data/pages/%s/%s/caret/' % (app_pool, item_id), params) def _get_pages(self, client): """Notify_others helper that renders and prepares the show plasmoids view""" anchors = PageAnchor.objects.all() entities = PageEntity.objects.all() main = render_to_string("pages/list_pages.html", {'anchors':anchors,'entities':entities}) tpl_params = {'anchors':anchors,'entities':entities} main = render_to_string("pages/list_pages.html", tpl_params) return [ {'data':{'dom':{'main':main}}}, {'main':{'tpl':'pages/list_pages.html','params':tpl_params}} ]