def downloadUrlFor(render: 'viur.core.render.html.default.Render', fileObj: dict, expires: Union[None, int] = conf["viur.downloadUrlFor.expiration"], derived: Optional[str] = None, downloadFileName: Optional[str] = None) -> Optional[str]: """ Constructs a signed download-url for the given file-bone. Mostly a wrapper around :meth:`viur.core.utils.downloadUrlFor`. :param fileObj: The file-bone (eg. skel["file"]) :param expires: None if the file is supposed to be public (which causes it to be cached on the google ede caches), otherwise it's lifetime in seconds :param derived: Optional the filename of a derived file, otherwise the the download-link will point to the originally uploaded file. :return: THe signed download-url relative to the current domain (eg /download/...) """ if expires is unsetMarker: raise ValueError("expires must be explicitly set") if "dlkey" not in fileObj and "dest" in fileObj: fileObj = fileObj["dest"] if expires: expires = timedelta(minutes=expires) if not isinstance(fileObj, (SkeletonInstance, dict)) or "dlkey" not in fileObj or "name" not in fileObj: return None if derived and ("derived" not in fileObj or not isinstance(fileObj["derived"], dict)): return None if derived: return utils.downloadUrlFor(folder=fileObj["dlkey"], fileName=derived, derived=True, expires=expires, downloadFileName=downloadFileName) else: return utils.downloadUrlFor(folder=fileObj["dlkey"], fileName=fileObj["name"], derived=False, expires=expires, downloadFileName=downloadFileName)
def downloadUrlFor(render, fileObj, derived=None, expires=timedelta(hours=1)): if not isinstance(fileObj, (SkeletonInstance, dict)) or "dlkey" not in fileObj or "name" not in fileObj: return None if derived and ("derived" not in fileObj or not isinstance(fileObj["derived"], dict)): return None if derived: return utils.downloadUrlFor(folder=fileObj["dlkey"], fileName=derived, derived=True, expires=expires) else: return utils.downloadUrlFor(folder=fileObj["dlkey"], fileName=fileObj["name"], derived=False, expires=expires)
def unserialize(self, skel, name): if "dlkey" in skel.dbEntity and "name" in skel.dbEntity: skel.accessedValues[name] = utils.downloadUrlFor(skel["dlkey"], skel["name"], derived=False) return True return False
def srcSetFor(render, fileObj, expires=timedelta(hours=1)): if not isinstance(fileObj, (SkeletonInstance, dict)) or not "dlkey" in fileObj or "derived" not in fileObj: return None if not isinstance(fileObj["derived"], dict): return "" resList = [] for fileName, deriviation in fileObj["derived"].items(): params = deriviation["params"] if params.get("group") == "srcset": resList.append("%s %sw" % (utils.downloadUrlFor(fileObj["dlkey"], fileName, True, expires), params["width"])) return ", ".join(resList)
def importBlobFromViur2(dlKey, fileName): if not conf.get("viur.viur2import.blobsource"): return False existingImport = db.Get(db.Key("viur-viur2-blobimport", dlKey)) if existingImport: if existingImport["success"]: return existingImport["dlurl"] return False if conf["viur.viur2import.blobsource"]["infoURL"]: try: importDataReq = urlopen( conf["viur.viur2import.blobsource"]["infoURL"] + dlKey) except: marker = db.Entity(db.Key("viur-viur2-blobimport", dlKey)) marker["success"] = False marker["error"] = "Failed URL-FETCH 1" db.Put(marker) return False if importDataReq.status != 200: marker = db.Entity(db.Key("viur-viur2-blobimport", dlKey)) marker["success"] = False marker["error"] = "Failed URL-FETCH 2" db.Put(marker) return False importData = json.loads(importDataReq.read()) oldBlobName = conf["viur.viur2import.blobsource"][ "gsdir"] + "/" + importData["key"] srcBlob = storage.Blob( bucket=bucket, name=conf["viur.viur2import.blobsource"]["gsdir"] + "/" + importData["key"]) else: oldBlobName = conf["viur.viur2import.blobsource"]["gsdir"] + "/" + dlKey srcBlob = storage.Blob( bucket=bucket, name=conf["viur.viur2import.blobsource"]["gsdir"] + "/" + dlKey) if not srcBlob.exists(): marker = db.Entity(db.Key("viur-viur2-blobimport", dlKey)) marker["success"] = False marker["error"] = "Local SRC-Blob missing" marker["oldBlobName"] = oldBlobName db.Put(marker) return False bucket.rename_blob(srcBlob, "%s/source/%s" % (dlKey, fileName)) marker = db.Entity(db.Key("viur-viur2-blobimport", dlKey)) marker["success"] = True marker["old_src_key"] = dlKey marker["old_src_name"] = fileName marker["dlurl"] = utils.downloadUrlFor(dlKey, fileName, False, None) db.Put(marker) return marker["dlurl"]
def srcSetFor(render: 'viur.core.render.html.default.Render', fileObj: dict, expires: Optional[int], width: Optional[int] = None, height: Optional[int] = None) -> str: """ Generates a string suitable for use as the srcset tag in html. This functionality provides the browser with a list of images in different sizes and allows it to choose the smallest file that will fill it's viewport without upscaling. :param render: The render instance that's calling this function :param fileObj: The file-bone (or if multiple=True a single value from it) to generate the srcset for :param expires: None if the file is supposed to be public (which causes it to be cached on the google ede caches), otherwise it's lifetime in seconds :param width: A list of widths that should be included in the srcset. If a given width is not available, it will be skipped. :param height: A list of heights that should be included in the srcset. If a given height is not available, it will be skipped. :return: The srctag generated or an empty string if a invalid file object was supplied """ if not width and not height: logging.error("Neither width or height supplied to srcSetFor") return "" if "dlkey" not in fileObj and "dest" in fileObj: fileObj = fileObj["dest"] if expires: expires = timedelta(minutes=expires) if not isinstance(fileObj, (SkeletonInstance, dict)) or not "dlkey" in fileObj or "derived" not in fileObj: logging.error("Invalid fileObj supplied to srcSetFor") return "" if not isinstance(fileObj["derived"], dict): return "" resList = [] for fileName, derivate in fileObj["derived"]["files"].items(): customData = derivate.get("customData", {}) if width and customData.get("width") in width: resList.append("%s %sw" % (utils.downloadUrlFor(fileObj["dlkey"], fileName, True, expires), customData["width"])) if height and customData.get("height") in height: resList.append("%s %sh" % (utils.downloadUrlFor(fileObj["dlkey"], fileName, True, expires), customData["height"])) return ", ".join(resList)
def add(self, skelType, node, *args, **kwargs): ## We can't add files directly (they need to be uploaded # if skelType != "node": # raise errors.NotAcceptable() if skelType == "leaf": # We need to handle leafs separately here skey = kwargs.get("skey") targetKey = kwargs.get("key") #if not skey or not securitykey.validate(skey, useSessionKey=True) or not targetKey: # raise errors.PreconditionFailed() skel = self.addLeafSkel() if not skel.fromDB(targetKey): raise errors.NotFound() if not skel["pending"]: raise errors.PreconditionFailed() skel["pending"] = False skel["parentdir"] = skel["pendingParentdir"] if skel["parentdir"]: rootNode = self.getRootNode(skel["parentdir"]) else: rootNode = None if not self.canAdd("leaf", rootNode): raise errors.Forbidden() blobs = list(bucket.list_blobs(prefix="%s/" % targetKey)) if len(blobs) != 1: logging.error("Invalid number of blobs in folder") logging.error(targetKey) raise errors.PreconditionFailed() blob = blobs[0] skel["mimetype"] = utils.escapeString(blob.content_type) skel["name"] = utils.escapeString( blob.name.replace("%s/source/" % targetKey, "")) skel["size"] = blob.size skel["rootnode"] = rootNode skel["weak"] = rootNode is None skel.toDB() # Add updated download-URL as the auto-generated isn't valid yet skel["downloadUrl"] = utils.downloadUrlFor(skel["dlkey"], skel["name"], derived=False) return self.render.addItemSuccess(skel) return super(File, self).add(skelType, node, *args, **kwargs)
def renderSkelValues(self, skel, injectDownloadURL=False): """ Prepares values of one :class:`server.db.skeleton.Skeleton` or a list of skeletons for output. :param skel: Skeleton which contents will be processed. :type skel: server.db.skeleton.Skeleton :returns: A dictionary or list of dictionaries. :rtype: dict """ if skel is None: return None elif isinstance(skel, dict): return skel res = {} for key, bone in skel.items(): res[key] = self.renderBoneValue(bone, skel, key) if injectDownloadURL and "dlkey" in skel and "name" in skel: res["downloadUrl"] = utils.downloadUrlFor(skel["dlkey"], skel["name"], derived=False, expires=config.conf["viur.render.json.downloadUrlExpiration"]) return res
def handle_starttag(self, tag, attrs): """ Delete all tags except for legal ones """ filterChars = "\"'\\\0\r\n@()" if self.validHtml and tag in self.validHtml["validTags"]: cacheTagStart = '<' + tag isBlankTarget = False styles = None classes = None for k, v in attrs: k = k.strip() v = v.strip() if any([c in k for c in filterChars]) or any([c in v for c in filterChars]): if k in {"title", "href", "alt"} and not any([c in v for c in "\"'\\\0\r\n"]): # If we have a title or href attribute, ignore @ and () pass else: # Either the key or the value contains a character that's not supposed to be there continue elif k == "class": # Classes are handled below classes = v.split(" ") continue elif k == "style": # Styles are handled below styles = v.split(";") continue elif k == "src": # We ensure that any src tag starts with an actual url checker = v.lower() if not (checker.startswith("http://") or checker.startswith("https://") or checker.startswith("/")): continue blobKey, derived, fileName = parseDownloadUrl(v) if blobKey: v = utils.downloadUrlFor(blobKey, fileName, derived, expires=None) if self.srcSet: # Build the src set with files already available. If a derived file is not yet build, # getReferencedBlobs will catch it, build it, and we're going to be re-called afterwards. fileObj = db.Query("file").filter("dlkey =", blobKey)\ .order(("creationdate", db.SortOrder.Ascending)).getEntry() srcSet = utils.srcSetFor(fileObj, None, self.srcSet.get("width"), self.srcSet.get("height")) cacheTagStart += ' srcSet="%s"' % srcSet if not tag in self.validHtml["validAttrs"].keys() or not k in self.validHtml["validAttrs"][tag]: # That attribute is not valid on this tag continue if k.lower()[0:2] != 'on' and v.lower()[0:10] != 'javascript': cacheTagStart += ' %s="%s"' % (k, v) if tag == "a" and k == "target" and v.lower() == "_blank": isBlankTarget = True if styles: syleRes = {} for s in styles: style = s[: s.find(":")].strip() value = s[s.find(":") + 1:].strip() if any([c in style for c in filterChars]) or any( [c in value for c in filterChars]): # Either the key or the value contains a character that's not supposed to be there continue if value.lower().startswith("expression") or value.lower().startswith("import"): # IE evaluates JS inside styles if the keyword expression is present continue if style in self.validHtml["validStyles"] and not any( [(x in value) for x in ["\"", ":", ";"]]): syleRes[style] = value if len(syleRes.keys()): cacheTagStart += " style=\"%s\"" % "; ".join( [("%s: %s" % (k, v)) for (k, v) in syleRes.items()]) if classes: validClasses = [] for currentClass in classes: validClassChars = string.ascii_lowercase + string.ascii_uppercase + string.digits + "-" if not all([x in validClassChars for x in currentClass]): # The class contains invalid characters continue isOkay = False for validClass in self.validHtml["validClasses"]: # Check if the classname matches or is white-listed by a prefix if validClass == currentClass: isOkay = True break if validClass.endswith("*"): validClass = validClass[:-1] if currentClass.startswith(validClass): isOkay = True break if isOkay: validClasses.append(currentClass) if validClasses: cacheTagStart += " class=\"%s\"" % " ".join(validClasses) if isBlankTarget: # Add rel tag to prevent the browser to pass window.opener around cacheTagStart += " rel=\"noopener noreferrer\"" if tag in self.validHtml["singleTags"]: # Single-Tags do have a visual representation; ensure it makes it into the result self.flushCache() self.result += cacheTagStart + '>' # dont need slash in void elements in html5 else: # We opened a 'normal' tag; push it on the cache so it can be discarded later if # we detect it has no content cacheTagStart += '>' self.tagCache.append((cacheTagStart, tag)) else: self.result += " "