Beispiel #1
0
class StaticCloudinaryStorage(MediaCloudinaryStorage):
    """
    Base storage for staticfiles kept in Cloudinary.
    Uploads only unhashed files, so it is highly unrecommended to use it directly,
    because static files are cached both by Cloudinary CDN and browsers
    and changing files could become problematic.
    """
    RESOURCE_TYPE = RESOURCE_TYPES['RAW']
    TAG = app_settings.STATIC_TAG

    def url(self, name):
        if settings.DEBUG:
            return settings.STATIC_URL + name
        return super(StaticCloudinaryStorage, self).url(name)

    def _upload(self, name, content):
        return cloudinary.uploader.upload(content, public_id=name, resource_type=self.RESOURCE_TYPE,
                                          invalidate=True, tags=self.TAG)

    # we only need 2 method of HashedFilesMixin, so we just copy them as function objects to avoid MRO complexities
    file_hash = HashedFilesMixin.file_hash if PY3 else get_method_function(HashedFilesMixin.file_hash)
    clean_name = HashedFilesMixin.clean_name if PY3 else get_method_function(HashedFilesMixin.clean_name)

    def _exists_with_etag(self, name, content):
        """
        Checks whether a file with a name and a content is already uploaded to Cloudinary.
        Uses ETAG header and MD5 hash for the content comparison.
        """
        url = self._get_url(name)
        response = requests.head(url)
        if response.status_code == 404:
            return False
        etag = response.headers['ETAG'].split('"')[1]
        hash = self.file_hash(name, content)
        return etag.startswith(hash)

    def _save(self, name, content):
        """
        Saves only when a file with a name and a content is not already uploaded to Cloudinary.
        """
        name = self.clean_name(name)  # to change to UNIX style path on windows if necessary
        if not self._exists_with_etag(name, content):
            content.seek(0)
            super(StaticCloudinaryStorage, self)._save(name, content)
        return self._prepend_prefix(name)

    def _get_prefix(self):
        return settings.STATIC_URL
Beispiel #2
0
    def _get_es_body(self, for_count=False):
        # If to_es has been overridden, call it and raise a deprecation warning
        if isinstance(self.query,
                      ElasticSearchQuery) and six.get_method_function(
                          self.query.to_es) != ElasticSearchQuery.to_es:
            warnings.warn(
                "The .to_es() method on Elasticsearch query classes is deprecated. "
                "Please rename {class_name}.to_es() to {class_name}.get_query()"
                .format(class_name=self.query.__class__.__name__),
                RemovedInWagtail14Warning,
                stacklevel=2)

            body = {
                'query': self.query.to_es(),
            }
        else:
            body = {'query': self.query.get_query()}

        if not for_count:
            sort = self.query.get_sort()

            if sort is not None:
                body['sort'] = sort

        return body
Beispiel #3
0
    def _get_es_body(self, for_count=False):
        # If to_es has been overridden, call it and raise a deprecation warning
        if (
            isinstance(self.query, ElasticSearchQuery)
            and six.get_method_function(self.query.to_es)
            != ElasticSearchQuery.to_es
        ):
            warnings.warn(
                "The .to_es() method on Elasticsearch query classes is deprecated. "
                "Please rename {class_name}.to_es() to {class_name}.get_query()".format(
                    class_name=self.query.__class__.__name__
                ),
                RemovedInWagtail14Warning, stacklevel=2)

            body = {
                'query': self.query.to_es(),
            }
        else:
            body = {
                'query': self.query.get_query()
            }

        if not for_count:
            sort = self.query.get_sort()

            if sort is not None:
                body['sort'] = sort

        return body
Beispiel #4
0
    def get_context_data(self, name, value, attrs=None, bound_field=None):
        ctx = super(CropDusterWidget,
                    self).get_context_data(name, value, attrs, bound_field)
        sizes = self.sizes
        related_object = ctx['instance']
        preview_url = ''
        preview_w = PREVIEW_WIDTH
        preview_h = PREVIEW_HEIGHT
        if related_object:
            preview_url = related_object.get_image_url(size_name='_preview')
            orig_width, orig_height = related_object.width, related_object.height
            if (orig_width and orig_height):
                resize_ratio = min(PREVIEW_WIDTH / float(orig_width),
                                   PREVIEW_HEIGHT / float(orig_height))
                if resize_ratio < 1:
                    preview_w = int(round(orig_width * resize_ratio))
                    preview_h = int(round(orig_height * resize_ratio))

        if six.callable(sizes):
            instance = getattr(getattr(bound_field, 'form', None), 'instance',
                               None)
            try:
                sizes_callable = six.get_method_function(sizes)
            except AttributeError:
                sizes_callable = sizes
            sizes = sizes_callable(instance, related=related_object)
        sizes = [s for s in sizes if not getattr(s, 'is_alias', False)]

        ctx.update({
            'sizes': json.dumps(sizes),
            'preview_url': preview_url,
            'preview_w': preview_w,
            'preview_h': preview_h,
        })
        return ctx
def _instance_overrides_method(base, instance, method_name):
    """
    Returns True if instance overrides a method (method_name)
    inherited from base.
    """
    bound_method = getattr(instance, method_name)
    unbound_method = getattr(base, method_name)
    return six.get_unbound_function(unbound_method) != six.get_method_function(bound_method)
Beispiel #6
0
    def set_non_atomic_dbs(self, view):
        if isinstance(view, types.MethodType):
            view = six.get_method_function(view)

        default_attr = '_replicated_view_default_non_atomic_dbs'
        default_set = getattr(view, default_attr, None)
        # If default_set is None then this first request. Set default.
        if default_set is None:
            view_set = getattr(view, '_non_atomic_requests', set())
            setattr(view, default_attr, view_set)
            default_set = view_set

        all_allowed_aliases = routers.all_allowed_aliases
        # If state master db_for_read() == db_for_write()
        current_alias = routers.db_for_read()
        not_used_aliases = set(a for a in all_allowed_aliases
                               if a != current_alias)
        view._non_atomic_requests = not_used_aliases | default_set
    def set_non_atomic_dbs(self, view):
        if isinstance(view, types.MethodType):
            view = six.get_method_function(view)

        default_attr = '_replicated_view_default_non_atomic_dbs'
        default_set = getattr(view, default_attr, None)
        # If default_set is None then this first request. Set default.
        if default_set is None:
            view_set = getattr(view, '_non_atomic_requests', set())
            setattr(view, default_attr, view_set)
            default_set = view_set

        all_allowed_aliases = routers.all_allowed_aliases
        # If state master db_for_read() == db_for_write()
        current_alias = routers.db_for_read()
        not_used_aliases = set(
            a for a in all_allowed_aliases
            if a != current_alias
        )
        view._non_atomic_requests = not_used_aliases | default_set
Beispiel #8
0
class HashCloudinaryMixin(object):
    def __init__(self, *args, **kwargs):
        self.manifest_storage = ManifestCloudinaryStorage()
        super(HashCloudinaryMixin, self).__init__(*args, **kwargs)

    def hashed_name(self, name, content=None, filename=None):
        parsed_name = urlsplit(unquote(name))
        clean_name = parsed_name.path.strip()
        opened = False
        if content is None:
            absolute_path = finders.find(clean_name)
            try:
                content = open(absolute_path, 'rb')
            except (IOError, OSError) as e:
                if e.errno == errno.ENOENT:
                    raise ValueError(
                        "The file '%s' could not be found with %r." %
                        (clean_name, self))
                else:
                    raise
            content = File(content)
            opened = True
        try:
            file_hash = self.file_hash(clean_name, content)
        finally:
            if opened:
                content.close()
        path, filename = os.path.split(clean_name)
        root, ext = os.path.splitext(filename)
        if file_hash is not None:
            file_hash = ".%s" % file_hash
        hashed_name = os.path.join(path, "%s%s%s" % (root, file_hash, ext))
        unparsed_name = list(parsed_name)
        unparsed_name[2] = hashed_name
        # Special casing for a @font-face hack, like url(myfont.eot?#iefix")
        # http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax
        if '?#' in name and not unparsed_name[3]:
            unparsed_name[2] += '?'
        return urlunsplit(unparsed_name)

    def post_process(self, paths, dry_run=False, **options):
        original_exists = self.exists
        self.exists = lambda name: False  # temporarily overwritten to prevent any exist check
        for response in super(HashCloudinaryMixin,
                              self).post_process(paths, dry_run, **options):
            yield response
        self.exists = original_exists

    def read_manifest(self):
        try:
            with self.manifest_storage.open(self.manifest_name) as manifest:
                return manifest.read().decode('utf-8')
        except IOError:
            return None

    def add_unix_path_keys_to_paths(self, paths):
        for path in paths.copy():
            if '\\' in path:
                clean_path = self.clean_name(path)
                paths[clean_path] = paths[path]

    def save_manifest(self):
        payload = {
            'paths': self.hashed_files,
            'version': self.manifest_version
        }
        if os.name == 'nt':
            paths = payload['paths']
            self.add_unix_path_keys_to_paths(paths)
        if self.manifest_storage.exists(self.manifest_name):
            self.manifest_storage.delete(self.manifest_name)
        contents = json.dumps(payload).encode('utf-8')
        self.manifest_storage._save(self.manifest_name, ContentFile(contents))

    # we only need 1 method of HashedFilesMixin, so we just copy it as function objects to avoid MRO complexities
    stored_name = HashedFilesMixin.stored_name if PY3 else get_method_function(
        HashedFilesMixin.stored_name)
Beispiel #9
0
class StaticCloudinaryStorage(MediaCloudinaryStorage):
    """
    Base storage for staticfiles kept in Cloudinary.
    Uploads only unhashed files, so it is highly unrecommended to use it directly,
    because static files are cached both by Cloudinary CDN and browsers
    and changing files could become problematic.
    """
    RESOURCE_TYPE = RESOURCE_TYPES['RAW']
    TAG = app_settings.STATIC_TAG

    def _get_resource_type(self, name):
        """
        Implemented as static files can be of different resource types.
        Because web developers are the people who control those files, we can distinguish them
        simply by looking at their extensions, we don't need any content based validation.
        """
        extension = self._get_file_extension(name)
        if extension is None:
            return self.RESOURCE_TYPE
        elif extension in app_settings.STATIC_IMAGES_EXTENSIONS:
            return RESOURCE_TYPES['IMAGE']
        elif extension in app_settings.STATIC_VIDEOS_EXTENSIONS:
            return RESOURCE_TYPES['VIDEO']
        else:
            return self.RESOURCE_TYPE

    @staticmethod
    def _get_file_extension(name):
        substrings = name.split('.')
        if len(substrings) == 1:  # no extensions
            return None
        else:
            return substrings[-1].lower()

    def url(self, name):
        if settings.DEBUG:
            return settings.STATIC_URL + name
        return super(StaticCloudinaryStorage, self).url(name)

    def _upload(self, name, content):
        resource_type = self._get_resource_type(name)
        name = self._remove_extension_for_non_raw_file(name)
        return cloudinary.uploader.upload(content,
                                          public_id=name,
                                          resource_type=resource_type,
                                          invalidate=True,
                                          tags=self.TAG)

    def _remove_extension_for_non_raw_file(self, name):
        """
        Implemented as image and video files' Cloudinary public id
        shouldn't contain file extensions, otherwise Cloudinary url
        would contain doubled extension - Cloudinary adds extension to url
        to allow file conversion to arbitrary file, like png to jpg.
        """
        file_resource_type = self._get_resource_type(name)
        if file_resource_type is None or file_resource_type == self.RESOURCE_TYPE:
            return name
        else:
            extension = self._get_file_extension(name)
            return name[:-len(extension) - 1]

    # we only need 2 methods of HashedFilesMixin, so we just copy them as function objects to avoid MRO complexities
    file_hash = HashedFilesMixin.file_hash if PY3 else get_method_function(
        HashedFilesMixin.file_hash)
    clean_name = HashedFilesMixin.clean_name if PY3 else get_method_function(
        HashedFilesMixin.clean_name)

    def _exists_with_etag(self, name, content):
        """
        Checks whether a file with a name and a content is already uploaded to Cloudinary.
        Uses ETAG header and MD5 hash for the content comparison.
        """
        url = self._get_url(name)
        response = requests.head(url)
        if response.status_code == 404:
            return False
        etag = response.headers['ETAG'].split('"')[1]
        hash = self.file_hash(name, content)
        return etag.startswith(hash)

    def _save(self, name, content):
        """
        Saves only when a file with a name and a content is not already uploaded to Cloudinary.
        """
        name = self.clean_name(
            name)  # to change to UNIX style path on windows if necessary
        if not self._exists_with_etag(name, content):
            content.seek(0)
            super(StaticCloudinaryStorage, self)._save(name, content)
        return self._prepend_prefix(name)

    def _get_prefix(self):
        return settings.STATIC_URL

    def listdir(self, path):
        """
        Not implemented as static assets can be of different resource types
        in contrast to media storages, which are specialized per given resource type.
        That's why we cannot use parent's class listdir.
        This method could be implemented in the future if there is a demand for it.
        """
        raise NotImplementedError()

    def stored_name(self, name):
        """
        Implemented to standardize interface
        for StaticCloudinaryStorage and StaticHashedCloudinaryStorage
        """
        return self._prepend_prefix(name)