def extract_bower_zipfile(self, zip_file, dest, expected_version=None):
        bower_json = None
        root = None
        deps_installed = []
        for info in zip_file.infolist():
            if PurePath(info.filename).name == "bower.json":
                with zip_file.open(info) as f:
                    bower_json = json.load(f)
                    root = str(PurePath(info.filename).parent)
                break
        version = bower_json["version"]
        if expected_version is not None:
            expected_version = Bower.clean_semver(expected_version)
            if not semver.match(version, expected_version):
                click.secho("error: versions do not match ({} =/= {})".format(
                    version, expected_version))
                raise InvalidPackageError
        if "dependencies" in bower_json:
            for package, version in bower_json["dependencies"].items():
                url = Bower.get_package_url(package)
                deps_installed.extend(self.get_bower_package(
                    url, dest=dest, version=version))
        ignore_patterns = [GitIgnorePattern(ig) for ig in bower_json["ignore"]]
        path_spec = PathSpec(ignore_patterns)
        namelist = [path for path in zip_file.namelist()
                    if PurePath(path).parts[0] == root]
        ignored = list(path_spec.match_files(namelist))
        for path in namelist:
            dest_path = PurePath(
                bower_json["name"],
                *PurePath(path).parts[1:])

            if path in ignored:
                continue

            for path in ignored:
                for parent in PurePath(path):
                    if parent in ignored:
                        continue

            if path.endswith("/"):
                if list(path_spec.match_files([str(dest_path)])):
                    ignored.append(PurePath(path))
                elif not (dest / dest_path).is_dir():
                    (dest / dest_path).mkdir(parents=True)
            else:
                target_path = dest / dest_path.parent / dest_path.name
                source = zip_file.open(path)
                target = target_path.open("wb")
                with source, target:
                    shutil.copyfileobj(source, target)
        deps_installed.append((bower_json["name"], bower_json["version"]))
        return deps_installed
def draw(self, context):
        import os
        import addon_utils

        layout = self.layout

        userpref = context.user_preferences
        used_ext = {ext.module for ext in userpref.addons}

        userpref_addons_folder = os.path.join(userpref.filepaths.script_directory, "addons")
        scripts_addons_folder = bpy.utils.user_resource('SCRIPTS', "addons")

        # collect the categories that can be filtered on
        addons = [(mod, addon_utils.module_bl_info(mod)) for mod in addon_utils.modules(refresh=False)]

        split = layout.split(percentage=0.2)
        col = split.column()
        col.prop(context.window_manager, "addon_search", text="", icon='VIEWZOOM')

        col.label(text="Supported Level")
        col.prop(context.window_manager, "addon_support", expand=True)

        col.label(text="Categories")
        col.prop(context.window_manager, "addon_filter", expand=True)

        col = split.column()

        # set in addon_utils.modules_refresh()
        if addon_utils.error_duplicates:
            self.draw_error(col,
                            "Multiple addons using the same name found!\n"
                            "likely a problem with the script search path.\n"
                            "(see console for details)",
                            )

        if addon_utils.error_encoding:
            self.draw_error(col,
                            "One or more addons do not have UTF-8 encoding\n"
                            "(see console for details)",
                            )

        filter = context.window_manager.addon_filter
        search = context.window_manager.addon_search.lower()
        support = context.window_manager.addon_support

        # initialized on demand
        user_addon_paths = []

        for mod, info in addons:
            module_name = mod.__name__

            is_enabled = module_name in used_ext

            if info["support"] not in support:
                continue

            # check if addon should be visible with current filters
            if ((filter == "All") or
                (filter == info["category"]) or
                (filter == "Enabled" and is_enabled) or
                (filter == "Disabled" and not is_enabled) or
                (filter == "User" and (mod.__file__.startswith((scripts_addons_folder, userpref_addons_folder))))
                ):

                if search and search not in info["name"].lower():
                    if info["author"]:
                        if search not in info["author"].lower():
                            continue
                    else:
                        continue

                # Addon UI Code
                col_box = col.column()
                box = col_box.box()
                colsub = box.column()
                row = colsub.row(align=True)

                row.operator(
                        "wm.addon_expand",
                        icon='TRIA_DOWN' if info["show_expanded"] else 'TRIA_RIGHT',
                        emboss=False,
                        ).module = module_name

                row.operator(
                        "wm.addon_disable" if is_enabled else "wm.addon_enable",
                        icon='CHECKBOX_HLT' if is_enabled else 'CHECKBOX_DEHLT', text="",
                        emboss=False,
                        ).module = module_name

                sub = row.row()
                sub.active = is_enabled
                sub.label(text='%s: %s' % (info["category"], info["name"]))
                if info["warning"]:
                    sub.label(icon='ERROR')

                # icon showing support level.
                sub.label(icon=self._support_icon_mapping.get(info["support"], 'QUESTION'))

                # Expanded UI (only if additional info is available)
                if info["show_expanded"]:
                    if info["description"]:
                        split = colsub.row().split(percentage=0.15)
                        split.label(text="Description:")
                        split.label(text=info["description"])
                    if info["location"]:
                        split = colsub.row().split(percentage=0.15)
                        split.label(text="Location:")
                        split.label(text=info["location"])
                    if mod:
                        split = colsub.row().split(percentage=0.15)
                        split.label(text="File:")
                        split.label(text=mod.__file__, translate=False)
                    if info["author"]:
                        split = colsub.row().split(percentage=0.15)
                        split.label(text="Author:")
                        split.label(text=info["author"], translate=False)
                    if info["version"]:
                        split = colsub.row().split(percentage=0.15)
                        split.label(text="Version:")
                        split.label(text='.'.join(str(x) for x in info["version"]), translate=False)
                    if info["warning"]:
                        split = colsub.row().split(percentage=0.15)
                        split.label(text="Warning:")
                        split.label(text='  ' + info["warning"], icon='ERROR')

                    user_addon = USERPREF_PT_addons.is_user_addon(mod, user_addon_paths)
                    tot_row = bool(info["wiki_url"]) + bool(user_addon)

                    if tot_row:
                        split = colsub.row().split(percentage=0.15)
                        split.label(text="Internet:")
                        if info["wiki_url"]:
                            split.operator("wm.url_open", text="Documentation", icon='HELP').url = info["wiki_url"]
                        split.operator("wm.url_open", text="Report a Bug", icon='URL').url = info.get(
                                "tracker_url",
                                "https://developer.blender.org/maniphest/task/edit/form/2")
                        if user_addon:
                            split.operator("wm.addon_remove", text="Remove", icon='CANCEL').module = mod.__name__

                        for i in range(4 - tot_row):
                            split.separator()

                    # Show addon user preferences
                    if is_enabled:
                        addon_preferences = userpref.addons[module_name].preferences
                        if addon_preferences is not None:
                            draw = getattr(addon_preferences, "draw", None)
                            if draw is not None:
                                addon_preferences_class = type(addon_preferences)
                                box_prefs = col_box.box()
                                box_prefs.label("Preferences:")
                                addon_preferences_class.layout = box_prefs
                                try:
                                    draw(context)
                                except:
                                    import traceback
                                    traceback.print_exc()
                                    box_prefs.label(text="Error (see console)", icon='ERROR')
                                del addon_preferences_class.layout

        # Append missing scripts
        # First collect scripts that are used but have no script file.
        module_names = {mod.__name__ for mod, info in addons}
        missing_modules = {ext for ext in used_ext if ext not in module_names}

        if missing_modules and filter in {"All", "Enabled"}:
            col.column().separator()
            col.column().label(text="Missing script files")

            module_names = {mod.__name__ for mod, info in addons}
            for module_name in sorted(missing_modules):
                is_enabled = module_name in used_ext
                # Addon UI Code
                box = col.column().box()
                colsub = box.column()
                row = colsub.row(align=True)

                row.label(text="", icon='ERROR')

                if is_enabled:
                    row.operator("wm.addon_disable", icon='CHECKBOX_HLT', text="", emboss=False).module = module_name

                row.label(text=module_name, translate=False)