def __init__(self): super().__init__('Installed') self.coreModDir = Path(get_freecad_resource_path(), 'Mod') self.userModDir = get_mod_path() self.userMacroDir = get_macro_path() self.workbenches = Gui.listWorkbenches() self.updates = {} self.showCorePackages = True
def getMacroList(self): macros = [] install_dir = get_macro_path() default_icon = utils.path_to_url( get_resource_path('html', 'img', 'package_macro.svg')) try: content = http_get(self.url, timeout=45) if content: data = json.loads(content) wiki = get_page_content_from_json(data) for m_link in MACRO_LINK.finditer(wiki): name = m_link.group('name').replace(' ', '_') label = m_link.group('label') description = m_link.group('description') icon = m_link.group('icon') if icon: icon = self.wiki + '/Special:Redirect/file/' + icon.replace( ' ', '_') else: icon = default_icon pkg = PackageInfo( key=name, installDir=install_dir, installFile=Path(install_dir, name + '.FCMacro'), name=name, title=label, description=description, icon=icon, isCore=False, type='Macro', isGit=False, isWiki=True, markedAsSafe=False, categories=[tr('Uncategorized')], date=None, version=None, readmeUrl='{0}/Macro_{1}?origin=*'.format( self.wiki, name), readmeFormat='html') flags.apply_predefined_flags(pkg) macros.append(pkg) except: traceback.print_exc(file=sys.stderr) return macros
def uninstall_macro(path): dirs = set() macros = get_macro_path() for file in get_macro_files(path): if file.exists(): if file.is_file(): os.remove(file) dirs.add(Path(file.parent)) elif file.is_dir(): dirs.append(file) for d in dirs: if not os.listdir(d) and d != macros: os.rmdir(d)
def linkMacrosFromMod(self, pkg): # Ensure macro dir macros = get_macro_path() if not macros.exists(): macros.mkdir(parents=True) # Search and link if pkg.installDir.exists(): for f in pkg.installDir.iterdir(): if f.name.lower().endswith(".fcmacro"): Path(macros, f.name).symlink_to(f) pref.set_plugin_parameter(pkg.name, 'destination', str(pkg.installDir))
def restore_absolute_paths(content): """Replace placeholders with current absolute paths.""" core_res_dir = get_freecad_resource_path() content = content.replace(_CORE_RES_DIR_, str(core_res_dir)) content = content.replace(_CORE_RES_URL_, core_res_dir.as_uri()) user_data_dir = get_app_data_path() content = content.replace(_USER_DATA_DIR_, str(user_data_dir)) content = content.replace(_USER_DATA_URL_, user_data_dir.as_uri()) user_macro_dir = get_macro_path() content = content.replace(_USER_MACRO_DIR_, str(user_macro_dir)) content = content.replace(_USER_MACRO_URL_, user_macro_dir.as_uri()) return content
def installMod(self, pkg): # Get Git info git_available, _, git_version, git_python, git_version_ok = install_info( ) # Get zip info zip_available = zlib.is_zip_available() # Initialize result result = InstallResult(gitAvailable=git_available, gitPythonAvailable=git_python is not None, zipAvailable=zip_available, gitVersionOk=git_version_ok, gitVersion=git_version) # Check valid install dir in_mods = get_mod_path() == pkg.installDir.parent in_macros = pkg.installFile and get_macro_path( ) in pkg.installFile.parents if not in_mods and not in_macros: log('Invalid install dir: {0}'.format(pkg.installDir)) result.ok = False result.invalidInstallDir = True return result # Try Git install if git_available and git_version_ok and git_python: result = self.installModFromGit(pkg, result) # Try zip/http install elif zip_available: result = self.installModFromHttpZip(pkg, result) if result.ok: try: self.linkMacrosFromMod(pkg) except: # ! TODO: Rollback everything if macro links fail? pass return result
def getMacroList(self): install_dir = get_macro_path() macros = [] path = self.downloadMacroList() if path: workers = [] for entry in path.glob('**/*'): if '.git' in entry.name.lower(): continue if entry.name.lower().endswith('.fcmacro'): worker = Worker(build_macro_package, entry, entry.stem, is_git=True, install_path=Path(install_dir, entry.name), base_path=entry.relative_to(path).parent) worker.start() workers.append(worker) macros = [flags.apply_predefined_flags(w.get()) for w in workers] return macros
def remove_absolute_paths(content): """Replace absolute paths to placeholders.""" # ! I don't like this code, improve later # This is used to store data in files as cache without hard references to # FreeCAD directories because directories can be different at restore time # ie. AppImage core_res_dir = get_freecad_resource_path() content = content.replace(core_res_dir.as_uri(), _CORE_RES_URL_) content = content.replace(str(core_res_dir), _CORE_RES_DIR_) user_data_dir = get_app_data_path() content = content.replace(user_data_dir.as_uri(), _USER_DATA_URL_) content = content.replace(str(user_data_dir), _USER_DATA_DIR_) user_macro_dir = get_macro_path() content = content.replace(user_macro_dir.as_uri(), _USER_MACRO_URL_) content = content.replace(str(user_macro_dir), _USER_MACRO_DIR_) return content
def build_macro_package(path, macro_name, is_core=False, is_git=False, is_wiki=False, install_path=None, base_path=""): with open(path, 'r', encoding='utf-8') as f: try: tags = get_macro_tags(f.read(), path) except: # !TODO: Handle encoding problems in old windows platforms tags = {k: None for k in MACRO_TAG_FILTER} log_err(tr('Macro {0} contains invalid characters').format(path)) install_dir = get_macro_path() base = dict(key=str(install_path) if install_path else str(path), type='Macro', isCore=is_core, installDir=install_dir, installFile=Path(install_dir, path.name), isGit=is_git, isWiki=is_wiki, basePath=base_path) tags.update(base) if not tags['title']: tags['title'] = tags['name'] or macro_name tags['name'] = macro_name # Always override name with actual file name if not tags['icon']: tags['icon'] = get_resource_path('html', 'img', 'package_macro.svg') try: if not Path(tags['icon']).exists(): tags['icon'] = get_resource_path('html', 'img', 'package_macro.svg') except: tags['icon'] = get_resource_path('html', 'img', 'package_macro.svg') tags['icon'] = utils.path_to_url(tags['icon']) if tags['comment']: tags['description'] = tags['comment'] if not tags['description']: tags['description'] = tr('Warning! No description') if tags['categories']: cats = COMMA_SEP_LIST_PATTERN.split(tags['categories']) tags['categories'] = [tr(c) for c in cats] else: tags['categories'] = [tr('Uncategorized')] if tags['files']: tags['files'] = COMMA_SEP_LIST_PATTERN.split(tags['files']) if tags['readme']: tags['readmeUrl'] = tags['readme'] tags['readmeFormat'] = 'html' elif tags['wiki']: tags['readmeUrl'] = tags['wiki'] tags['readmeFormat'] = 'html' elif tags['web']: tags['readmeUrl'] = tags['web'] tags['readmeFormat'] = 'html' return PackageInfo(**tags)
def installMacro(self, pkg): (git_available, _, git_version, git_python, git_version_ok) = install_info() # Initialize result result = InstallResult(gitAvailable=git_available, gitPythonAvailable=git_python is not None, zipAvailable=zlib.is_zip_available(), gitVersionOk=git_version_ok, gitVersion=git_version) # Ensure last version if available locally src_dir = self.downloadMacroList() # Get path of source macro file src_file = Path(src_dir, pkg.basePath, pkg.installFile.name) # Copy Macro files = [] try: macros_dir = get_macro_path() if not macros_dir.exists(): macros_dir.mkdir(parents=True) log('Installing', pkg.installFile) shutil.copy2(src_file, pkg.installFile) files.append(pkg.installFile) # Copy files if pkg.files: for f in pkg.files: file_base_path = utils.path_relative(f) dst = Path(pkg.installDir, file_base_path).absolute() src = Path(src_dir, pkg.basePath, file_base_path).absolute() log('Installing ', dst) if pkg.installDir not in dst.parents: result.message = tr( 'Macro package attempts to install files outside of permitted path' ) raise Exception() if src_dir not in src.parents: result.message = tr( 'Macro package attempts to access files outside of permitted path' ) raise Exception() dst_dir = dst.parent if dst_dir != pkg.installDir and dst_dir not in files and not dst_dir.exists( ): dst_dir.mkdir(parents=True) files.append(dst_dir) shutil.copy2(src, dst) files.append(dst) result.ok = True except: log(traceback.format_exc()) result.ok = False if not result.message: result.message = tr( 'Macro was not installed, please contact the maintainer.') # Rollback files.sort(reverse=True) for f in files: try: log("Rollback ", f) if f.is_file(): f.unlink() elif f.is_dir(): shutil.rmtree(f, ignore_errors=True) except: log(traceback.format_exc()) return result