def __init__(self, module, rootNode=None, node=None, isSelector=False, *args, **kwargs): """ @param modul: Name of the modul we shall handle. Must be a list application! @type modul: string @param rootNode: The rootNode we shall display. If None, we try to select one. @type rootNode: String or None @param node: The node we shall display at start. Must be a child of rootNode @type node: String or None """ super(TreeWidget, self).__init__() self["class"].append("tree") self.module = module self.rootNode = rootNode self.node = node or rootNode self.actionBar = ActionBar(module, "tree") self.appendChild(self.actionBar) self.pathList = html5.Div() self.pathList["class"].append("breadcrumb") self.appendChild(self.pathList) self.entryFrame = SelectableDiv(self.nodeWidget, self.leafWidget) self.appendChild(self.entryFrame) self.entryFrame.selectionActivatedEvent.register(self) self._batchSize = 99 self._currentCursor = {"node": None, "leaf": None} self._currentRequests = [] self.rootNodeChangedEvent = EventDispatcher("rootNodeChanged") self.nodeChangedEvent = EventDispatcher("nodeChanged") self.isSelector = isSelector if self.rootNode: self.reloadData() else: NetworkService.request(self.module, "listRootNodes", successHandler=self.onSetDefaultRootNode) self.path = [] self.sinkEvent("onClick") #Proxy some events and functions of the original table for f in [ "selectionChangedEvent", "selectionActivatedEvent", "cursorMovedEvent", "getCurrentSelection", "selectionReturnEvent" ]: setattr(self, f, getattr(self.entryFrame, f)) self.actionBar.setActions(self.defaultActions + (["select", "close"] if isSelector else []))
def __init__(self, module, rootNode=None, isSelector=False, context=None, *args, **kwargs): """ @param module: Name of the modul we shall handle. Must be a hierarchy application! @type module: string @param rootNode: The repository we shall display. If none, we try to select one. @type rootNode: String or None """ super(HierarchyWidget, self).__init__() self.module = module self.rootNode = rootNode self.actionBar = ActionBar(module, "hierarchy") self.appendChild(self.actionBar) self.entryFrame = html5.Ol() self.entryFrame["class"].append("hierarchy") self.appendChild(self.entryFrame) self.selectionChangedEvent = EventDispatcher("selectionChanged") self.selectionActivatedEvent = EventDispatcher("selectionActivated") self.rootNodeChangedEvent = EventDispatcher("rootNodeChanged") self._currentCursor = None self._currentRequests = [] self.addClass("supports_drop") self.isSelector = isSelector self._expandedNodes = [] self.context = context if self.rootNode: self.reloadData() else: NetworkService.request(self.module, "listRootNodes", self.context or {}, successHandler=self.onSetDefaultRootNode, failureHandler=self.showErrorMsg) self.path = [] self.sinkEvent("onClick", "onDblClick") ##Proxy some events and functions of the original table #for f in ["selectionChangedEvent","selectionActivatedEvent","cursorMovedEvent","getCurrentSelection"]: # setattr( self, f, getattr(self.table,f)) self.actionBar.setActions( ["selectrootnode", "add", "edit", "clone", "delete"] + (["select", "close"] if isSelector else []) + ["reload"]) self.sinkEvent("onDrop", "onDragOver")
def __init__(self, modul, key): super(RepeatDatePopup, self).__init__() self.module = modul self.editIdx = RepeatDatePopup.__editIdx_ #Iternal counter to ensure unique ids RepeatDatePopup.__editIdx_ += 1 self.key = key self._lastData = {} #Dict of structure and values received self.closeOnSuccess = False h3 = html5.H3() h3["class"].append("modul_%s" % self.module) h3["class"].append("apptype_list") h3.appendChild(html5.TextNode(translate("create recurrent dates"))) self.wasInitialRequest = True self.actionbar = ActionBar( self.module, "list", "repeatdate") self.appendChild( self.actionbar ) self.form = html5.Form() self.appendChild(self.form) self.actionbar.setActions(["create.recurrent"]) self.reloadData()
def __init__(self, module, filter=None, columns=None, isSelector=False, filterID=None, filterDescr=None, batchSize=None, context=None, autoload=True, *args, **kwargs): """ @param module: Name of the modul we shall handle. Must be a list application! @type module: string """ if not module in conf["modules"].keys(): conf["mainWindow"].log( "error", translate("The module '{module}' does not exist.", module=module)) assert module in conf["modules"].keys() super(ListWidget, self).__init__() self._batchSize = batchSize or conf[ "batchSize"] # How many rows do we fetch at once? self.isDetaching = False #If set, this widget is beeing about to be removed - dont issue nextBatchNeeded requests self.module = module self.context = context self.actionBar = ActionBar(module, "list", currentAction="list") self.appendChild(self.actionBar) self.sideBar = SideBar() self.appendChild(self.sideBar) myView = None if filterID: if conf["modules"] and module in conf["modules"].keys(): if "views" in conf["modules"][module].keys( ) and conf["modules"][module]["views"]: for v in conf["modules"][module]["views"]: if v["__id"] == filterID: myView = v break if myView and "extendedFilters" in myView.keys( ) and myView["extendedFilters"]: self.appendChild(CompoundFilter(myView, module, embed=True)) checkboxes = (conf["modules"] and module in conf["modules"].keys() and "checkboxSelection" in conf["modules"][module].keys() and conf["modules"][module]["checkboxSelection"]) indexes = (conf["modules"] and module in conf["modules"].keys() and "indexes" in conf["modules"][module].keys() and conf["modules"][module]["indexes"]) self.table = DataTable(checkboxes=checkboxes, indexes=indexes, *args, **kwargs) self.appendChild(self.table) self._currentCursor = None self._structure = None self._currentRequests = [] self.columns = [] if isSelector and filter is None and columns is None: #Try to select a reasonable set of cols / filter if conf["modules"] and module in conf["modules"].keys(): tmpData = conf["modules"][module] if "columns" in tmpData.keys(): columns = tmpData["columns"] if "filter" in tmpData.keys(): filter = tmpData["filter"] self.table.setDataProvider(self) self.filter = filter.copy() if isinstance(filter, dict) else {} self.columns = columns[:] if isinstance(columns, list) else [] self.filterID = filterID #Hint for the sidebarwidgets which predefined filter is currently active self.filterDescr = filterDescr #Human-readable description of the current filter self._tableHeaderIsValid = False self.isSelector = isSelector #Proxy some events and functions of the original table for f in [ "selectionChangedEvent", "selectionActivatedEvent", "cursorMovedEvent", "tableChangedEvent", "getCurrentSelection" ]: setattr(self, f, getattr(self.table, f)) self.actionBar.setActions(self.getDefaultActions(myView)) if isSelector: self.selectionActivatedEvent.register(self) self.emptyNotificationDiv = html5.Div() self.emptyNotificationDiv.appendChild( html5.TextNode(translate("Currently no entries"))) self.emptyNotificationDiv["class"].append("emptynotification") self.appendChild(self.emptyNotificationDiv) self.emptyNotificationDiv["style"]["display"] = "none" self.table["style"]["display"] = "none" self.filterDescriptionSpan = html5.Span() self.appendChild(self.filterDescriptionSpan) self.filterDescriptionSpan["class"].append("filterdescription") self.updateFilterDescription() if autoload: self.reloadData()
class ListWidget(html5.Div): """ Provides the interface to list-applications. It acts as a data-provider for a DataTable and binds an action-bar to this table. """ def __init__(self, module, filter=None, columns=None, isSelector=False, filterID=None, filterDescr=None, batchSize=None, context=None, autoload=True, *args, **kwargs): """ @param module: Name of the modul we shall handle. Must be a list application! @type module: string """ if not module in conf["modules"].keys(): conf["mainWindow"].log( "error", translate("The module '{module}' does not exist.", module=module)) assert module in conf["modules"].keys() super(ListWidget, self).__init__() self._batchSize = batchSize or conf[ "batchSize"] # How many rows do we fetch at once? self.isDetaching = False #If set, this widget is beeing about to be removed - dont issue nextBatchNeeded requests self.module = module self.context = context self.actionBar = ActionBar(module, "list", currentAction="list") self.appendChild(self.actionBar) self.sideBar = SideBar() self.appendChild(self.sideBar) myView = None if filterID: if conf["modules"] and module in conf["modules"].keys(): if "views" in conf["modules"][module].keys( ) and conf["modules"][module]["views"]: for v in conf["modules"][module]["views"]: if v["__id"] == filterID: myView = v break if myView and "extendedFilters" in myView.keys( ) and myView["extendedFilters"]: self.appendChild(CompoundFilter(myView, module, embed=True)) checkboxes = (conf["modules"] and module in conf["modules"].keys() and "checkboxSelection" in conf["modules"][module].keys() and conf["modules"][module]["checkboxSelection"]) indexes = (conf["modules"] and module in conf["modules"].keys() and "indexes" in conf["modules"][module].keys() and conf["modules"][module]["indexes"]) self.table = DataTable(checkboxes=checkboxes, indexes=indexes, *args, **kwargs) self.appendChild(self.table) self._currentCursor = None self._structure = None self._currentRequests = [] self.columns = [] if isSelector and filter is None and columns is None: #Try to select a reasonable set of cols / filter if conf["modules"] and module in conf["modules"].keys(): tmpData = conf["modules"][module] if "columns" in tmpData.keys(): columns = tmpData["columns"] if "filter" in tmpData.keys(): filter = tmpData["filter"] self.table.setDataProvider(self) self.filter = filter.copy() if isinstance(filter, dict) else {} self.columns = columns[:] if isinstance(columns, list) else [] self.filterID = filterID #Hint for the sidebarwidgets which predefined filter is currently active self.filterDescr = filterDescr #Human-readable description of the current filter self._tableHeaderIsValid = False self.isSelector = isSelector #Proxy some events and functions of the original table for f in [ "selectionChangedEvent", "selectionActivatedEvent", "cursorMovedEvent", "tableChangedEvent", "getCurrentSelection" ]: setattr(self, f, getattr(self.table, f)) self.actionBar.setActions(self.getDefaultActions(myView)) if isSelector: self.selectionActivatedEvent.register(self) self.emptyNotificationDiv = html5.Div() self.emptyNotificationDiv.appendChild( html5.TextNode(translate("Currently no entries"))) self.emptyNotificationDiv["class"].append("emptynotification") self.appendChild(self.emptyNotificationDiv) self.emptyNotificationDiv["style"]["display"] = "none" self.table["style"]["display"] = "none" self.filterDescriptionSpan = html5.Span() self.appendChild(self.filterDescriptionSpan) self.filterDescriptionSpan["class"].append("filterdescription") self.updateFilterDescription() if autoload: self.reloadData() def updateFilterDescription(self): self.filterDescriptionSpan.removeAllChildren() if self.filterDescr: self.filterDescriptionSpan.appendChild( html5.TextNode(html5.utils.unescape(self.filterDescr))) def getDefaultActions(self, view=None): """ Returns the list of actions available in our actionBar """ defaultActions = ["add", "edit", "clone", "delete", "|", "preview", "selectfields"]\ + (["|", "select","close"] if self.isSelector else [])+["|", "reload","selectfilter"] #if not self.isSelector: # defaultActions += ["|", "exportcsv"] # Extended actions from view? if view and "actions" in view.keys(): if defaultActions[-1] != "|": defaultActions.append("|") defaultActions.extend(view["actions"] or []) # Extended Actions from config? elif conf["modules"] and self.module in conf["modules"].keys(): cfg = conf["modules"][self.module] if "actions" in cfg.keys() and cfg["actions"]: if defaultActions[-1] != "|": defaultActions.append("|") defaultActions.extend(cfg["actions"]) return defaultActions def showErrorMsg(self, req=None, code=None): """ Removes all currently visible elements and displayes an error message """ self.actionBar["style"]["display"] = "none" self.table["style"]["display"] = "none" errorDiv = html5.Div() errorDiv["class"].append("error_msg") if code and (code == 401 or code == 403): txt = translate("Access denied!") else: txt = translate("An unknown error occurred!") errorDiv["class"].append("error_code_%s" % (code or 0)) errorDiv.appendChild(html5.TextNode(txt)) self.appendChild(errorDiv) def onNextBatchNeeded(self): """ Requests the next rows from the server and feed them to the table. """ if self._currentCursor and not self.isDetaching: filter = {} if self.context: filter.update(self.context) filter.update(self.filter) filter["amount"] = self._batchSize filter["cursor"] = self._currentCursor self._currentRequests.append( NetworkService.request(self.module, "list", filter, successHandler=self.onCompletion, failureHandler=self.showErrorMsg, cacheable=True)) self._currentCursor = None else: self.table.setDataProvider(None) def onAttach(self): super(ListWidget, self).onAttach() NetworkService.registerChangeListener(self) def onDetach(self): self.isDetaching = True super(ListWidget, self).onDetach() NetworkService.removeChangeListener(self) def onDataChanged(self, module, **kwargs): """ Refresh our view if element(s) in this modul have changed """ if module and module != self.module: return self.reloadData() def reloadData(self): """ Removes all currently displayed data and refetches the first batch from the server. """ self.table.clear() self._currentCursor = None self._currentRequests = [] filter = {} if self.context: filter.update(self.context) filter.update(self.filter) filter["amount"] = self._batchSize self._currentRequests.append( NetworkService.request(self.module, "list", filter, successHandler=self.onCompletion, failureHandler=self.showErrorMsg, cacheable=True)) def setFilter(self, filter, filterID=None, filterDescr=None): """ Applies a new filter. """ self.filter = filter self.filterID = filterID self.filterDescr = filterDescr self.updateFilterDescription() self.reloadData() def setContext(self, context): """ Applies a new context. """ self.context = context self.reloadData() def getFilter(self): if self.filter: return ({k: v for k, v in self.filter.items()}) return ({}) def onCompletion(self, req): """ Pass the rows received to the datatable. @param req: The network request that succeed. """ if not req in self._currentRequests: return self._currentRequests.remove(req) self.actionBar.resetLoadingState() data = NetworkService.decode(req) if data["structure"] is None: if self.table.getRowCount(): self.table.setDataProvider( None) #We cant load any more results else: self.table["style"]["display"] = "none" self.emptyNotificationDiv["style"]["display"] = "" #self.element.innerHTML = "<center><strong>Keine Ergebnisse</strong></center>" return self.table["style"]["display"] = "" self.emptyNotificationDiv["style"]["display"] = "none" self._structure = data["structure"] if not self._tableHeaderIsValid: if not self.columns: self.columns = [] for boneName, boneInfo in data["structure"]: if boneInfo["visible"]: self.columns.append(boneName) self.setFields(self.columns) if data["skellist"] and "cursor" in data.keys(): self._currentCursor = data["cursor"] self.table.setDataProvider(self) else: self.table.setDataProvider(None) self.table.extend(data["skellist"]) def setFields(self, fields): if not self._structure: self._tableHeaderIsValid = False return boneInfoList = [] tmpDict = {key: bone for key, bone in self._structure} fields = [x for x in fields if x in tmpDict.keys()] self.columns = fields for boneName in fields: boneInfo = tmpDict[boneName] delegateFactory = viewDelegateSelector.select( self.module, boneName, tmpDict)(self.module, boneName, tmpDict) self.table.setCellRender(boneName, delegateFactory) boneInfoList.append(boneInfo) if conf["showBoneNames"]: self.table.setHeader(fields) else: self.table.setHeader([x.get("descr", "") for x in boneInfoList]) self.table.setShownFields(fields) rendersDict = {} for boneName in fields: boneInfo = tmpDict[boneName] delegateFactory = viewDelegateSelector.select( self.module, boneName, tmpDict)(self.module, boneName, tmpDict) rendersDict[boneName] = delegateFactory boneInfoList.append(boneInfo) self.table.setCellRenders(rendersDict) self._tableHeaderIsValid = True def getFields(self): return (self.columns[:]) def onSelectionActivated(self, table, selection): conf["mainWindow"].removeWidget(self) def activateCurrentSelection(self): """ Emits the selectionActivated event if there's currently a selection """ self.table.activateCurrentSelection() @staticmethod def canHandle(moduleName, moduleInfo): return moduleInfo["handler"] == "list" or moduleInfo[ "handler"].startswith("list.") @staticmethod def render(moduleName, adminInfo, context): filter = adminInfo.get("filter") columns = adminInfo.get("columns") return ListWidget(module=moduleName, filter=filter, columns=columns, context=context)
class RepeatDatePopup(html5.Div): __editIdx_ = 0 def __init__(self, modul, key): super(RepeatDatePopup, self).__init__() self.module = modul self.editIdx = RepeatDatePopup.__editIdx_ #Iternal counter to ensure unique ids RepeatDatePopup.__editIdx_ += 1 self.key = key self._lastData = {} #Dict of structure and values received self.closeOnSuccess = False h3 = html5.H3() h3["class"].append("modul_%s" % self.module) h3["class"].append("apptype_list") h3.appendChild(html5.TextNode(translate("create recurrent dates"))) self.wasInitialRequest = True self.actionbar = ActionBar( self.module, "list", "repeatdate") self.appendChild( self.actionbar ) self.form = html5.Form() self.appendChild(self.form) self.actionbar.setActions(["create.recurrent"]) self.reloadData() def reloadData(self): self.save({}) return def save(self, data): self.wasInitialRequest = not len(data)>0 if self.module=="_tasks": return #FIXME! else: if not data: NetworkService.request(self.module,"view/%s" % self.key, successHandler=self.setData, failureHandler=self.showErrorMsg) else: NetworkService.request(self.module, "add", data, secure=len(data)>0, successHandler=self.setData, failureHandler=self.showErrorMsg ) def setData( self, request=None, data=None, ignoreMissing=False ): """ Rebuilds the UI according to the skeleton received from server @param request: A finished NetworkService request @type request: NetworkService @type data: dict @param data: The data received """ assert (request or data) if request: data = NetworkService.decode( request ) try: skelStructure = {k: v for k, v in data["structure"]} except AttributeError: NetworkService.notifyChange(self.module) conf["mainWindow"].removeWidget( self ) return print print("data", data) print("action", data["action"]) if "action" in data and (data["action"] in ["addSuccess", "editSuccess"]): NetworkService.notifyChange(self.module) logDiv = html5.Div() logDiv["class"].append("msg") spanMsg = html5.Span() spanMsg.appendChild( html5.TextNode( translate("Entry saved!") )) spanMsg["class"].append("msgspan") logDiv.appendChild(spanMsg) if self.module in conf["modules"].keys(): spanMsg = html5.Span() spanMsg.appendChild( html5.TextNode( conf["modules"][self.module]["name"] )) spanMsg["class"].append("modulspan") logDiv.appendChild(spanMsg) if "values" in data.keys() and "name" in data["values"].keys(): spanMsg = html5.Span() spanMsg.appendChild( html5.TextNode( str(data["values"]["name"]) )) spanMsg["class"].append("namespan") logDiv.appendChild(spanMsg) conf["mainWindow"].log("success",logDiv) if self.closeOnSuccess: conf["mainWindow"].removeWidget( self ) return self.clear() # self.bones = {} self.reloadData() return self.clear() self.actionbar.resetLoadingState() self.dataCache = data fieldSets = {} cat = "byweek" fs = html5.Fieldset() fs["class"] = cat if cat=="byweek": fs["class"].append("active") fs["name"] = cat legend = html5.Legend() fshref = fieldset_A() fshref.appendChild(html5.TextNode(cat) ) legend.appendChild( fshref ) fs.appendChild(legend) section = html5.Section() fs.appendChild(section) fs._section = section fieldSets[ cat ] = fs self.dtstart = data["values"]["startdate"] startdateLabel = html5.Label("Termin") startdateLabel["class"].append("termin") startdateLabel["class"].append("date") startdate_id = "vi_%s_%s_edit_bn_%s" % ( self.editIdx, self.module, "repeatdate") startdateLabel["for"] = startdate_id startdate = date.DateViewBoneDelegate("termin", "startdate", skelStructure).render(data["values"], "startdate") startdate["id"] = startdate_id containerDiv = html5.Div() containerDiv.appendChild(startdateLabel) containerDiv.appendChild(startdate) containerDiv["class"].append("bone") containerDiv["class"].append("bone_startdate") containerDiv["class"].append("date") fieldSets[ cat ]._section.appendChild( containerDiv ) countLabel = html5.Label("Wiederholungen") countLabel["class"].append("count") countLabel["class"].append("numeric") count_id = "vi_%s_%s_edit_bn_%s" % ( self.editIdx, self.module, "count") countLabel["for"] = count_id self.count = html5.Input() self.count["id"] = count_id containerDiv2 = html5.Div() containerDiv2["class"].append("bone") containerDiv2["class"].append("bone_count") containerDiv2["class"].append("date") containerDiv2.appendChild(countLabel) containerDiv2.appendChild(self.count) # containerDiv3 = html5.Div() # self.byweekday = list() # for key, value in [["MO", "Mo"], ["TU", "Di"], ["TH", "Mi"], ["WE", "Do"], ["FR", "Fr"], ["SA", "Sa"], ["SU", "So"]]: # alabel=html5.Label() # acheckbox=html5.Input() # acheckbox["type"]="checkbox" # acheckbox["name"]=key # alabel.appendChild(acheckbox) # aspan=html5.Span() # aspan.element.innerHTML=value # alabel.appendChild(aspan) # containerDiv3.appendChild(alabel) # containerDiv2["class"].append("bone") # containerDiv2["class"].append("bone_count") # containerDiv2["class"].append("byweekday") # self.byweekday.append(acheckbox) fieldSets[ cat ]._section.appendChild(containerDiv2) # fieldSets[ cat ]._section.appendChild(containerDiv3) for (k,v) in fieldSets.items(): if not "active" in v["class"]: v["class"].append("active") tmpList = [(k,v) for (k,v) in fieldSets.items()] tmpList.sort( key=lambda x:x[0]) for k,v in tmpList: self.form.appendChild( v ) v._section = None def clear(self): """ Removes all visible bones/forms/fieldsets. """ for c in self.form._children[ : ]: self.form.removeChild( c ) def showErrorMsg(self, req=None, code=None): """ Removes all currently visible elements and displayes an error message """ self.actionbar["style"]["display"] = "none" self.form["style"]["display"] = "none" errorDiv = html5.Div() errorDiv["class"].append("error_msg") if code and (code==401 or code==403): txt = translate("Access denied!") else: txt = translate("An unknown error occurred!") errorDiv["class"].append("error_code_%s" % (code or 0)) errorDiv.appendChild( html5.TextNode( txt ) ) self.appendChild( errorDiv ) def doSave( self, closeOnSuccess=False): self.closeOnSuccess = closeOnSuccess data ={"count": self.count._getValue(), "kind" : "2", "dtstart" : self.dtstart # , "byweekday" : [box["name"] for box in self.byweekday if box["checked"]] } # r = rrule.rrule(2, dtstart=datetime(2014, 7,1, 18,00), count=7) NetworkService.request(self.module, "addrecurrent/%s" % self.key, data, secure=True, successHandler=self.setData, failureHandler=self.showErrorMsg )
class TreeWidget(html5.Div): nodeWidget = NodeWidget leafWidget = LeafWidget defaultActions = [ "add.node", "add.leaf", "selectrootnode", "edit", "delete", "reload" ] def __init__(self, module, rootNode=None, node=None, selectMode=None, *args, **kwargs): """ :param module: Name of the module we shall handle. Must be a list application! :type module: str :param rootNode: The rootNode we shall display. If None, we try to select one. :type rootNode: str or None :param node: The node we shall display at start. Must be a child of rootNode :type node: str or None """ super(TreeWidget, self).__init__() self.addClass("tree") self.module = module self.rootNode = rootNode self.node = node or rootNode self.actionBar = ActionBar(module, "tree") self.appendChild(self.actionBar) self.pathList = html5.Ul() self.pathList["class"].append("breadcrumb") self.appendChild(self.pathList) self.entryFrame = SelectionContainer(self.nodeWidget, self.leafWidget) self.appendChild(self.entryFrame) self.entryFrame.selectionActivatedEvent.register(self) self._batchSize = 99 self._currentCursor = {"node": None, "leaf": None} self._currentRequests = [] self.rootNodeChangedEvent = EventDispatcher("rootNodeChanged") self.nodeChangedEvent = EventDispatcher("nodeChanged") assert selectMode in [ None, "single", "multi", "single.leaf", "single.node", "multi.leaf", "multi.node" ] self.selectMode = selectMode if self.rootNode: self.reloadData() self.rebuildPath() else: NetworkService.request(self.module, "listRootNodes", successHandler=self.onSetDefaultRootNode) self.sinkEvent("onClick") # Proxy some events and functions of the original table for f in [ "selectionChangedEvent", "selectionActivatedEvent", "cursorMovedEvent", "getCurrentSelection", "selectionReturnEvent" ]: setattr(self, f, getattr(self.entryFrame, f)) self.actionBar.setActions(self.defaultActions + ( ["select", "close"] if self.selectMode else [])) def showErrorMsg(self, req=None, code=None): """ Removes all currently visible elements and displays an error message """ self.actionBar["style"]["display"] = "none" self.entryFrame["style"]["display"] = "none" errorDiv = html5.Div() errorDiv["class"].append("error_msg") if code and (code == 401 or code == 403): txt = "Access denied!" else: txt = "An unknown error occurred!" errorDiv["class"].append("error_code_%s" % (code or 0)) errorDiv.appendChild(html5.TextNode(txt)) self.appendChild(errorDiv) def onAttach(self): super(TreeWidget, self).onAttach() NetworkService.registerChangeListener(self) def onDetach(self): super(TreeWidget, self).onDetach() NetworkService.removeChangeListener(self) def onDataChanged(self, module, **kwargs): if module != self.module: isRootNode = False for k, v in conf["modules"].items(): if (k == module and v.get("handler") == "list" and v.get("rootNodeOf") == self.module): isRootNode = True break if not isRootNode: return if "selectrootnode" in self.actionBar.widgets.keys(): self.actionBar.widgets["selectrootnode"].update() self.reloadData() def onSelectionActivated(self, div, selection): if not selection: return item = selection[0] if item.skelType == "node": self.setNode(item.data["key"]) elif item.skelType == "leaf" and "leaf" in (self.selectMode or ""): self.returnCurrentSelection() def activateCurrentSelection(self): return self.entryFrame.activateCurrentSelection() def returnCurrentSelection(self): conf["mainWindow"].removeWidget(self) return self.entryFrame.returnCurrentSelection() def onClick(self, event): super(TreeWidget, self).onClick(event) for c in self.pathList._children: # Test if the user clicked inside the path-list if html5.utils.doesEventHitWidgetOrParents(event, c): self.setNode(c.data["key"]) return def onSetDefaultRootNode(self, req): data = NetworkService.decode(req) if len(data) > 0: self.setRootNode(data[0]["key"], self.node) def setRootNode(self, rootNode, node=None): self.rootNode = rootNode self.node = node or rootNode self.rootNodeChangedEvent.fire(rootNode) if node: self.nodeChangedEvent.fire(node) self.reloadData() self.rebuildPath() def setNode(self, node): self.node = node self.nodeChangedEvent.fire(node) self.reloadData() self.rebuildPath() def rebuildPath(self): """ Rebuild the displayed path-list. """ self.pathList.removeAllChildren() NetworkService.request(self.module, "view/node/%s" % self.node, successHandler=self.onPathRequestSucceded) def onPathRequestSucceded(self, req): """ Rebuild the displayed path-list according to request data """ answ = NetworkService.decode(req) skel = answ["values"] if skel["parentdir"] and skel["parentdir"] != skel["key"]: c = self.nodeWidget(self.module, skel, answ["structure"]) NetworkService.request(self.module, "view/node/%s" % skel["parentdir"], successHandler=self.onPathRequestSucceded) else: c = self.nodeWidget(self.module, { "key": self.rootNode, "name": "root" }, []) c.addClass("is_rootnode") self.pathList.prependChild(c) def reloadData(self, paramsOverride=None): assert self.node is not None, "reloadData called while self.node is None" self.entryFrame.clear() self._currentRequests = [] if paramsOverride: params = paramsOverride.copy() else: params = {"node": self.node} if "amount" not in params: params["amount"] = self._batchSize r = NetworkService.request(self.module, "list/node", params, successHandler=self.onRequestSucceded, failureHandler=self.showErrorMsg) r.reqType = "node" self._currentRequests.append(r) r = NetworkService.request(self.module, "list/leaf", params, successHandler=self.onRequestSucceded, failureHandler=self.showErrorMsg) r.reqType = "leaf" self._currentRequests.append(r) from handler.tree import TreeHandler # import must be here, otherwise it throws an importError if isinstance(conf["mainWindow"].currentPane, TreeHandler): conf["theApp"].setPath(self.module + "/list/" + self.node) def onRequestSucceded(self, req): if not req in self._currentRequests: return self._currentRequests.remove(req) data = NetworkService.decode(req) for skel in data["skellist"]: if req.reqType == "node": n = self.nodeWidget(self.module, skel, data["structure"]) else: n = self.leafWidget(self.module, skel, data["structure"]) self.entryFrame.appendChild(n) self.entryFrame.sortChildren(self.getChildKey) if "cursor" in data.keys() and len( data["skellist"]) == req.params["amount"]: self._currentCursor[req.reqType] = data["cursor"] req.params["cursor"] = data["cursor"] r = NetworkService.request(self.module, "list/%s" % req.reqType, req.params, successHandler=self.onRequestSucceded, failureHandler=self.showErrorMsg) r.reqType = req.reqType self._currentRequests.append(r) else: self._currentCursor[req.reqType] = None self.actionBar.resetLoadingState() def getChildKey(self, widget): """ Derives a string used to sort the entries in our entryframe """ name = (widget.data.get("name") or "").lower() if isinstance(widget, self.nodeWidget): return "0-%s" % name elif isinstance(widget, self.leafWidget): return "1-%s" % name else: return "2-" @staticmethod def canHandle(moduleName, moduleInfo): return moduleInfo["handler"].startswith("tree.") @staticmethod def render(moduleName, adminInfo, context): rootNode = context.get(conf["vi.context.prefix"] + "rootNode") if context else None return TreeWidget(module=moduleName, rootNode=rootNode, context=context)
def __init__(self, module, applicationType, key=0, node=None, skelType=None, clone=False, hashArgs=None, context=None, logaction = "Entry saved!", *args, **kwargs): """ Initialize a new Edit or Add-Widget for the given module. :param module: Name of the module :type module: str :param applicationType: Defines for what application this Add / Edit should be created. This hides additional complexity introduced by the hierarchy / tree-application :type applicationType: Any of EditWidget.appList, EditWidget.appHierarchy, EditWidget.appTree or EditWidget.appSingleton :param id: ID of the entry. If none, it will add a new Entry. :type id: Number :param rootNode: If applicationType==EditWidget.appHierarchy, the new entry will be added under this node, if applicationType==EditWidget,appTree the final node is derived from this and the path-parameter. Has no effect if applicationType is not appHierarchy or appTree or if an id have been set. :type rootNode: str :param path: Specifies the path from the rootNode for new entries in a treeApplication :type path: str :param clone: If true, it will load the values from the given id, but will save a new entry (i.e. allows "cloning" an existing entry) :type clone: bool :param hashArgs: Dictionary of parameters (usually supplied by the window.hash property) which should prefill values. :type hashArgs: dict """ if not module in conf["modules"].keys(): conf["mainWindow"].log("error", translate("The module '{module}' does not exist.", module=module)) assert module in conf["modules"].keys() super(EditWidget, self ).__init__(*args, **kwargs) self.module = module # A Bunch of santy-checks, as there is a great chance to mess around with this widget assert applicationType in [ EditWidget.appList, EditWidget.appHierarchy, EditWidget.appTree, EditWidget.appSingleton ] #Invalid Application-Type? if applicationType==EditWidget.appHierarchy or applicationType==EditWidget.appTree: assert key is not None or node is not None #Need either an id or an node if clone: assert key is not None #Need an id if we should clone an entry assert not applicationType==EditWidget.appSingleton # We cant clone a singleton if applicationType==EditWidget.appHierarchy or applicationType==EditWidget.appTree: assert node is not None #We still need a rootNode for cloning if applicationType==EditWidget.appTree: assert node is not None #We still need a path for cloning #FIXME self.clone_of = key else: self.clone_of = None # End santy-checks self.editIdx = EditWidget.__editIdx_ #Internal counter to ensure unique ids EditWidget.__editIdx_ += 1 self.applicationType = applicationType self.key = key self.mode = "edit" if self.key or applicationType == EditWidget.appSingleton else "add" self.modified = False self.node = node self.skelType = skelType self.clone = clone self.bones = {} self.closeOnSuccess = False self.logaction = logaction self.sinkEvent("onChange") self.context = context self.views = {} self._lastData = {} #Dict of structure and values received if hashArgs: self._hashArgs = parseHashParameters(hashArgs) else: self._hashArgs = None self.editTaskID = None self.wasInitialRequest = True #Wherever the last request attempted to save data or just fetched the form # Action bar self.actionbar = ActionBar(self.module, self.applicationType, (self.mode if not clone else "clone")) self.appendChild(self.actionbar) editActions = [] if self.mode == "edit": editActions.append("refresh") if module in conf["modules"] and conf["modules"][module]: editActions.extend(conf["modules"][module].get("editActions", [])) if applicationType == EditWidget.appSingleton: self.actionbar.setActions(["save.singleton"] + editActions) else: self.actionbar.setActions(["save.close", "save.continue"] + editActions) # Set path if applicationType == EditWidget.appSingleton: conf["theApp"].setPath(module + "/" + self.mode) elif self.mode == "edit": conf["theApp"].setPath(module + "/" + (self.mode if not clone else "clone") + "/" + self.key) else: conf["theApp"].setPath(module + "/" + self.mode) # Input form self.form = html5.Form() self.appendChild(self.form) # Engage self.reloadData()
class EditWidget(html5.Div): appList = "list" appHierarchy = "hierarchy" appTree = "tree" appSingleton = "singleton" __editIdx_ = 0 #Internal counter to ensure unique ids def __init__(self, module, applicationType, key=0, node=None, skelType=None, clone=False, hashArgs=None, context=None, logaction = "Entry saved!", *args, **kwargs): """ Initialize a new Edit or Add-Widget for the given module. :param module: Name of the module :type module: str :param applicationType: Defines for what application this Add / Edit should be created. This hides additional complexity introduced by the hierarchy / tree-application :type applicationType: Any of EditWidget.appList, EditWidget.appHierarchy, EditWidget.appTree or EditWidget.appSingleton :param id: ID of the entry. If none, it will add a new Entry. :type id: Number :param rootNode: If applicationType==EditWidget.appHierarchy, the new entry will be added under this node, if applicationType==EditWidget,appTree the final node is derived from this and the path-parameter. Has no effect if applicationType is not appHierarchy or appTree or if an id have been set. :type rootNode: str :param path: Specifies the path from the rootNode for new entries in a treeApplication :type path: str :param clone: If true, it will load the values from the given id, but will save a new entry (i.e. allows "cloning" an existing entry) :type clone: bool :param hashArgs: Dictionary of parameters (usually supplied by the window.hash property) which should prefill values. :type hashArgs: dict """ if not module in conf["modules"].keys(): conf["mainWindow"].log("error", translate("The module '{module}' does not exist.", module=module)) assert module in conf["modules"].keys() super(EditWidget, self ).__init__(*args, **kwargs) self.module = module # A Bunch of santy-checks, as there is a great chance to mess around with this widget assert applicationType in [ EditWidget.appList, EditWidget.appHierarchy, EditWidget.appTree, EditWidget.appSingleton ] #Invalid Application-Type? if applicationType==EditWidget.appHierarchy or applicationType==EditWidget.appTree: assert key is not None or node is not None #Need either an id or an node if clone: assert key is not None #Need an id if we should clone an entry assert not applicationType==EditWidget.appSingleton # We cant clone a singleton if applicationType==EditWidget.appHierarchy or applicationType==EditWidget.appTree: assert node is not None #We still need a rootNode for cloning if applicationType==EditWidget.appTree: assert node is not None #We still need a path for cloning #FIXME self.clone_of = key else: self.clone_of = None # End santy-checks self.editIdx = EditWidget.__editIdx_ #Internal counter to ensure unique ids EditWidget.__editIdx_ += 1 self.applicationType = applicationType self.key = key self.mode = "edit" if self.key or applicationType == EditWidget.appSingleton else "add" self.modified = False self.node = node self.skelType = skelType self.clone = clone self.bones = {} self.closeOnSuccess = False self.logaction = logaction self.sinkEvent("onChange") self.context = context self.views = {} self._lastData = {} #Dict of structure and values received if hashArgs: self._hashArgs = parseHashParameters(hashArgs) else: self._hashArgs = None self.editTaskID = None self.wasInitialRequest = True #Wherever the last request attempted to save data or just fetched the form # Action bar self.actionbar = ActionBar(self.module, self.applicationType, (self.mode if not clone else "clone")) self.appendChild(self.actionbar) editActions = [] if self.mode == "edit": editActions.append("refresh") if module in conf["modules"] and conf["modules"][module]: editActions.extend(conf["modules"][module].get("editActions", [])) if applicationType == EditWidget.appSingleton: self.actionbar.setActions(["save.singleton"] + editActions) else: self.actionbar.setActions(["save.close", "save.continue"] + editActions) # Set path if applicationType == EditWidget.appSingleton: conf["theApp"].setPath(module + "/" + self.mode) elif self.mode == "edit": conf["theApp"].setPath(module + "/" + (self.mode if not clone else "clone") + "/" + self.key) else: conf["theApp"].setPath(module + "/" + self.mode) # Input form self.form = html5.Form() self.appendChild(self.form) # Engage self.reloadData() def onDetach(self): utils.setPreventUnloading(False) super(EditWidget, self).onDetach() def onAttach(self): super(EditWidget, self).onAttach() utils.setPreventUnloading(True) def performLogics(self): fields = self.serializeForDocument() for key, desc in self.dataCache["structure"]: if desc.get("params") and desc["params"]: for event in ["logic.visibleIf", "logic.readonlyIf", "logic.evaluate"]: #add more here! logic = desc["params"].get(event) if not logic: continue # Compile logic at first run if isinstance(logic, str): desc["params"][event] = conf["logics"].compile(logic) if desc["params"][event] is None: alert("ViUR logics: Parse error in >%s<" % logic) continue logic = desc["params"][event] res = conf["logics"].execute(logic, fields) if event == "logic.evaluate": self.bones[key].unserialize({key: res}) elif res: if event == "logic.visibleIf": self.containers[key].show() elif event == "logic.readonlyIf": self.containers[key].disable() # add more here... else: if event == "logic.visibleIf": self.containers[key].hide() elif event == "logic.readonlyIf": self.containers[key].enable() # add more here... def onChange(self, event): self.modified = True DeferredCall(self.performLogics) def onBoneChange(self, bone): self.modified = True DeferredCall(self.performLogics) def showErrorMsg(self, req=None, code=None): """ Removes all currently visible elements and displays an error message """ try: print(req.result) print(NetworkService.decode(req)) except: pass if code and (code==401 or code==403): txt = translate("Access denied!") else: txt = translate("An error occurred: {code}", code=code or 0) conf["mainWindow"].log("error", txt) if self.wasInitialRequest: conf["mainWindow"].removeWidget(self) def reloadData(self): self.save({}) def save(self, data): """ Creates the actual NetworkService request used to transmit our data. If data is None, it fetches a clean add/edit form. :param data: The values to transmit or None to fetch a new, clean add/edit form. :type data: dict or None """ self.wasInitialRequest = not len(data) > 0 if self.context: # Data takes precedence over context. ndata = self.context.copy() ndata.update(data.copy()) data = ndata if self.module=="_tasks": NetworkService.request(None, "/vi/%s/execute/%s" % (self.module, self.key), data, secure=not self.wasInitialRequest, successHandler=self.setData, failureHandler=self.showErrorMsg) elif self.applicationType == EditWidget.appList: ## Application: List if self.key and (not self.clone or self.wasInitialRequest): NetworkService.request(self.module, "edit/%s" % self.key, data, secure=not self.wasInitialRequest, successHandler=self.setData, failureHandler=self.showErrorMsg) else: NetworkService.request(self.module, "add", data, secure=not self.wasInitialRequest, successHandler=self.setData, failureHandler=self.showErrorMsg ) elif self.applicationType == EditWidget.appHierarchy: ## Application: Hierarchy if self.key and (not self.clone or self.wasInitialRequest): NetworkService.request(self.module, "edit/%s" % self.key, data, secure=not self.wasInitialRequest, successHandler=self.setData, failureHandler=self.showErrorMsg) else: NetworkService.request(self.module, "add/%s" % self.node, data, secure=not self.wasInitialRequest, successHandler=self.setData, failureHandler=self.showErrorMsg) elif self.applicationType == EditWidget.appTree: ## Application: Tree if self.key and not self.clone: NetworkService.request(self.module, "edit/%s/%s" % (self.skelType, self.key), data, secure=not self.wasInitialRequest, successHandler=self.setData, failureHandler=self.showErrorMsg) else: NetworkService.request(self.module, "add/%s/%s" % (self.skelType, self.node), data, secure=not self.wasInitialRequest, successHandler=self.setData, failureHandler=self.showErrorMsg) elif self.applicationType == EditWidget.appSingleton: ## Application: Singleton NetworkService.request(self.module, "edit", data, secure=not self.wasInitialRequest, successHandler=self.setData, failureHandler=self.showErrorMsg) else: raise NotImplementedError() #Should never reach this def clear(self): """ Removes all visible bones/forms/fieldsets. """ for c in self.form._children[ : ]: self.form.removeChild( c ) def closeOrContinue(self, sender=None ): NetworkService.notifyChange(self.module, key=self.key, action=self.mode) if self.closeOnSuccess: if self.module == "_tasks": self.parent().close() return conf["mainWindow"].removeWidget(self) return self.clear() self.bones = {} if self.mode == "add": self.key = 0 self.reloadData() def doCloneHierarchy(self, sender=None ): if self.applicationType == EditWidget.appHierarchy: NetworkService.request( self.module, "clone", { "fromRepo" : self.node, "toRepo" : self.node, "fromParent" : self.clone_of, "toParent" : self.key }, secure=True, successHandler=self.cloneComplete ) else: NetworkService.request( conf[ "modules" ][ self.module ][ "rootNodeOf" ], "clone", { "fromRepo" : self.clone_of, "toRepo" : self.key }, secure=True, successHandler=self.cloneComplete ) def cloneComplete(self, request): logDiv = html5.Div() logDiv["class"].append("msg") spanMsg = html5.Span() spanMsg.appendChild( html5.TextNode( translate( u"The hierarchy will be cloned in the background." ) ) ) spanMsg["class"].append("msgspan") logDiv.appendChild(spanMsg) conf["mainWindow"].log("success",logDiv) self.closeOrContinue() def setData(self, request=None, data=None, ignoreMissing=False, askHierarchyCloning=True): """ Rebuilds the UI according to the skeleton received from server :param request: A finished NetworkService request :type request: NetworkService :type data: dict :param data: The data received """ assert (request or data) if request: data = NetworkService.decode(request) if "action" in data and (data["action"] == "addSuccess" or data["action"] == "editSuccess"): self.modified = False logDiv = html5.Div() logDiv["class"].append("msg") spanMsg = html5.Span() spanMsg.appendChild( html5.TextNode( translate( self.logaction ) ) ) spanMsg["class"].append("msgspan") logDiv.appendChild(spanMsg) if self.module in conf["modules"].keys(): spanMsg = html5.Span() if self.module.startswith( "_" ): spanMsg.appendChild( html5.TextNode( self.key ) ) else: spanMsg.appendChild( html5.TextNode( conf["modules"][self.module]["name"] )) spanMsg["class"].append("modulespan") logDiv.appendChild(spanMsg) if "values" in data.keys() and "name" in data["values"].keys(): spanMsg = html5.Span() name = data["values"].get("name") or data["values"].get("key", "") if isinstance(name, dict): if conf["currentlanguage"] in name.keys(): name = name[conf["currentlanguage"]] else: name = name.values() if isinstance(name, list): name = ", ".join(name) spanMsg.appendChild(html5.TextNode(str(html5.utils.unescape(name)))) spanMsg["class"].append("namespan") logDiv.appendChild(spanMsg) try: self.key = data["values"]["key"] except: self.key = None conf["mainWindow"].log("success",logDiv) if askHierarchyCloning and self.clone: # for lists, which are rootNode entries of hierarchies, ask to clone entire hierarchy if self.applicationType == EditWidget.appList and "rootNodeOf" in conf[ "modules" ][ self.module ]: YesNoDialog( translate( u"Do you want to clone the entire hierarchy?" ), yesCallback=self.doCloneHierarchy, noCallback=self.closeOrContinue ) return # for cloning within a hierarchy, ask for cloning all subentries. elif self.applicationType == EditWidget.appHierarchy: YesNoDialog( translate( u"Do you want to clone all subentries of this item?" ), yesCallback=self.doCloneHierarchy, noCallback=self.closeOrContinue ) return self.closeOrContinue() return #Clear the UI self.clear() self.bones = {} self.views = {} self.containers = {} self.actionbar.resetLoadingState() self.dataCache = data self.modified = False tmpDict = {k: v for k, v in data["structure"]} fieldSets = {} firstCat = None currRow = 0 hasMissing = False defaultCat = conf["modules"][self.module].get("visibleName", self.module) contextVariable = conf["modules"][self.module].get("editContext") if self.mode == "edit" and contextVariable: if not self.context: self.context = {} if "=" in contextVariable: contextVariable, contextKey = contextVariable.split("=", 1) else: contextKey = "key" self.context.update({ contextVariable: data["values"].get(contextKey) }) for key, bone in data["structure"]: cat = defaultCat #meow! if ("params" in bone.keys() and isinstance(bone["params"], dict) and "category" in bone["params"].keys()): cat = bone["params"]["category"] if not cat in fieldSets.keys(): fieldSets[cat] = EditWidgetFieldset(cat) wdgGen = editBoneSelector.select(self.module, key, tmpDict) widget = wdgGen.fromSkelStructure(self.module, key, tmpDict) widget["id"] = "vi_%s_%s_%s_%s_bn_%s" % (self.editIdx, self.module, self.mode, cat, key) if "setContext" in dir(widget) and callable(widget.setContext): widget.setContext(self.context) if "changeEvent" in dir(widget): widget.changeEvent.register(self) descrLbl = html5.Label(key if conf["showBoneNames"] else bone.get("descr", key)) descrLbl["class"].append(key) descrLbl["class"].append(bone["type"].replace(".","_")) # Elements if ("params" in bone.keys() and isinstance(bone["params"], dict) and "elements.source" in bone["params"].keys()): descrLbl.addClass("elements-%s" % bone["params"]["elements.source"]) descrLbl["for"] = "vi_%s_%s_%s_%s_bn_%s" % (self.editIdx, self.module, self.mode, cat, key) if bone["required"] or (bone.get("unique") and bone["error"]): descrLbl["class"].append("is_required") if bone["error"] is not None: descrLbl["class"].append("is_invalid") descrLbl["title"] = bone["error"] fieldSets[ cat ]["class"].append("is_incomplete") # Put info into message center conf["mainWindow"].log("info", "%s: %s" % (bone.get("descr", key), translate(bone["error"]))) hasMissing = True elif bone["error"] is None and not self.wasInitialRequest: descrLbl["class"].append("is_valid") if isinstance(bone["error"], dict): widget.setExtendedErrorInformation(bone["error"]) containerDiv = html5.Div() containerDiv.appendChild(descrLbl) containerDiv.appendChild(widget) if ("params" in bone.keys() and isinstance(bone["params"], dict) and "tooltip" in bone["params"].keys()): containerDiv.appendChild(ToolTip(longText=bone["params"]["tooltip"])) fieldSets[cat]._section.appendChild(containerDiv) containerDiv.addClass("bone", "bone_%s" % key, bone["type"].replace(".","_")) if "." in bone["type"]: for t in bone["type"].split("."): containerDiv["class"].append(t) currRow += 1 self.bones[key] = widget self.containers[key] = containerDiv #Hide invisible bones or logic-flavored bones with their default desire if not bone["visible"] or (bone["params"] and bone["params"].get("logic.visibleIf")): self.containers[key].hide() elif bone["visible"] and not firstCat: firstCat = fieldSets[cat] # NO elif! if bone["params"] and bone["params"].get("logic.readonlyIf"): self.containers[key].disable() # Hide all fieldSets where all fields are invisible for fs in fieldSets.values(): fs.checkVisibility() # Show default category if firstCat: firstCat.removeClass("inactive") firstCat.addClass("active") tmpList = [(k,v) for (k,v) in fieldSets.items()] tmpList.sort(key=lambda x:x[0]) for k, v in tmpList: self.form.appendChild( v ) v._section = None # Views views = conf["modules"][self.module].get("editViews") if self.mode == "edit" and isinstance(views, list): for view in views: vmodule = view.get("module") vvariable = view.get("context") vclass = view.get("class") vtitle = view.get("title") vcolumns = view.get("columns") vfilter = view.get("filter") vactions = view.get("actions") if not vmodule: print("Misconfiured view: %s" % view) continue if vmodule not in conf["modules"]: print("Module '%s' is not described." % vmodule) continue vdescr = conf["modules"][vmodule] fs = EditWidgetFieldset(vmodule, vtitle or vdescr.get("name", vmodule)) fs.addClass("editview", "inactive") if vclass: fs.addClass(*vclass) fieldSets[vmodule] = EditWidgetFieldset(vmodule, vtitle or vdescr.get("name", vmodule)) if vvariable: context = self.context.copy() if self.context else {} if "=" in vvariable: vkey, vvalue = vvariable.split("=", 1) if vvalue[0] == "$": vvalue = data["values"].get(vvalue[1:]) else: vkey = vvariable vvalue = data["values"].get("key") context[vkey] = vvalue else: context = self.context self.views[vmodule] = ListWidget( vmodule, filter=vfilter or vdescr.get("filter", {}), columns=vcolumns or vdescr.get("columns"), context=context, actions=vactions ) fs._section.appendChild(self.views[vmodule]) self.form.appendChild(fs) self.unserialize(data["values"]) if self._hashArgs: #Apply the default values (if any) self.unserialize(self._hashArgs) self._hashArgs = None self._lastData = data if hasMissing and not self.wasInitialRequest: conf["mainWindow"].log("warning",translate("Could not save entry!")) DeferredCall(self.performLogics) def unserialize(self, data = None): """ Applies the actual data to the bones. """ for bone in self.bones.values(): if "setContext" in dir(bone) and callable(bone.setContext): bone.setContext(self.context) if data is not None: bone.unserialize(data) def serializeForPost(self, validityCheck = False): res = {} for key, bone in self.bones.items(): if key == "key": continue #ignore the key, it is stored in self.key! try: res.update(bone.serializeForPost()) except InvalidBoneValueException: if validityCheck: # Fixme: Bad hack.. lbl = bone.parent()._children[0] if "is_valid" in lbl["class"]: lbl["class"].remove("is_valid") lbl["class"].append("is_invalid") self.actionbar.resetLoadingState() return None return res def serializeForDocument(self): res = self._lastData.get("values", {}) for key, bone in self.bones.items(): try: res.update(bone.serializeForDocument()) except InvalidBoneValueException as e: res[key] = str(e) return res def doSave( self, closeOnSuccess=False, *args, **kwargs ): """ Starts serializing and transmitting our values to the server. """ self.closeOnSuccess = closeOnSuccess res = self.serializeForPost(True) if res is None: return None self.save(res)
class HierarchyWidget(html5.Div): """ Displays a hierarchy where entries are direct children of each other. (There's only one type on entries in a HierarchyApplication. If you need to differentiate between nodes/leafs use a TreeApplication instead) """ def __init__(self, module, rootNode=None, selectMode=None, context=None, *args, **kwargs): """ :param module: Name of the module we shall handle. Must be a hierarchy application! :type module: str :param rootNode: The repository we shall display. If none, we try to select one. :type rootNode: str or None """ super( HierarchyWidget, self ).__init__( ) self.module = module self.rootNode = rootNode self.actionBar = ActionBar(module, "hierarchy") self.appendChild( self.actionBar ) self.entryFrame = html5.Ol() self.entryFrame["class"].append("hierarchy") self.appendChild( self.entryFrame ) self.selectionChangedEvent = EventDispatcher("selectionChanged") self.selectionActivatedEvent = EventDispatcher("selectionActivated") self.rootNodeChangedEvent = EventDispatcher("rootNodeChanged") self._currentCursor = None self._currentRequests = [] self.addClass("supports_drop") assert selectMode in [None, "single", "multi"] self.selectMode = selectMode self._expandedNodes = [] self.context = context if self.rootNode: self.reloadData() else: NetworkService.request(self.module, "listRootNodes", self.context or {}, successHandler=self.onSetDefaultRootNode, failureHandler=self.showErrorMsg ) self.path = [] self.sinkEvent("onClick", "onDblClick") ##Proxy some events and functions of the original table #for f in ["selectionChangedEvent","selectionActivatedEvent","cursorMovedEvent","getCurrentSelection"]: # setattr( self, f, getattr(self.table,f)) self.actionBar.setActions(["selectrootnode","add","edit","clone","delete"]+(["select","close"] if self.selectMode else [])+["reload"]) self.sinkEvent("onDrop","onDragOver") def showErrorMsg(self, req=None, code=None): """ Removes all currently visible elements and displayes an error message """ self.actionBar["style"]["display"] = "none" self.entryFrame["style"]["display"] = "none" errorDiv = html5.Div() errorDiv["class"].append("error_msg") if code and (code==401 or code==403): txt = translate("Access denied!") else: txt = translate("An unknown error occurred!") errorDiv["class"].append("error_code_%s" % (code or 0)) errorDiv.appendChild( html5.TextNode( txt ) ) self.appendChild( errorDiv ) def onDataChanged(self, module, **kwargs): if module != self.module: isRootNode = False for k, v in conf[ "modules" ].items(): if (k == module and v.get("handler") == "list" and v.get("rootNodeOf") == self.module): isRootNode = True break if not isRootNode: return self.actionBar.widgets["selectrootnode"].update() self.reloadData() def onAttach(self): super( HierarchyWidget, self ).onAttach() NetworkService.registerChangeListener( self ) def onDetach(self): super( HierarchyWidget, self ).onDetach() NetworkService.removeChangeListener( self ) def itemForEvent(self,event, elem=None): """ Tries to map an event to one of our HierarchyItems. Returns the item actually clicked, not its top-level parent. """ if elem is None: elem = self.entryFrame for child in elem._children: if child.element==event.target: if isinstance( child, HierarchyItem ): # User clicked directly on one HierarchyItem return( child ) else: # User clicked somewhere INTO one HierarchyItem # Return a marker to the outer recursion that this is the correct HierarchyItem return( False ) tmp = self.itemForEvent( event, child ) if tmp is False: if isinstance(child, HierarchyItem): return( child ) else: return( False ) elif tmp is not None: return( tmp ) return( None ) def itemForKey(self, key, elem=None ): """ Returns the HierarchyWidget displaying the entry with the given key. :param key: The key (id) of the item. :type key: str :returns: HierarchyItem """ if elem is None: elem = self.entryFrame for child in elem._children: if child.data["key"]==key: return( child ) tmp = self.itemForKey( key, child.ol ) if tmp is not None: return( tmp ) return( None ) def onClick(self, event): item = self.itemForEvent(event) if item is None: return if html5.utils.doesEventHitWidgetOrChildren(event, item.expandLink): item.toggleExpand() if not item.isLoaded: item.isLoaded = True self.loadNode(item.data["key"]) else: self.setCurrentItem( item ) self.selectionChangedEvent.fire( self, item ) def onDblClick(self, event): item = self.itemForEvent( event ) if item is None: return self.setCurrentItem( item ) self.selectionActivatedEvent.fire( self, [item] ) if self.selectMode: conf["mainWindow"].removeWidget(self) def setCurrentItem(self, item): if self._currentCursor: self._currentCursor["class"].remove("is_focused") item["class"].append("is_focused") self._currentCursor = item def onSetDefaultRootNode(self, req): """ We requested the list of rootNodes for that module and that request just finished. Parse the respone and set our rootNode to the first rootNode received. """ data = NetworkService.decode( req ) if len(data)>0: self.setRootNode( data[0]["key"]) def setRootNode(self, rootNode): """ Set the currently displayed hierarchy to 'rootNode'. :param rootNode: Key of the rootNode which children we shall display :type rootNode: str """ self.rootNode = rootNode self._currentCursor = None self.rootNodeChangedEvent.fire(rootNode) self.reloadData() def reloadData(self): """ Reload the data were displaying. """ def collectExpandedNodes( currNode ): res = [] for c in currNode._children[:]: if isinstance( c, HierarchyItem ): if c.isExpanded: res.append( c.data["key"] ) res.extend( collectExpandedNodes(c.ol) ) return( res ) self._expandedNodes = collectExpandedNodes( self.entryFrame ) self._currentRequests = [] for c in self.entryFrame._children[:]: self.entryFrame.removeChild(c) self.loadNode( self.rootNode ) def loadNode(self, node, cursor = None): """ Fetch the (direct) children of the given node. Once the list is received, append them to their parent node. :param node: Key of the node to fetch :type node: str """ params = { "parent": node, "orderby": "sortindex" } if cursor: params.update({"cursor": cursor}) if self.context: params.update(self.context) r = NetworkService.request(self.module, "list", params, successHandler=self.onRequestSucceded, failureHandler=self.showErrorMsg) r.node = node self._currentRequests.append(r) def onRequestSucceded(self, req): """ The NetworkRequest for a (sub)node finished. Create a new HierarchyItem for each entry received and add them to our view """ if not req in self._currentRequests: #Prevent inserting old (stale) data self.actionBar.resetLoadingState() return self._currentRequests.remove(req) data = NetworkService.decode(req) if req.node == self.rootNode: ol = self.entryFrame else: tmp = self.itemForKey(req.node) ol = tmp.ol assert ol is not None for skel in data["skellist"]: hi = HierarchyItem( self.module, skel, data["structure"] ) ol.appendChild( hi ) if hi.data["key"] in self._expandedNodes: hi.toggleExpand() if not hi.isLoaded: hi.isLoaded = True self.loadNode(hi.data["key"]) if not ol._children and ol != self.entryFrame: ol.parent()["class"].append("has_no_childs") if data["skellist"] and data["cursor"]: self.loadNode(req.node, data["cursor"]) self.actionBar.resetLoadingState() def getCurrentSelection(self): """ Returns the list of entries currently selected. :returns: list of dict """ if self._currentCursor is not None: return( [ self._currentCursor.data ] ) return( [] ) def onDrop(self, event): """ We got a drop event. Make that item a direct child of our rootNode """ srcKey = event.dataTransfer.getData("Text") NetworkService.request(self.module,"reparent",{"item":srcKey,"dest":self.rootNode}, secure=True, modifies=True ) event.stopPropagation() def onDragOver(self, event): """ Allow dropping children on the rootNode """ event.preventDefault() event.stopPropagation() def activateCurrentSelection(self): if self._currentCursor: self.selectionActivatedEvent.fire( self, [self._currentCursor] ) conf["mainWindow"].removeWidget(self) @staticmethod def canHandle(moduleName, moduleInfo): return moduleInfo["handler"] == "hierarchy" or moduleInfo["handler"].startswith("hierarchy.") @staticmethod def render(moduleName, adminInfo, context): rootNode = context.get(conf["vi.context.prefix"] + "rootNode") if context else None return HierarchyWidget(module=moduleName, rootNode=rootNode, context=context)
def __init__(self, editHtml, actionBarHint=translate("Text Editor"), *args, **kwargs): super(Wysiwyg, self).__init__(*args, **kwargs) self.cursorMovedEvent = EventDispatcher("cursorMoved") self.saveTextEvent = EventDispatcher("saveText") self.abortTextEvent = EventDispatcher("abortText") self.textActions = ["style.text.bold", "style.text.italic"]+\ ["style.text.h%s" % x for x in range(0,4)]+\ ["text.removeformat", "style.text.justifyCenter", "style.text.justifyLeft", "style.text.justifyRight", "style.text.blockquote", "text.orderedList", "text.unorderedList", "text.indent", "text.outdent", "text.image", "text.link", "text.table", "text.flipView", "text.undo", "text.redo", "text.abort", "text.save"] #self["type"] = "text" self.actionbar = ActionBar(None, None, actionBarHint) self.isWysiwygMode = True self.discardNextClickEvent = False self.appendChild(self.actionbar) self.tableDiv = html5.Div() self.tableDiv["class"].append("tableeditor") self.appendChild(self.tableDiv) for c in [ TableInsertRowBeforeAction, TableInsertRowAfterAction, TableInsertColBeforeAction, TableInsertColAfterAction, TableRemoveRowAction, TableRemoveColAction ]: self.tableDiv.appendChild(c()) self.tableDiv["style"]["display"] = "none" self.linkEditor = LinkEditor() self.appendChild(self.linkEditor) self.imgEditor = ImageEditor() self.appendChild(self.imgEditor) self.editor = Editor(editHtml) self.appendChild(self.editor) self.actionbar.setActions(self.textActions) #btn = html5.ext.Button("Apply", self.saveText) #btn["class"].append("icon apply") #self.appendChild( btn ) self.currentImage = None self.cursorImage = None self.lastMousePos = None self.sinkEvent("onMouseDown", "onMouseUp", "onMouseMove", "onClick")
class Wysiwyg(html5.Div): def __init__(self, editHtml, actionBarHint=translate("Text Editor"), *args, **kwargs): super(Wysiwyg, self).__init__(*args, **kwargs) self.cursorMovedEvent = EventDispatcher("cursorMoved") self.saveTextEvent = EventDispatcher("saveText") self.abortTextEvent = EventDispatcher("abortText") self.textActions = ["style.text.bold", "style.text.italic"]+\ ["style.text.h%s" % x for x in range(0,4)]+\ ["text.removeformat", "style.text.justifyCenter", "style.text.justifyLeft", "style.text.justifyRight", "style.text.blockquote", "text.orderedList", "text.unorderedList", "text.indent", "text.outdent", "text.image", "text.link", "text.table", "text.flipView", "text.undo", "text.redo", "text.abort", "text.save"] #self["type"] = "text" self.actionbar = ActionBar(None, None, actionBarHint) self.isWysiwygMode = True self.discardNextClickEvent = False self.appendChild(self.actionbar) self.tableDiv = html5.Div() self.tableDiv["class"].append("tableeditor") self.appendChild(self.tableDiv) for c in [ TableInsertRowBeforeAction, TableInsertRowAfterAction, TableInsertColBeforeAction, TableInsertColAfterAction, TableRemoveRowAction, TableRemoveColAction ]: self.tableDiv.appendChild(c()) self.tableDiv["style"]["display"] = "none" self.linkEditor = LinkEditor() self.appendChild(self.linkEditor) self.imgEditor = ImageEditor() self.appendChild(self.imgEditor) self.editor = Editor(editHtml) self.appendChild(self.editor) self.actionbar.setActions(self.textActions) #btn = html5.ext.Button("Apply", self.saveText) #btn["class"].append("icon apply") #self.appendChild( btn ) self.currentImage = None self.cursorImage = None self.lastMousePos = None self.sinkEvent("onMouseDown", "onMouseUp", "onMouseMove", "onClick") def flipView(self, *args, **kwargs): htmlStr = self.editor.element.innerHTML if self.isWysiwygMode: self.imgEditor.doClose() self.linkEditor.doClose() self.tableDiv["style"]["display"] = None outStr = "" indent = 0 indestStr = " " inStr = htmlStr.replace("&", "&").replace("<", "<").replace( ">", ">") while inStr: if inStr.startswith("<div>"): outStr += "<br>" outStr += indestStr * indent indent += 1 elif inStr.startswith("</div>"): indent -= 1 outStr += "<br>" outStr += indestStr * indent elif inStr.startswith("<br"): outStr += "<br>" outStr += indestStr * indent elif inStr.startswith("<table"): outStr += "<br>" outStr += indestStr * indent indent += 1 elif inStr.startswith("</table"): indent -= 1 outStr += "<br>" outStr += indestStr * indent elif inStr.startswith("<tr"): outStr += "<br>" outStr += indestStr * indent indent += 1 elif inStr.startswith("</tr"): indent -= 1 outStr += "<br>" outStr += indestStr * indent elif inStr.startswith("<td"): outStr += "<br>" outStr += indestStr * indent elif inStr.startswith("<th>"): outStr += "<br>" outStr += indestStr * indent elif inStr.startswith("<thead>"): outStr += "<br>" outStr += indestStr * indent indent += 1 elif inStr.startswith("</thead>"): indent -= 1 outStr += "<br>" outStr += indestStr * indent elif inStr.startswith("<tbody>"): outStr += "<br>" outStr += indestStr * indent indent += 1 elif inStr.startswith("</tbody>"): indent -= 1 outStr += "<br>" outStr += indestStr * indent outStr += inStr[0] inStr = inStr[1:] self.editor.element.innerHTML = outStr self.actionbar.setActions(["text.flipView"]) else: htmlStr = re.sub(r'<[^>]*?>', '', htmlStr) htmlStr = htmlStr.replace(" ", "").replace(" ", "") self.editor.element.innerHTML = htmlStr.replace( "<", "<").replace(">", ">").replace("&", "&") self.actionbar.setActions(self.textActions) self.isWysiwygMode = not self.isWysiwygMode return self.isWysiwygMode def saveText(self, *args, **kwargs): self.saveTextEvent.fire(self, self.editor.element.innerHTML) def abortText(self, *args, **kwargs): self.abortTextEvent.fire(self) def onMouseDown(self, event): self.lastMousePos = None if event.target.tagName == "IMG": offsetLeft = event.pageX - event.target.offsetLeft offsetTop = event.pageY - event.target.offsetTop if event.target.offsetParent is not None: offsetLeft -= event.target.offsetParent.offsetLeft offsetTop -= event.target.offsetParent.offsetTop if offsetLeft > 0.8 * event.target.clientWidth and offsetTop > 0.8 * event.target.clientHeight: self.currentImage = event.target self.imgEditor.doOpen(event.target) self.discardNextClickEvent = True event.preventDefault() event.stopPropagation() else: self.currentImage = None super(Wysiwyg, self).onMouseDown(event) node = eval("window.top.getSelection().anchorNode") while node and node != self.editor.element: #FIXME.. emit cursormoved event node = node.parentNode def onMouseUp(self, event): self.currentImage = None self.lastMousePos = None super(Wysiwyg, self).onMouseUp(event) def onMouseMove(self, event): if event.target.tagName == "IMG": offsetLeft = event.pageX - event.target.offsetLeft offsetTop = event.pageY - event.target.offsetTop if event.target.offsetParent is not None: offsetLeft -= event.target.offsetParent.offsetLeft offsetTop -= event.target.offsetParent.offsetTop if offsetLeft > 0.8 * event.target.clientWidth and offsetTop > 0.8 * event.target.clientHeight: self.cursorImage = event.target self.cursorImage.style.cursor = "se-resize" else: if self.cursorImage is not None: self.cursorImage.style.cursor = "default" self.cursorImage = None elif self.cursorImage is not None: self.cursorImage.style.cursor = "default" self.cursorImage = None if self.currentImage is not None and event.target.tagName == "IMG" and self.currentImage == event.target: if self.lastMousePos is None: self.lastMousePos = (event.x, event.y) return x, y = self.lastMousePos self.lastMousePos = (event.x, event.y) event.target.width = event.target.clientWidth - (x - event.x) event.target.height = event.target.clientHeight - (y - event.y) event.preventDefault() event.stopPropagation() else: self.lastMousePos = None self.currentImage = None super(Wysiwyg, self).onMouseMove(event) def onClick(self, event): if self.discardNextClickEvent: self.discardNextClickEvent = False return super(Wysiwyg, self).onClick(event) domWdg = event.target isEditorTarget = False while domWdg: if domWdg == self.editor.element: isEditorTarget = True break domWdg = domWdg.parentNode if not isEditorTarget: return node = eval("window.top.getSelection().anchorNode") nodeStack = [] i = 10 #Try to extract the relevant nodes from the dom while i > 0 and node and node != self.editor.element: i -= 1 nodeStack.append(node) node = node.parentNode if "TABLE" in [(x.tagName if "tagName" in dir(x) else "") for x in nodeStack]: self.tableDiv["style"]["display"] = "" else: self.tableDiv["style"]["display"] = "none" self.linkEditor.onCursorMoved(nodeStack) self.imgEditor.onCursorMoved(nodeStack) self.cursorMovedEvent.fire(nodeStack)