Esempio n. 1
0
class Search(html5.Div):
    def __init__(self, *args, **kwargs):
        super(Search, self).__init__(*args, **kwargs)
        self.startSearchEvent = EventDispatcher("startSearch")
        self.addClass("flr-search")
        self.searchLbl = html5.H2()
        self.searchLbl.appendChild(html5.TextNode(
            translate("Fulltext search")))
        self.searchLbl.addClass("flr-search-label")
        self.appendChild(self.searchLbl)
        self.searchInput = Input()
        self.searchInput["type"] = "text"
        self.appendChild(self.searchInput)
        self.btn = Button(translate("Search"), callback=self.doSearch)
        self.appendChild(self.btn)
        self.sinkEvent("onKeyDown")
        self.last_search = ""

    def doSearch(self, *args, **kwargs):
        if self.searchInput["value"] != self.last_search:
            if len(self.searchInput["value"]):
                self.startSearchEvent.fire(self.searchInput["value"])
            else:
                self.resetSearch()

            self.last_search = self.searchInput["value"]

    def resetSearch(self):
        self.startSearchEvent.fire(None)

    def onKeyDown(self, event):
        if html5.isReturn(event):
            self.doSearch()
            event.preventDefault()
            event.stopPropagation()

    def resetLoadingState(self):
        if "is-loading" in self.btn["class"]:
            self.btn.removeClass("is-loading")

    def reevaluate(self):
        self.doSearch()

    def focus(self):
        self.searchInput.focus()
Esempio n. 2
0
class Uploader(Progress):
	"""
		Uploads a file to the server while providing visual feedback of the progress.
	"""

	def __init__(self, file, node, context=None, showResultMessage=True, *args, **kwargs):
		"""
			:param file: The file to upload
			:type file: A javascript "File" Object
			:param node: Key of the desired node of our parents tree application or None for an anonymous upload.
			:type node: str or None
			:param showResultMessage: if True replaces progressbar with complete message on success
			:type showResultMessage: bool
		"""
		super(Uploader, self).__init__()
		self.uploadSuccess = EventDispatcher("uploadSuccess")
		self.uploadFailed = EventDispatcher("uploadFailed")
		self.responseValue = None
		self.targetKey = None
		self.showResultMessage = showResultMessage
		self.context = context

		r = NetworkService.request("file", "getUploadURL",
			params={"node": node} if node else {},
			successHandler=self.onUploadUrlAvailable,
			failureHandler=self.onFailed,
			secure=True
		)
		r.file = file
		r.node = node
		self.node = node

		# self.parent().addClass("is-uploading")

	def onUploadUrlAvailable(self, req):
		"""
			Internal callback - the actual upload url (retrieved by calling /file/getUploadURL) is known.
		"""

		params = NetworkService.decode(req)["values"]

		formData = html5.jseval("new FormData();")

		#if self.context:
		#	for k, v in self.context.items():
		#		formData.append(k, v)

		#if req.node and str(req.node) != "null":
		#	formData.append("node", req.node)

		for key, value in params["params"].items():
			if key == "key":
				self.targetKey = value[:-16]  # Truncate source/file.dat
				fileName = req.file.name
				value = value.replace("file.dat", fileName)

			formData.append(key, value)
		formData.append("file", req.file)

		self.xhr = html5.jseval("new XMLHttpRequest()")
		self.xhr.open("POST", params["url"])
		self.xhr.onload = self.onLoad
		self.xhr.upload.onprogress = self.onProgress
		self.xhr.send(formData)

	def onSkeyAvailable(self, req):
		"""
			Internal callback - the Security-Key is known.
			# Only for core 2.x needed
		"""
		formData = html5.jseval("new FormData();")
		formData.append("file", req.file)

		if self.context:
			for k, v in self.context.items():
				formData.append(k, v)

		if req.node and str(req.node) != "null":
			formData.append("node", req.node)

		formData.append("skey", NetworkService.decode(req))
		self.xhr = html5.jseval("new XMLHttpRequest()")
		self.xhr.open("POST", req.destUrl)
		self.xhr.onload = self.onLoad
		self.xhr.upload.onprogress = self.onProgress
		self.xhr.send(formData)

	def onLoad(self, *args, **kwargs):
		"""
			Internal callback - The state of our upload changed.
		"""
		if self.xhr.status in [200, 204]:
			NetworkService.request(
				"file", "add", {
					"key": self.targetKey,
					"node": self.node,
					"skelType": "leaf"
				},
			    successHandler=self.onUploadAdded,
				secure=True
			)
		else:
			DeferredCall(self.onFailed, self.xhr.status, _delay=1000)

	def onUploadAdded(self, req):
		self.responseValue = NetworkService.decode(req)
		DeferredCall(self.onSuccess, _delay=1000)

	def onProgress(self, event):
		"""
			Internal callback - further bytes have been transmitted
		"""
		if event.lengthComputable:
			complete = int(event.loaded / event.total * 100)
			self["value"] = complete
			self["max"] = 100

	def onSuccess(self, *args, **kwargs):
		"""
			Internal callback - The upload succeeded.
		"""
		if isinstance(self.responseValue["values"], list):
			for v in self.responseValue["values"]:
				self.uploadSuccess.fire(self, v)

		else:
			self.uploadSuccess.fire(self, self.responseValue["values"])

		NetworkService.notifyChange("file")
		if self.showResultMessage:
			self.replaceWithMessage("Upload complete", isSuccess=True)

	def onFailed(self, errorCode, *args, **kwargs):
		if self.showResultMessage:
			self.replaceWithMessage("Upload failed with status code %s" % errorCode, isSuccess=False)
		self.uploadFailed.fire(self, errorCode)

	def replaceWithMessage(self, message, isSuccess):
		self.parent().removeClass("is-uploading")
		self.parent().removeClass("log-progress")
		if isSuccess:
			self.parent().addClass("log-success")
		else:
			self.parent().addClass("log-failed")
		msg = html5.Span()
		msg.appendChild(html5.TextNode(message))
		self.parent().appendChild(msg)
		self.parent().removeChild(self)
Esempio n. 3
0
class viurForm(html5.Form):
    """
	Handles an input form for a VIUR skeleton.
	"""
    def __init__(self,
                 formName=None,
                 moduleName=None,
                 actionName="add",
                 skel=None,
                 structure=None,
                 visible=(),
                 ignore=(),
                 hide=(),
                 defaultValues=()):
        super().__init__()
        self.formName = formName
        self.moduleName = moduleName
        self.actionName = actionName
        self.bones = {}
        self.skel = skel
        self.errors = []
        self.visible = visible
        self.ignore = ignore
        self.hide = hide
        self.defaultValues = defaultValues

        if structure:
            self.structure = {k: v for k, v in structure}

        self.formSuccessEvent = EventDispatcher("formSuccess")
        self.formSuccessEvent.register(self)

        self.addClass("form")
        self.sinkEvent("onChange")

    def onChange(self, event):
        self.applyVisiblity()

    def _setModulename(self, val):
        self.moduleName = val

    def _setActionname(self, val):
        self.actionName = val

    def _setFormname(self, val):
        self.formName = val

    def buildForm(self):
        for key, bone in self.structure.items():
            if key in self.ignore:
                continue
            elif self.visible and key not in self.visible:
                continue

            boneValue = None
            if key in self.defaultValues:
                boneValue = self.defaultValues[key]
            bonefield = boneField(key, self, boneValue)
            self.appendChild(bonefield)

        submitbtn = sendForm(text="speichern", form=self)
        self.appendChild(submitbtn)

    def buildInternalForm(self):
        for key, bone in self.structure.items():
            if key in self.ignore:
                continue
            elif self.visible and key not in self.visible:
                continue

            bonefield = boneField(key, self)
            bonefield.onAttach()  #we need a better solution
            self.appendChild(bonefield)

    def registerField(self, key, widget):
        if key in self.ignore:
            return 0
        elif self.visible and key not in self.visible:
            return 0

        if key in self.bones:
            logging.debug(
                "Double field definition in {}!, only first field will be used",
                self)
            return 0

        self.bones.update({key: widget})

    def applyVisiblity(self):
        #only PoC!
        #WIP
        for key, boneField in self.bones.items():
            codestr = getattr(boneField, "visibleif", None)
            if not codestr:
                continue

            from flare.config import conf

            seInst = conf["safeEvalInstance"]
            seResult = seInst.execute(seInst.compile(codestr),
                                      self.collectCurrentFormValues())

            widget = boneField.bonewidget
            if not seResult:
                boneField.hide()
            else:
                boneField.show()

    def submitForm(self):
        res = self.collectCurrentFormValues()

        NetworkService.request(
            self.moduleName,
            self.actionName,
            res,
            secure=True,  #always with fresh skey
            successHandler=self.actionSuccess,
            failureHandler=self.actionFailed)

        return res

    def collectCurrentFormValues(self):
        res = {}
        if "key" in self.skel and self.skel["key"]:
            res["key"] = self.skel["key"]

        for key, boneField in self.bones.items():
            widget = boneField.bonewidget
            # ignore the key, it is stored in self.key, and read-only bones
            if key == "key" or widget.bone.readonly:
                continue

            try:
                res[key] = widget.serialize()
                if res[key] is None:
                    res[key] = ""

            except InvalidBoneValueException:
                pass
        # if validityCheck:
        #	return None
        return res

    def actionSuccess(self, req):
        resp = NetworkService.decode(req)
        logging.debug("actionSuccess: %r", resp)
        '''
		severity cases:
			NotSet = 0
			InvalidatesOther = 1 <-- relevant
			Empty = 2
			Invalid = 3			 <-- relevant
		'''
        if "action" in resp and resp["action"].endswith("Success"):
            # form approved: Let's store the new skeleton values and fire the success event
            self.skel = resp["values"]
            self.formSuccessEvent.fire(self)

        else:
            #form rejected
            self.errors = resp["errors"]

            for error in self.errors:
                if error["fieldPath"] in self.bones:
                    boneField = self.bones[
                        error["fieldPath"]]  # todo dependency errors
                    if (error["severity"]%2 == 0 and boneField["required"]) or\
                     (error["severity"]%2 == 1): #invalid

                        boneField.setInvalid()
                    else:
                        boneField.setValid()

            self.createFormErrorMessage()

    def createFormSuccessMessage(self):
        try:
            self.removeChild(self.errorhint)
        except:
            pass

        if "successhint" not in dir(self):
            # language=HTML
            self.prependChild('''
						<div [name]="successhint" class="msg is-active msg--success ">Erfolgreich gespeichert!</div>
					''')

    def createFormErrorMessage(self):
        try:
            self.removeChild(self.successhint)
        except:
            pass

        if "errorhint" not in dir(self):
            #language=HTML
            self.prependChild('''
				<div [name]="errorhint" class="msg is-active msg--error "></div>
			''')

        self.errorhint.removeAllChildren()
        for error in self.errors:
            if error["fieldPath"] in self.bones:
                boneField = self.bones[
                    error["fieldPath"]]  # todo dependency errors
                if error["severity"] == 1 or error["severity"] == 3:  # invalid
                    #language=HTML
                    self.errorhint.appendChild(
                        '''<span class="flr-bone--error">{{boneDescr}}: {{error}} </span>''',
                        boneDescr=boneField.structure[boneField.boneName].get(
                            "descr", boneField.boneName),
                        error=error["errorMessage"])

    def actionFailed(self, req, *args, **kwargs):
        logging.debug("FAILED: %r", req)

    def onFormSuccess(self, event):
        self.createFormSuccessMessage()
Esempio n. 4
0
class NetworkService(object):
    """
		Generic wrapper around ajax requests.
		Handles caching and multiplexing multiple concurrent requests to
		the same resource. It also acts as the central proxy to notify
		currently active widgets of changes made to data on the server.
	"""
    changeListeners = [
    ]  # All currently active widgets which will be informed of changes made

    host = ""
    prefix = "/json"
    defaultFailureHandler = NiceError

    retryCodes = [0, -1]
    retryMax = 3
    retryDelay = 5000

    @staticmethod
    def notifyChange(module, **kwargs):
        """
			Broadcasts a change made to data of module 'module' to all currently
			registered changeListeners.

			:param module: Name of the module where the change occured
			:type module: str
		"""
        for c in NetworkService.changeListeners:
            c.onDataChanged(module, **kwargs)

    @staticmethod
    def registerChangeListener(listener):
        """
			Registers object 'listener' for change notifications.
			'listener' must provide an 'onDataChanged' function accepting
			one parameter: the name of the module. Does nothing if that object
			has already registered.
			:param listener: The object to register
			:type listener: object
		"""
        if listener in NetworkService.changeListeners:
            return

        NetworkService.changeListeners.append(listener)

    @staticmethod
    def removeChangeListener(listener):
        """
			Unregisters the object 'listener' from change notifications.
			:param listener: The object to unregister. It must be currently registered.
			:type listener: object
		"""
        assert listener in NetworkService.changeListeners, "Attempt to remove unregistered listener %s" % str(
            listener)
        NetworkService.changeListeners.remove(listener)

    @staticmethod
    def genReqStr(params):
        boundary_str = "---" + ''.join([
            random.choice(string.ascii_lowercase + string.ascii_uppercase +
                          string.digits) for x in range(13)
        ])
        boundary = boundary_str

        res = f"Content-Type: multipart/mixed; boundary=\"{boundary}\"\r\nMIME-Version: 1.0\r\n"
        res += "\r\n--" + boundary

        def expand(key, value):
            ret = ""

            if all([x in dir(value) for x in ["name", "read"]]):  # File
                type = "application/octet-stream"
                filename = os.path.basename(value.name).decode(
                    sys.getfilesystemencoding())

                ret += \
                 f"\r\nContent-Type: {type}" \
                 f"\r\nMIME-Version: 1.0" \
                 f"\r\nContent-Disposition: form-data; name=\"{key}\"; filename=\"{filename}\"\r\n\r\n"
                ret += str(value.read())
                ret += '\r\n--' + boundary

            elif isinstance(value, list):
                if any([isinstance(entry, dict) for entry in value]):
                    for idx, entry in enumerate(value):
                        ret += expand(key + "." + str(idx), entry)
                else:
                    for entry in value:
                        ret += expand(key, entry)

            elif isinstance(value, dict):
                for key_, entry in value.items():
                    ret += expand(((key + ".") if key else "") + key_, entry)

            else:
                ret += \
                 "\r\nContent-Type: application/octet-stream" \
                 "\r\nMIME-Version: 1.0" \
                 f"\r\nContent-Disposition: form-data; name=\"{key}\"\r\n\r\n"
                ret += str(value) if value is not None else ""
                ret += '\r\n--' + boundary

            return ret

        for key, value in params.items():
            res += expand(key, value)

        res += "--\r\n"

        # fixme: DEBUG!
        #print(res)

        return res, boundary

    @staticmethod
    def decode(req):
        """
			Decodes a response received from the server (ie parsing the json)
			:type req: Instance of NetworkService response
			:returns: object
		"""
        return json.loads(req.result)

    @staticmethod
    def isOkay(req):
        answ = NetworkService.decode(req)
        return isinstance(answ, str) and answ == "OKAY"

    @staticmethod
    def urlForArgs(module, path):
        """
			Constructs the final url for that request.
			If module is given, it prepends "/prefix"
			If module is None, path is returned unchanged.
			:param module: Name of the target module or None
			:type module: str or None
			:param path: Path (either relative to 'module' or absolute if 'module' is None
			:type path: str
			:returns: str
		"""
        if module:
            href = "%s/%s/%s" % (NetworkService.prefix, module, path)
        else:
            href = path

        if not href.startswith("/"):
            href = "/" + href

        return NetworkService.host + href

    def __init__(self, module, url, params, successHandler, failureHandler,
                 finishedHandler, modifies, secure, kickoff):
        """
			Constructs a new NetworkService request.
			Should not be called directly (use NetworkService.request instead).
		"""
        super(NetworkService, self).__init__()

        self.result = None
        self.status = None
        self.waitingForSkey = False
        self.module = module
        self.url = url

        if params and not isinstance(params, dict):
            self.url += "/%s" % params
            params = {}

        self.params = params

        self.successHandler = [successHandler] if successHandler else []
        self.failureHandler = [failureHandler] if failureHandler else []
        self.finishedHandler = [finishedHandler] if finishedHandler else []

        self.requestFinishedEvent = EventDispatcher('finished')
        self.requestFinishedEvent.register(self)
        self.modifies = modifies
        self.secure = secure

        self.kickoffs = 0
        if kickoff:
            self.kickoff()

    def kickoff(self):
        self.status = "running"
        self.kickoffs += 1

        if self.secure:
            self.waitingForSkey = True
            self.doFetch(
                "%s%s/skey" % (NetworkService.host, NetworkService.prefix),
                None, None)
        else:
            self.doFetch(NetworkService.urlForArgs(self.module, self.url),
                         self.params, None)

    @staticmethod
    def request(module,
                url,
                params=None,
                successHandler=None,
                failureHandler=None,
                finishedHandler=None,
                modifies=False,
                secure=False,
                kickoff=True,
                group=None):
        """
			Performs an AJAX request. Handles caching and security-keys.

			Calls made to this function are guaranteed to be async.

			:param module: Target module on the server. Set to None if you want to call anything else
			:type module: str or None
			:param url: The path (relative to module) or a full url if module is None
			:type url: None
			:param successHandler: function beeing called if the request succeeds. Must take one argument (the request).
			:type successHandler: callable
			:param failureHandler: function beeing called if the request failes. Must take two arguments (the request and an error-code).
			:type failureHandler: callable
			:param finishedHandler: function beeing called if the request finished (regardless wherever it succeeded or not). Must take one argument (the request).
			:type finishedHandler: callable
			:param modifies: If set to True, it will automatically broadcast an onDataChanged event for that module.
			:type modifies: bool
			:param secure: If true, include a fresh securitykey in this request. Defaults to False.
			:type secure: bool

		"""
        logging.debug("NetworkService.request module=%r, url=%r, params=%r",
                      module, url, params)

        if group or secure:
            #secure and grouped requests will be handled later
            kickoff = False

        dataRequest = NetworkService(module, url, params, successHandler,
                                     failureHandler, finishedHandler, modifies,
                                     secure, kickoff)

        if group:
            group.addRequest(dataRequest)

        if not group and secure:
            # single secure requests will be queued to ensure a fresh skey
            #a group is triggered externally and processed sequentially
            skeyRequestQueue.append(dataRequest)

        return dataRequest

    def doFetch(self, url, params, skey):
        """
			Internal function performing the actual AJAX request.
		"""

        if params:
            if skey:
                params["skey"] = skey

            contentType = None

            if isinstance(params, dict):
                multipart, boundary = NetworkService.genReqStr(params)
                contentType = "multipart/form-data; boundary=" + boundary + "; charset=utf-8"
            elif isinstance(params, bytes):
                contentType = "application/x-www-form-urlencoded"
                multipart = params
            else:
                multipart = params

            HTTPRequest("POST",
                        url,
                        self.onCompletion,
                        self.onError,
                        payload=multipart,
                        content_type=contentType)

        else:
            if skey:
                if "?" in url:
                    url += "&skey=%s" % skey
                else:
                    url += "?skey=%s" % skey

            HTTPRequest("GET", url, self.onCompletion, self.onError)

    def onCompletion(self, text):
        """
			Internal hook for the AJAX call.
		"""
        if self.waitingForSkey:
            self.waitingForSkey = False
            self.doFetch(NetworkService.urlForArgs(self.module, self.url),
                         self.params, json.loads(text))
        else:
            self.result = text
            self.status = "succeeded"
            try:
                for s in self.successHandler:
                    s(self)
                for s in self.finishedHandler:
                    s(self)
                self.requestFinishedEvent.fire(True)
            except:
                if self.modifies:
                    DeferredCall(
                        NetworkService.notifyChange,
                        self.module,
                        key=self.params.get("key") if self.params else None,
                        action=self.url,
                        _delay=2500)
                raise

            if self.modifies:
                DeferredCall(
                    NetworkService.notifyChange,
                    self.module,
                    key=self.params.get("key") if self.params else None,
                    action=self.url,
                    _delay=2500)

            # Remove references to our handlers
            self.clear()

    def onError(self, text, code):
        """
			Internal hook for the AJAX call.
		"""
        self.status = "failed"
        self.result = text

        logging.debug(
            "NetworkService.onError kickoffs=%r, retryMax=%r, code=%r, retryCodes=%r",
            self.kickoffs, self.retryMax, code, self.retryCodes)

        if self.kickoffs < self.retryMax and int(code) in self.retryCodes:
            # The following can be used to pass errors to a bugtracker service like Stackdriver or Bugsnag
            logError = None  # html5.window.top.logError
            if logError and self.kickoffs == self.retryMax - 1:
                logError(
                    "NetworkService.onError code:%s module:%s url:%s params:%s"
                    % (code, self.module, self.url, self.params))

            logging.error("error %d, kickoff %d, will retry now", code,
                          self.kickoffs)
            DeferredCall(self.kickoff, _delay=self.retryDelay)
            return

        for s in self.failureHandler:
            s(self, code)

        if not self.failureHandler and self.defaultFailureHandler:
            self.defaultFailureHandler(code)

        if not self.defaultFailureHandler:
            self.clear()

        for s in self.finishedHandler:
            s(self)

        self.requestFinishedEvent.fire(False)

    def onTimeout(self, text):
        """
			Internal hook for the AJAX call.
		"""
        self.onError(text, -1)

    def clear(self):
        self.successHandler = []
        self.finishedHandler = []
        self.failureHandler = []
        self.params = None

    def onFinished(self, success):
        pass