Exemplo n.º 1
0
    def _get_asset_content(self, xmlid, options):
        options = dict(options,
            inherit_branding=False, inherit_branding_auto=False,
            edit_translations=False, translatable=False,
            rendering_bundle=True)

        env = self.env(context=options)

        # TODO: This helper can be used by any template that wants to embedd the backend.
        #       It is currently necessary because the ir.ui.view bundle inheritance does not
        #       match the module dependency graph.
        def get_modules_order():
            if request:
                from actpy.addons.web.controllers.main import module_boot
                return json.dumps(module_boot())
            return '[]'
        template = env['ir.qweb'].render(xmlid, {"get_modules_order": get_modules_order})

        files = []
        remains = []
        for el in html.fragments_fromstring(template):
            if isinstance(el, pycompat.string_types):
                remains.append(pycompat.to_text(el))
            elif isinstance(el, html.HtmlElement):
                href = el.get('href', '')
                src = el.get('src', '')
                atype = el.get('type')
                media = el.get('media')

                can_aggregate = not urls.url_parse(href).netloc and not href.startswith('/web/content')
                if el.tag == 'style' or (el.tag == 'link' and el.get('rel') == 'stylesheet' and can_aggregate):
                    if href.endswith('.sass'):
                        atype = 'text/sass'
                    elif href.endswith('.less'):
                        atype = 'text/less'
                    if atype not in ('text/less', 'text/sass'):
                        atype = 'text/css'
                    path = [segment for segment in href.split('/') if segment]
                    filename = get_resource_path(*path) if path else None
                    files.append({'atype': atype, 'url': href, 'filename': filename, 'content': el.text, 'media': media})
                elif el.tag == 'script':
                    atype = 'text/javascript'
                    path = [segment for segment in src.split('/') if segment]
                    filename = get_resource_path(*path) if path else None
                    files.append({'atype': atype, 'url': src, 'filename': filename, 'content': el.text, 'media': media})
                else:
                    remains.append(html.tostring(el, encoding='unicode'))
            else:
                try:
                    remains.append(html.tostring(el, encoding='unicode'))
                except Exception:
                    # notYETimplementederror
                    raise NotImplementedError

        return (files, remains)
Exemplo n.º 2
0
    def test_01_debug_mode_assets(self):
        """ Checks that the ir.attachments records created for compiled less assets in debug mode
        are correctly invalidated.
        """
        # Compile for the first time
        self._bundle(self._get_asset(), True, False)

        # Compile a second time, without changes
        self._bundle(self._get_asset(), False, False)

        # Touch the file and compile a third time
        path = get_resource_path('test_assetsbundle', 'static', 'src', 'less',
                                 'test_lessfile1.less')
        t = time.time() + 5
        asset = self._get_asset()
        _touch(path, asset, t=t)
        self._bundle(asset, True, True)

        # Because we are in the same transaction since the beginning of the test, the first asset
        # created and the second one have the same write_date, but the file's last modified date
        # has really been modified. If we do not update the write_date to a posterior date, we are
        # not able to reproduce the case where we compile this bundle again without changing
        # anything.
        self.cr.execute(
            "update ir_attachment set write_date=clock_timestamp() + interval '10 seconds' where id = (select max(id) from ir_attachment)"
        )

        # Compile a fourth time, without changes
        self._bundle(self._get_asset(), False, False)
Exemplo n.º 3
0
 def _compute_rating_image(self):
     for rating in self:
         try:
             image_path = get_resource_path(
                 'rating', 'static/src/img',
                 'rating_%s.png' % (int(rating.rating), ))
             rating.rating_image = base64.b64encode(
                 open(image_path, 'rb').read())
         except (IOError, OSError):
             rating.rating_image = False
Exemplo n.º 4
0
 def get_command(self):
     try:
         if os.name == 'nt':
             lessc = misc.find_in_path('lessc.cmd')
         else:
             lessc = misc.find_in_path('lessc')
     except IOError:
         lessc = 'lessc'
     lesspath = get_resource_path('web', 'static', 'lib', 'bootstrap',
                                  'less')
     return [
         lessc, '-', '--no-js', '--no-color',
         '--include-path=%s' % lesspath
     ]
Exemplo n.º 5
0
    def _get_files(self):
        def get_scripts(path):
            if not path:
                return {}
            return {
                version: glob.glob1(opj(path, version), '*.py')
                for version in os.listdir(path)
                if os.path.isdir(opj(path, version))
            }

        for pkg in self.graph:
            if not (hasattr(pkg, 'update') or pkg.state == 'to upgrade'
                    or getattr(pkg, 'load_state', None) == 'to upgrade'):
                continue

            self.migrations[pkg.name] = {
                'module':
                get_scripts(get_resource_path(pkg.name, 'migrations')),
                'maintenance':
                get_scripts(
                    get_resource_path('base', 'maintenance', 'migrations',
                                      pkg.name)),
            }
Exemplo n.º 6
0
 def stat(self):
     if not (self.inline or self._filename or self._ir_attach):
         path = (segment for segment in self.url.split('/') if segment)
         self._filename = get_resource_path(*path)
         if self._filename:
             return
         try:
             # Test url against ir.attachments
             fields = ['__last_update', 'datas', 'mimetype']
             domain = [('type', '=', 'binary'), ('url', '=', self.url)]
             attach = self.bundle.env['ir.attachment'].sudo().search_read(
                 domain, fields)
             self._ir_attach = attach[0]
         except Exception:
             raise AssetNotFound("Could not find %s" % self.name)
Exemplo n.º 7
0
    def test_03_date_invalidation(self):
        """ Checks that a bundle is invalidated when one of its assets' modification date is changed.
        """
        bundle0 = self._get_asset(self.jsbundle_xmlid)
        bundle0.js()
        last_modified0 = bundle0.last_modified
        version0 = bundle0.version

        path = get_resource_path('test_assetsbundle', 'static', 'src', 'js',
                                 'test_jsfile1.js')
        bundle1 = self._get_asset(self.jsbundle_xmlid)
        _touch(path, bundle1)

        bundle1.js()
        last_modified1 = bundle1.last_modified
        version1 = bundle1.version
        self.assertNotEquals(last_modified0, last_modified1)
        self.assertNotEquals(version0, version1)

        # check if the previous attachment is correctly cleaned
        self.assertEquals(len(self._any_ira_for_bundle('js')), 1)
Exemplo n.º 8
0
    def test_10_paginated_css_date_invalidation(self):
        """ Checks that a bundle is invalidated when one of its assets' modification date is changed.
        """
        bundle0 = self._get_asset(self.cssbundle_xmlid,
                                  env=self.env(context={'max_css_rules': 1}))
        bundle0.css()
        last_modified0 = bundle0.last_modified
        version0 = bundle0.version

        path = get_resource_path('test_assetsbundle', 'static', 'src', 'css',
                                 'test_cssfile1.css')
        bundle1 = self._get_asset(self.cssbundle_xmlid,
                                  env=self.env(context={'max_css_rules': 1}))
        _touch(path, bundle1)

        bundle1.css()
        last_modified1 = bundle1.last_modified
        version1 = bundle1.version

        self.assertNotEquals(last_modified0, last_modified1)
        self.assertNotEquals(version0, version1)

        # check if the previous attachment is correctly cleaned
        self.assertEquals(len(self._any_ira_for_bundle('css')), 3)
Exemplo n.º 9
0
 def load_script(path, module_name):
     full_path = get_resource_path(*path.split(os.path.sep))
     spec = importlib.util.spec_from_file_location(module_name, full_path)
     module = importlib.util.module_from_spec(spec)
     spec.loader.exec_module(module)
     return module
Exemplo n.º 10
0
 def _load(self, module, *args):
     tools.convert_file(self.cr, 'account_asset',
                        get_resource_path(module, *args), {}, 'init', False,
                        'test', self.registry._assertion_report)
Exemplo n.º 11
0
    def binary_content(cls,
                       xmlid=None,
                       model='ir.attachment',
                       id=None,
                       field='datas',
                       unique=False,
                       filename=None,
                       filename_field='datas_fname',
                       download=False,
                       mimetype=None,
                       default_mimetype='application/octet-stream',
                       access_token=None,
                       env=None):
        """ Get file, attachment or downloadable content

        If the ``xmlid`` and ``id`` parameter is omitted, fetches the default value for the
        binary field (via ``default_get``), otherwise fetches the field for
        that precise record.

        :param str xmlid: xmlid of the record
        :param str model: name of the model to fetch the binary from
        :param int id: id of the record from which to fetch the binary
        :param str field: binary field
        :param bool unique: add a max-age for the cache control
        :param str filename: choose a filename
        :param str filename_field: if not create an filename with model-id-field
        :param bool download: apply headers to download the file
        :param str mimetype: mintype of the field (for headers)
        :param str default_mimetype: default mintype if no mintype found
        :param str access_token: optional token for unauthenticated access
                                 only available  for ir.attachment
        :param Environment env: by default use request.env
        :returns: (status, headers, content)
        """
        env = env or request.env
        # get object and content
        obj = None
        if xmlid:
            obj = env.ref(xmlid, False)
        elif id and model == 'ir.attachment' and access_token:
            obj = env[model].sudo().browse(int(id))
            if not consteq(obj.access_token, access_token):
                return (403, [], None)
        elif id and model in env.registry:
            obj = env[model].browse(int(id))

        # obj exists
        if not obj or not obj.exists() or field not in obj:
            return (404, [], None)

        # check read access
        try:
            last_update = obj['__last_update']
        except AccessError:
            return (403, [], None)

        status, headers, content = None, [], None

        # attachment by url check
        module_resource_path = None
        if model == 'ir.attachment' and obj.type == 'url' and obj.url:
            url_match = re.match("^/(\w+)/(.+)$", obj.url)
            if url_match:
                module = url_match.group(1)
                module_path = get_module_path(module)
                module_resource_path = get_resource_path(
                    module, url_match.group(2))
                if module_path and module_resource_path:
                    module_path = os.path.join(
                        os.path.normpath(module_path),
                        '')  # join ensures the path ends with '/'
                    module_resource_path = os.path.normpath(
                        module_resource_path)
                    if module_resource_path.startswith(module_path):
                        with open(module_resource_path, 'rb') as f:
                            content = base64.b64encode(f.read())
                        last_update = pycompat.text_type(
                            os.path.getmtime(module_resource_path))

            if not module_resource_path:
                module_resource_path = obj.url

            if not content:
                status = 301
                content = module_resource_path
        else:
            content = obj[field] or ''

        # filename
        if not filename:
            if filename_field in obj:
                filename = obj[filename_field]
            elif module_resource_path:
                filename = os.path.basename(module_resource_path)
            else:
                filename = "%s-%s-%s" % (obj._name, obj.id, field)

        # mimetype
        mimetype = 'mimetype' in obj and obj.mimetype or False
        if not mimetype:
            if filename:
                mimetype = mimetypes.guess_type(filename)[0]
            if not mimetype and getattr(env[model]._fields[field],
                                        'attachment', False):
                # for binary fields, fetch the ir_attachement for mimetype check
                attach_mimetype = env['ir.attachment'].search_read(
                    domain=[('res_model', '=', model), ('res_id', '=', id),
                            ('res_field', '=', field)],
                    fields=['mimetype'],
                    limit=1)
                mimetype = attach_mimetype and attach_mimetype[0]['mimetype']
            if not mimetype:
                mimetype = guess_mimetype(base64.b64decode(content),
                                          default=default_mimetype)

        headers += [('Content-Type', mimetype),
                    ('X-Content-Type-Options', 'nosniff')]

        # cache
        etag = bool(request) and request.httprequest.headers.get(
            'If-None-Match')
        retag = '"%s"' % hashlib.md5(
            pycompat.to_text(content).encode('utf-8')).hexdigest()
        status = status or (304 if etag == retag else 200)
        headers.append(('ETag', retag))
        headers.append(
            ('Cache-Control', 'max-age=%s' % (STATIC_CACHE if unique else 0)))

        # content-disposition default name
        if download:
            headers.append(
                ('Content-Disposition', cls.content_disposition(filename)))
        return (status, headers, content)
Exemplo n.º 12
0
    def get_assets_editor_resources(self,
                                    key,
                                    get_views=True,
                                    get_less=True,
                                    bundles=False,
                                    bundles_restriction=[]):
        # Related views must be fetched if the user wants the views and/or the style
        views = request.env["ir.ui.view"].get_related_views(key,
                                                            bundles=bundles)
        views = views.read(
            ['name', 'id', 'key', 'xml_id', 'arch', 'active', 'inherit_id'])

        less_files_data_by_bundle = []

        # Load less only if asked by the user
        if get_less:
            # Compile regex outside of the loop
            # This will used to exclude library less files from the result
            excluded_url_matcher = re.compile(
                "^(.+/lib/.+)|(.+import_bootstrap.less)$")

            # Load already customized less files attachments
            custom_attachments = request.env["ir.attachment"].search([
                ("url", "=like",
                 self._make_custom_less_file_url("%%.%%", "%%"))
            ])

            # First check the t-call-assets used in the related views
            url_infos = dict()
            for v in views:
                for asset_call_node in etree.fromstring(
                        v["arch"]).xpath("//t[@t-call-assets]"):
                    if asset_call_node.get("t-css") == "false":
                        continue
                    asset_name = asset_call_node.get("t-call-assets")

                    # Loop through bundle files to search for LESS file info
                    less_files_data = []
                    for file_info in request.env["ir.qweb"]._get_asset_content(
                            asset_name, {})[0]:
                        if file_info["atype"] != "text/less":
                            continue
                        url = file_info["url"]

                        # Exclude library files (see regex above)
                        if excluded_url_matcher.match(url):
                            continue

                        # Check if the file is customized and get bundle/path info
                        less_file_data = self._match_less_file_url(url)
                        if not less_file_data:
                            continue

                        # Save info (arch will be fetched later)
                        url_infos[url] = less_file_data
                        less_files_data.append(url)

                    # Less data is returned sorted by bundle, with the bundles names and xmlids
                    if len(less_files_data):
                        less_files_data_by_bundle.append([
                            dict(xmlid=asset_name,
                                 name=request.env.ref(asset_name).name),
                            less_files_data
                        ])

            # Filter bundles/files:
            # - A file which appears in multiple bundles only appears in the first one (the first in the DOM)
            # - Only keep bundles with files which appears in the asked bundles and only keep those files
            for i in range(0, len(less_files_data_by_bundle)):
                bundle_1 = less_files_data_by_bundle[i]
                for j in range(0, len(less_files_data_by_bundle)):
                    bundle_2 = less_files_data_by_bundle[j]
                    # In unwanted bundles, keep only the files which are in wanted bundles too (less_helpers)
                    if bundle_1[0][
                            "xmlid"] not in bundles_restriction and bundle_2[
                                0]["xmlid"] in bundles_restriction:
                        bundle_1[1] = [
                            item_1 for item_1 in bundle_1[1]
                            if item_1 in bundle_2[1]
                        ]
            for i in range(0, len(less_files_data_by_bundle)):
                bundle_1 = less_files_data_by_bundle[i]
                for j in range(i + 1, len(less_files_data_by_bundle)):
                    bundle_2 = less_files_data_by_bundle[j]
                    # In every bundle, keep only the files which were not found in previous bundles
                    bundle_2[1] = [
                        item_2 for item_2 in bundle_2[1]
                        if item_2 not in bundle_1[1]
                    ]

            # Only keep bundles which still have files and that were requested
            less_files_data_by_bundle = [
                data for data in less_files_data_by_bundle
                if (len(data[1]) > 0 and (not bundles_restriction or data[0]
                                          ["xmlid"] in bundles_restriction))
            ]

            # Fetch the arch of each kept file, in each bundle
            for bundle_data in less_files_data_by_bundle:
                for i in range(0, len(bundle_data[1])):
                    url = bundle_data[1][i]
                    url_info = url_infos[url]

                    content = None
                    if url_info["customized"]:
                        # If the file is already customized, the content is found in the corresponding attachment
                        content = base64.b64decode(
                            custom_attachments.filtered(
                                lambda a: a.url == url).datas)
                    else:
                        # If the file is not yet customized, the content is found by reading the local less file
                        module = url_info["module"]
                        module_path = get_module_path(module)
                        module_resource_path = get_resource_path(
                            module, url_info["resource_path"])
                        if module_path and module_resource_path:
                            module_path = os.path.join(
                                os.path.normpath(module_path),
                                '')  # join ensures the path ends with '/'
                            module_resource_path = os.path.normpath(
                                module_resource_path)
                            if module_resource_path.startswith(module_path):
                                with open(module_resource_path, "rb") as f:
                                    content = f.read()

                    bundle_data[1][i] = dict(
                        url="/%s/%s" %
                        (url_info["module"], url_info["resource_path"]),
                        arch=content,
                        customized=url_info["customized"],
                    )

        return dict(
            views=get_views and views or [],
            less=get_less and less_files_data_by_bundle or [],
        )