Ejemplo n.º 1
0
 def run(self):
     self.progressbar_show.emit(True)
     self.info_label.emit(translate("AddonsInstaller", "Retrieving description..."))
     if len(self.repos[self.idx]) == 4:
         desc = self.repos[self.idx][3]
     else:
         url = self.repos[self.idx][1]
         self.info_label.emit(translate("AddonsInstaller", "Retrieving info from") + ' ' + str(url))
         u = urlopen(url)
         p = u.read()
         if sys.version_info.major >= 3 and isinstance(p, bytes):
             p = p.decode("utf-8")
         u.close()
         desc = re.findall("<meta property=\"og:description\" content=\"(.*?)\"",p)
         if desc:
             desc = desc[0]
             if self.repos[self.idx][0] in OBSOLETE:
                 desc += " <b>This add-on is marked as obsolete</b> - This usually means it is no longer maintained, and some more advanced add-on in this list provides the same functionality."
         else:
             desc = "Unable to retrieve addon description"
         self.repos[self.idx].append(desc)
         self.addon_repos.emit(self.repos)
     if self.repos[self.idx][2] == 1:
         upd = False
         # checking for updates
         if not NOGIT:
             try:
                 import git
             except:
                 pass
             else:
                 repo = self.repos[self.idx]
                 clonedir = FreeCAD.getUserAppDataDir() + os.sep + "Mod" + os.sep + repo[0]
                 if os.path.exists(clonedir):
                     if not os.path.exists(clonedir + os.sep + '.git'):
                         # Repair addon installed with raw download
                         bare_repo = git.Repo.clone_from(repo[1], clonedir + os.sep + '.git', bare=True)
                         try:
                             with bare_repo.config_writer() as cw:
                                 cw.set('core', 'bare', False)
                         except AttributeError:
                             FreeCAD.Console.PrintWarning(translate("AddonsInstaller", "Outdated GitPython detected, consider upgrading with pip.")+"\n")
                             cw = bare_repo.config_writer()
                             cw.set('core', 'bare', False)
                             del cw
                         repo = git.Repo(clonedir)
                         repo.head.reset('--hard')
                     gitrepo = git.Git(clonedir)
                     gitrepo.fetch()
                     if "git pull" in gitrepo.status():
                         upd = True
         if upd:
             message = "<strong>" + translate("AddonsInstaller", "An update is available for this addon.") + "</strong><br>" + desc + ' - <a href="' + self.repos[self.idx][1] + '"><span style="word-wrap: break-word;width:15em;text-decoration: underline; color:#0000ff;">' + self.repos[self.idx][1] + '</span></a>'
         else:
             message = "<strong>" + translate("AddonsInstaller", "This addon is already installed.") + "</strong><br>" + desc + ' - <a href="' + self.repos[self.idx][1] + '"><span style="word-wrap: break-word;width:15em;text-decoration: underline; color:#0000ff;">' + self.repos[self.idx][1] + '</span></a>'
     else:
         message = desc + ' - <a href="' + self.repos[self.idx][1] + '"><span style="word-wrap: break-word;width:15em;text-decoration: underline; color:#0000ff;">' + self.repos[self.idx][1] + '</span></a>'
     self.info_label.emit( message )
     self.progressbar_show.emit(False)
     self.stop = True
Ejemplo n.º 2
0
 def run(self):
     "populates the list of addons"
     self.progressbar_show.emit(True)
     u = urlopen("https://github.com/FreeCAD/FreeCAD-addons")
     p = u.read()
     if sys.version_info.major >= 3 and isinstance(p, bytes):
         p = p.decode("utf-8")
     u.close()
     p = p.replace("\n"," ")
     p = re.findall("octicon-file-submodule(.*?)message",p)
     basedir = FreeCAD.getUserAppDataDir()
     moddir = basedir + os.sep + "Mod"
     repos = []
     for l in p:
         #name = re.findall("data-skip-pjax=\"true\">(.*?)<",l)[0]
         name = re.findall("title=\"(.*?) @",l)[0]
         self.info_label.emit(name)
         #url = re.findall("title=\"(.*?) @",l)[0]
         url = "https://github.com/" + re.findall("href=\"\/(.*?)\/tree",l)[0]
         addondir = moddir + os.sep + name
         #print ("found:",name," at ",url)
         if not os.path.exists(addondir):
             state = 0
         else:
             state = 1
         repos.append([name,url,state])
     if not repos:
         self.info_label.emit(translate("AddonsInstaller", "Unable to download addon list."))
     else:
         repos = sorted(repos, key=lambda s: s[0].lower())
         for repo in repos:
             self.addon_repo.emit(repo)
         self.info_label.emit(translate("AddonsInstaller", "Workbenches list was updated."))
     self.progressbar_show.emit(False)
     self.stop = True
Ejemplo n.º 3
0
 def download(self,giturl,clonedir):
     "downloads and unzip from github"
     import zipfile
     bakdir = None
     if os.path.exists(clonedir):
         bakdir = clonedir+".bak"
         if os.path.exists(bakdir):
             shutil.rmtree(bakdir)
         os.rename(clonedir,bakdir)
     os.makedirs(clonedir)
     zipurl = giturl+"/archive/master.zip"
     try:
         print("Downloading "+zipurl)
         u = urlopen(zipurl)
     except:
         return translate("AddonsInstaller", "Error: Unable to download") + " " + zipurl
     zfile = _stringio()
     zfile.write(u.read())
     zfile = zipfile.ZipFile(zfile)
     master = zfile.namelist()[0] # github will put everything in a subfolder
     zfile.extractall(clonedir)
     u.close()
     zfile.close()
     for filename in os.listdir(clonedir+os.sep+master):
         shutil.move(clonedir+os.sep+master+os.sep+filename, clonedir+os.sep+filename)
     os.rmdir(clonedir+os.sep+master)
     if bakdir:
         shutil.rmtree(bakdir)
     return translate("AddonsInstaller", "Successfully installed") + " " + zipurl
Ejemplo n.º 4
0
    def checkDependencies(self,baseurl):
        "checks if the repo contains a metadata.txt and check its contents"
        import FreeCADGui
        ok = True
        message = ""
        depsurl = baseurl.replace("github.com","raw.githubusercontent.com")
        if not depsurl.endswith("/"):
            depsurl += "/"
        depsurl += "master/metadata.txt"
        try:
            mu = urlopen(depsurl)
        except urllib2.HTTPError:
            # no metadata.txt, we just continue without deps checking
            pass
        else:
            # metadata.txt found
            depsfile = mu.read()
            mu.close()

            # urllib2 gives us a bytelike object instead of a string. Have to consider that
            try:
                depsfile = depsfile.decode('utf-8')
            except AttributeError:
                pass

            deps = depsfile.split("\n")
            for l in deps:
                if l.startswith("workbenches="):
                    depswb = l.split("=")[1].split(",")
                    for wb in depswb:
                        if wb.strip():
                            if not wb.strip() in FreeCADGui.listWorkbenches().keys():
                                if not wb.strip()+"Workbench" in FreeCADGui.listWorkbenches().keys():
                                    ok = False
                                    message += translate("AddonsInstaller","Missing workbench") + ": " + wb + ", "
                elif l.startswith("pylibs="):
                    depspy = l.split("=")[1].split(",")
                    for pl in depspy:
                        if pl.strip():
                            try:
                                __import__(pl.strip())
                            except:
                                ok = False
                                message += translate("AddonsInstaller","Missing python module") +": " + pl + ", "
                elif l.startswith("optionalpylibs="):
                    opspy = l.split("=")[1].split(",")
                    for pl in opspy:
                        if pl.strip():
                            try:
                                __import__(pl.strip())
                            except:
                                message += translate("AddonsInstaller","Missing optional python module (doesn't prevent installing)") +": " + pl + ", "
        if message and (not ok):
            message = translate("AddonsInstaller", "Some errors were found that prevent to install this workbench") + ": <b>" + message + "</b>. "
            message += translate("AddonsInstaller","Please install the missing components first.")
        return ok, message
Ejemplo n.º 5
0
 def run(self):
     i = 0
     for repo in self.repos:
         url = repo[1]
         u = urlopen(url)
         p = u.read()
         if sys.version_info.major >= 3 and isinstance(p, bytes):
             p = p.decode("utf-8")
         u.close()
         desc = re.findall("<meta property=\"og:description\" content=\"(.*?)\"",p)
         if desc:
             desc = desc[0]
         else:
             desc = "Unable to retrieve addon description"
         self.repos[i].append(desc)
         i += 1
         self.addon_repos.emit(self.repos)
     self.stop = True
Ejemplo n.º 6
0
    def retrieve_macros_from_wiki(self):
        """Retrieve macros from the wiki

        Read the wiki and emit a signal for each found macro.
        Reads only the page https://www.freecadweb.org/wiki/Macros_recipes.
        """
        self.info_label_signal.emit("Downloading list of macros...")
        self.progressbar_show.emit(True)
        u = urlopen("https://www.freecadweb.org/wiki/Macros_recipes")
        p = u.read()
        u.close()
        if sys.version_info.major >= 3 and isinstance(p, bytes):
            p = p.decode("utf-8")
        macros = re.findall('title="(Macro.*?)"', p)
        macros = [mac for mac in macros if ('translated' not in mac)]
        for mac in macros:
            macname = mac[6:]  # Remove "Macro ".
            macname = macname.replace("&amp;","&")
            if (macname not in MACROS_BLACKLIST) and ('recipes' not in macname.lower()):
                macro = Macro(macname)
                macro.on_wiki = True
                self.macros.append(macro)
Ejemplo n.º 7
0
def getIfcOpenShell(force=False):
    """downloads and installs IfcOpenShell"""

    ifcok = False
    if not force:
        try:
            import ifcopenshell
        except:
            ifcok = False
        else:
            ifcok = True

    if not ifcok:
        # ifcopenshell not installed
        import re, json
        from PySide import QtGui
        import zipfile
        import addonmanager_utilities
        if not FreeCAD.GuiUp:
            reply = QtGui.QMessageBox.Yes
        else:
            reply = QtGui.QMessageBox.question(
                None, translate("BIM", "IfcOpenShell not found"),
                translate(
                    "BIM",
                    "IfcOpenShell is needed to import and export IFC files. It appears to be missing on your system. Would you like to download and install it now? It will be installed in FreeCAD's Macros directory."
                ), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
                QtGui.QMessageBox.No)
        if reply == QtGui.QMessageBox.Yes:
            print(
                "Loading list of latest IfcOpenBot builds from https://github.com/IfcOpenBot/IfcOpenShell..."
            )
            url1 = "https://api.github.com/repos/IfcOpenBot/IfcOpenShell/comments?per_page=100"
            u = addonmanager_utilities.urlopen(url1)
            if u:
                r = u.read()
                u.close()
                d = json.loads(r)
                l = d[-1]['body']
                links = re.findall("http.*?zip", l)
                pyv = "python-" + str(sys.version_info.major) + str(
                    sys.version_info.minor)
                if sys.platform.startswith("linux"):
                    plat = "linux"
                elif sys.platform.startswith("win"):
                    plat = "win"
                elif sys.platform.startswith("darwin"):
                    plat = "macos"
                else:
                    print("Error - unknown platform")
                    return
                if sys.maxsize > 2**32:
                    plat += "64"
                else:
                    plat += "32"
                print("Looking for", plat, pyv)
                for link in links:
                    if ("ifcopenshell-" + pyv in link) and (plat in link):
                        print("Downloading " + link + "...")
                        p = FreeCAD.ParamGet(
                            "User parameter:BaseApp/Preferences/Macro")
                        fp = p.GetString(
                            "MacroPath",
                            os.path.join(FreeCAD.getUserAppDataDir(),
                                         "Macros"))
                        u = addonmanager_utilities.urlopen(link)
                        if u:
                            if sys.version_info.major < 3:
                                import StringIO as io
                                _stringio = io.StringIO
                            else:
                                import io
                                _stringio = io.BytesIO
                            zfile = _stringio()
                            zfile.write(u.read())
                            zfile = zipfile.ZipFile(zfile)
                            zfile.extractall(fp)
                            u.close()
                            zfile.close()
                            print("Successfully installed IfcOpenShell to", fp)
                            break
                else:
                    print("Unable to find a build for your version")
Ejemplo n.º 8
0
    def run(self):

        "populates the list of addons"

        self.progressbar_show.emit(True)
        u = utils.urlopen("https://github.com/FreeCAD/FreeCAD-addons")
        if not u:
            self.progressbar_show.emit(False)
            self.done.emit()
            self.stop = True
            return
        p = u.read()
        if sys.version_info.major >= 3 and isinstance(p, bytes):
            p = p.decode("utf-8")
        u.close()
        p = p.replace("\n"," ")
        p = re.findall("octicon-file-submodule(.*?)message",p)
        basedir = FreeCAD.getUserAppDataDir()
        moddir = basedir + os.sep + "Mod"
        repos = []
        # querying official addons
        for l in p:
            #name = re.findall("data-skip-pjax=\"true\">(.*?)<",l)[0]
            res = re.findall("title=\"(.*?) @",l)
            if res:
                name = res[0]
            else:
                print("AddonMananger: Debug: couldn't find title in",l)
                continue
            self.info_label.emit(name)
            #url = re.findall("title=\"(.*?) @",l)[0]
            url = utils.getRepoUrl(l)
            if url:
                addondir = moddir + os.sep + name
                #print ("found:",name," at ",url)
                if os.path.exists(addondir) and os.listdir(addondir):
                    # make sure the folder exists and it contains files!
                    state = 1
                else:
                    state = 0
                repos.append([name,url,state])
        # querying custom addons
        customaddons = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons").GetString("CustomRepositories","").split("\n")
        for url in customaddons:
            if url:
                name = url.split("/")[-1]
                if name.lower().endswith(".git"):
                    name = name[:-4]
                addondir = moddir + os.sep + name
                if not os.path.exists(addondir):
                    state = 0
                else:
                    state = 1
                repos.append([name,url,state])
        if not repos:
            self.info_label.emit(translate("AddonsInstaller", "Unable to download addon list."))
        else:
            repos = sorted(repos, key=lambda s: s[0].lower())
            for repo in repos:
                self.addon_repo.emit(repo)
            self.info_label.emit(translate("AddonsInstaller", "Workbenches list was updated."))
        self.progressbar_show.emit(False)
        self.done.emit()
        self.stop = True
Ejemplo n.º 9
0
 def fill_details_from_wiki(self, url):
     code = ""
     try:
         u = urlopen(url)
     except urllib2.HTTPError:
         return
     p = u.read()
     if sys.version_info.major >= 3 and isinstance(p, bytes):
         p = p.decode('utf-8')
     u.close()
     # check if the macro page has its code hosted elsewhere, download if needed
     if "rawcodeurl" in p:
         rawcodeurl = re.findall("rawcodeurl.*?href=\"(http.*?)\">",p)
         if rawcodeurl:
             rawcodeurl = rawcodeurl[0]
             try:
                 u2 = urlopen(rawcodeurl)
             except urllib2.HTTPError:
                 return
             # code = u2.read()
             # github is slow to respond... We need to use this trick below
             response = ""
             block = 8192
             #expected = int(u2.headers['content-length'])
             while 1:
                 #print("expected:",expected,"got:",len(response))
                 data = u2.read(block)
                 if not data:
                     break
                 if sys.version_info.major >= 3 and isinstance(data, bytes):
                     data = data.decode('utf-8')
                 response += data
             if response:
                 code = response
             u2.close()
     if not code:
         code = re.findall('<pre>(.*?)<\/pre>', p.replace('\n', '--endl--'))
         if code:
             # code = code[0]
             # take the biggest code block
             code = sorted(code, key=len)[-1]
             code = code.replace('--endl--', '\n')
         else:
             FreeCAD.Console.PrintWarning(translate("AddonsInstaller", "Unable to fetch the code of this macro."))
         # Clean HTML escape codes.
         try:
             from HTMLParser import HTMLParser
         except ImportError:
             from html.parser import HTMLParser
         if sys.version_info.major < 3:
             code = code.decode('utf8')
         try:
             code = HTMLParser().unescape(code)
             code = code.replace(b'\xc2\xa0'.decode("utf-8"), ' ')
         except:
             FreeCAD.Console.PrintWarning(translate("AddonsInstaller", "Unable to clean macro code: ") + code + '\n')
         if sys.version_info.major < 3:
             code = code.encode('utf8')
     desc = re.findall("<td class=\"ctEven left macro-description\">(.*?)<\/td>", p.replace('\n', ' '))
     if desc:
         desc = desc[0]
     else:
         FreeCAD.Console.PrintWarning(translate("AddonsInstaller", "Unable to retrieve a description for this macro."))
         desc = "No description available"
     self.desc = desc
     self.url = url
     self.code = code
     self.parsed = True
Ejemplo n.º 10
0
 def fill_details_from_wiki(self, url):
     code = ""
     u = urlopen(url)
     if u is None:
         print(
             "AddonManager: Debug: connection is lost (proxy setting changed?)",
             url)
         return
     p = u.read()
     if sys.version_info.major >= 3 and isinstance(p, bytes):
         p = p.decode("utf-8")
     u.close()
     # check if the macro page has its code hosted elsewhere, download if needed
     if "rawcodeurl" in p:
         rawcodeurl = re.findall("rawcodeurl.*?href=\"(http.*?)\">", p)
         if rawcodeurl:
             rawcodeurl = rawcodeurl[0]
             u2 = urlopen(rawcodeurl)
             if u2 is None:
                 print("AddonManager: Debug: unable to open URL",
                       rawcodeurl)
                 return
             # code = u2.read()
             # github is slow to respond... We need to use this trick below
             response = ""
             block = 8192
             # expected = int(u2.headers["content-length"])
             while True:
                 # print("expected:", expected, "got:", len(response))
                 data = u2.read(block)
                 if not data:
                     break
                 if sys.version_info.major >= 3 and isinstance(data, bytes):
                     data = data.decode("utf-8")
                 response += data
             if response:
                 code = response
             u2.close()
     if not code:
         code = re.findall(r"<pre>(.*?)</pre>", p.replace("\n", "--endl--"))
         if code:
             # code = code[0]
             # take the biggest code block
             code = sorted(code, key=len)[-1]
             code = code.replace("--endl--", "\n")
         else:
             FreeCAD.Console.PrintWarning(
                 translate("AddonsInstaller",
                           "Unable to fetch the code of this macro."))
         # Clean HTML escape codes.
         if sys.version_info.major < 3:
             code = code.decode("utf8")
         code = unescape(code)
         code = code.replace(b"\xc2\xa0".decode("utf-8"), " ")
         if sys.version_info.major < 3:
             code = code.encode("utf8")
     desc = re.findall(
         r"<td class=\"ctEven left macro-description\">(.*?)</td>",
         p.replace("\n", " "))
     if desc:
         desc = desc[0]
     else:
         FreeCAD.Console.PrintWarning(
             translate("AddonsInstaller",
                       "Unable to retrieve a description for this macro."))
         desc = "No description available"
     self.desc = desc
     self.url = url
     self.code = code
     self.parsed = True
Ejemplo n.º 11
0
 def run(self):
     self.progressbar_show.emit(True)
     self.info_label.emit(
         translate("AddonsInstaller", "Retrieving description..."))
     if len(self.repos[self.idx]) == 4:
         desc = self.repos[self.idx][3]
     else:
         url = self.repos[self.idx][1]
         self.info_label.emit(
             translate("AddonsInstaller", "Retrieving info from ") +
             str(url))
         u = urlopen(url)
         p = u.read()
         if sys.version_info.major >= 3 and isinstance(p, bytes):
             p = p.decode("utf-8")
         u.close()
         desc = re.findall(
             "<meta property=\"og:description\" content=\"(.*?)\"", p)
         if desc:
             desc = desc[0]
         else:
             desc = "Unable to retrieve addon description"
         self.repos[self.idx].append(desc)
         self.addon_repos.emit(self.repos)
     if self.repos[self.idx][2] == 1:
         upd = False
         # checking for updates
         if not NOGIT:
             try:
                 import git
             except:
                 pass
             else:
                 repo = self.repos[self.idx]
                 clonedir = FreeCAD.getUserAppDataDir(
                 ) + os.sep + "Mod" + os.sep + repo[0]
                 if os.path.exists(clonedir):
                     if not os.path.exists(clonedir + os.sep + '.git'):
                         # Repair addon installed with raw download
                         bare_repo = git.Repo.clone_from(repo[1],
                                                         clonedir + os.sep +
                                                         '.git',
                                                         bare=True)
                         try:
                             with bare_repo.config_writer() as cw:
                                 cw.set('core', 'bare', False)
                         except AttributeError:
                             FreeCAD.Console.PrintWarning(
                                 translate(
                                     "AddonsInstaller",
                                     "Outdated GitPython detected, consider upgrading with pip."
                                 ) + "\n")
                             cw = bare_repo.config_writer()
                             cw.set('core', 'bare', False)
                             del cw
                         repo = git.Repo(clonedir)
                         repo.head.reset('--hard')
                     gitrepo = git.Git(clonedir)
                     gitrepo.fetch()
                     if "git pull" in gitrepo.status():
                         upd = True
         if upd:
             message = "<strong>" + translate(
                 "AddonsInstaller", "An update is available for this addon."
             ) + "</strong><br>" + desc + ' - <a href="' + self.repos[self.idx][
                 1] + '"><span style="word-wrap: break-word;width:15em;text-decoration: underline; color:#0000ff;">' + self.repos[
                     self.idx][1] + '</span></a>'
         else:
             message = "<strong>" + translate(
                 "AddonsInstaller", "This addon is already installed."
             ) + "</strong><br>" + desc + ' - <a href="' + self.repos[self.idx][
                 1] + '"><span style="word-wrap: break-word;width:15em;text-decoration: underline; color:#0000ff;">' + self.repos[
                     self.idx][1] + '</span></a>'
     else:
         message = desc + ' - <a href="' + self.repos[self.idx][
             1] + '"><span style="word-wrap: break-word;width:15em;text-decoration: underline; color:#0000ff;">' + self.repos[
                 self.idx][1] + '</span></a>'
     self.info_label.emit(message)
     self.progressbar_show.emit(False)
     self.stop = True
Ejemplo n.º 12
0
 def fill_details_from_wiki(self, url):
     code = ""
     try:
         u = urlopen(url)
     except urllib2.HTTPError:
         return
     p = u.read()
     if sys.version_info.major >= 3 and isinstance(p, bytes):
         p = p.decode('utf-8')
     u.close()
     # check if the macro page has its code hosted elsewhere, download if needed
     if "rawcodeurl" in p:
         rawcodeurl = re.findall("rawcodeurl.*?href=\"(http.*?)\">", p)
         if rawcodeurl:
             rawcodeurl = rawcodeurl[0]
             try:
                 u2 = urlopen(rawcodeurl)
             except urllib2.HTTPError:
                 return
             # code = u2.read()
             # github is slow to respond... We need to use this trick below
             response = ""
             block = 8192
             #expected = int(u2.headers['content-length'])
             while 1:
                 #print("expected:",expected,"got:",len(response))
                 data = u2.read(block)
                 if not data:
                     break
                 response += data
             if response:
                 code = response
             if sys.version_info.major >= 3 and isinstance(code, bytes):
                 code = code.decode('utf-8')
             u2.close()
     if not code:
         code = re.findall('<pre>(.*?)<\/pre>', p.replace('\n', '--endl--'))
         if code:
             # code = code[0]
             # take the biggest code block
             code = sorted(code, key=len)[-1]
             code = code.replace('--endl--', '\n')
         else:
             FreeCAD.Console.PrintWarning(
                 translate("AddonsInstaller",
                           "Unable to fetch the code of this macro."))
         # Clean HTML escape codes.
         try:
             from HTMLParser import HTMLParser
         except ImportError:
             from html.parser import HTMLParser
         if sys.version_info.major < 3:
             code = code.decode('utf8')
         try:
             code = HTMLParser().unescape(code)
             code = code.replace(b'\xc2\xa0'.decode("utf-8"), ' ')
         except:
             FreeCAD.Console.PrintWarning(
                 translate("AddonsInstaller",
                           "Unable to clean macro code: ") + code + '\n')
         if sys.version_info.major < 3:
             code = code.encode('utf8')
     desc = re.findall(
         "<td class=\"ctEven left macro-description\">(.*?)<\/td>",
         p.replace('\n', ' '))
     if desc:
         desc = desc[0]
     else:
         FreeCAD.Console.PrintWarning(
             translate("AddonsInstaller",
                       "Unable to retrieve a description for this macro."))
         desc = "No description available"
     self.desc = desc
     self.url = url
     self.code = code
     self.parsed = True
Ejemplo n.º 13
0
    def run(self):
        "populates the list of addons"

        self.progressbar_show.emit(True)

        # update info lists
        global obsolete, macros_blacklist, py2only
        u = utils.urlopen(
            "https://raw.githubusercontent.com/FreeCAD/FreeCAD-addons/master/addonflags.json"
        )
        if u:
            p = u.read()
            if sys.version_info.major >= 3 and isinstance(p, bytes):
                p = p.decode("utf-8")
            u.close()
            hit = re.findall(
                r'"obsolete"[^\{]*?{[^\{]*?"Mod":\[(?P<obsolete>[^\[\]]+?)\]}',
                p.replace("\n", "").replace(" ", ""))
            if hit:
                obsolete = hit[0].replace('"', "").split(",")
            hit = re.findall(
                r'"blacklisted"[^\{]*?{[^\{]*?"Macro":\[(?P<blacklisted>[^\[\]]+?)\]}',
                p.replace("\n", "").replace(" ", ""))
            if hit:
                macros_blacklist = hit[0].replace('"', "").split(",")
            hit = re.findall(
                r'"py2only"[^\{]*?{[^\{]*?"Mod":\[(?P<py2only>[^\[\]]+?)\]}',
                p.replace("\n", "").replace(" ", ""))
            if hit:
                py2only = hit[0].replace('"', "").split(",")
        else:
            print("Debug: addon_flags.json not found")

        u = utils.urlopen(
            "https://raw.githubusercontent.com/FreeCAD/FreeCAD-addons/master/.gitmodules"
        )
        if not u:
            self.progressbar_show.emit(False)
            self.done.emit()
            self.stop = True
            return
        p = u.read()
        if sys.version_info.major >= 3 and isinstance(p, bytes):
            p = p.decode("utf-8")
        u.close()
        p = re.findall((r'(?m)\[submodule\s*"(?P<name>.*)"\]\s*'
                        r"path\s*=\s*(?P<path>.+)\s*"
                        r"url\s*=\s*(?P<url>https?://.*)"), p)
        basedir = FreeCAD.getUserAppDataDir()
        moddir = basedir + os.sep + "Mod"
        repos = []
        # querying official addons
        for name, path, url in p:
            self.info_label.emit(name)
            url = url.split(".git")[0]
            addondir = moddir + os.sep + name
            if os.path.exists(addondir) and os.listdir(addondir):
                # make sure the folder exists and it contains files!
                state = 1
            else:
                state = 0
            repos.append([name, url, state])
        # querying custom addons
        customaddons = (FreeCAD.ParamGet(
            "User parameter:BaseApp/Preferences/Addons").GetString(
                "CustomRepositories", "").split("\n"))
        for url in customaddons:
            if url:
                name = url.split("/")[-1]
                if name.lower().endswith(".git"):
                    name = name[:-4]
                addondir = moddir + os.sep + name
                if not os.path.exists(addondir):
                    state = 0
                else:
                    state = 1
                repos.append([name, url, state])
        if not repos:
            self.info_label.emit(
                translate("AddonsInstaller", "Unable to download addon list."))
        else:
            repos = sorted(repos, key=lambda s: s[0].lower())
            for repo in repos:
                self.addon_repo.emit(repo)
            self.info_label.emit(
                translate("AddonsInstaller", "Workbenches list was updated."))
        self.progressbar_show.emit(False)
        self.done.emit()
        self.stop = True
Ejemplo n.º 14
0
    def checkDependencies(self, baseurl):
        "checks if the repo contains a metadata.txt and check its contents"
        import FreeCADGui
        ok = True
        message = ""
        depsurl = baseurl.replace("github.com", "raw.githubusercontent.com")
        if not depsurl.endswith("/"):
            depsurl += "/"
        depsurl += "master/metadata.txt"
        try:
            mu = urlopen(depsurl)
        except urllib2.HTTPError:
            # no metadata.txt, we just continue without deps checking
            pass
        else:
            # metadata.txt found
            depsfile = mu.read()
            mu.close()

            # urllib2 gives us a bytelike object instead of a string. Have to consider that
            try:
                depsfile = depsfile.decode('utf-8')
            except AttributeError:
                pass

            deps = depsfile.split("\n")
            for l in deps:
                if l.startswith("workbenches="):
                    depswb = l.split("=")[1].split(",")
                    for wb in depswb:
                        if wb.strip():
                            if not wb.strip() in FreeCADGui.listWorkbenches(
                            ).keys():
                                if not wb.strip(
                                ) + "Workbench" in FreeCADGui.listWorkbenches(
                                ).keys():
                                    ok = False
                                    message += translate(
                                        "AddonsInstaller",
                                        "Missing workbench") + ": " + wb + ", "
                elif l.startswith("pylibs="):
                    depspy = l.split("=")[1].split(",")
                    for pl in depspy:
                        if pl.strip():
                            try:
                                __import__(pl.strip())
                            except:
                                ok = False
                                message += translate(
                                    "AddonsInstaller",
                                    "Missing python module") + ": " + pl + ", "
                elif l.startswith("optionalpylibs="):
                    opspy = l.split("=")[1].split(",")
                    for pl in opspy:
                        if pl.strip():
                            try:
                                __import__(pl.strip())
                            except:
                                message += translate(
                                    "AddonsInstaller",
                                    "Missing optional python module (doesn't prevent installing)"
                                ) + ": " + pl + ", "
        if message and (not ok):
            message = translate(
                "AddonsInstaller",
                "Some errors were found that prevent to install this workbench"
            ) + ": <b>" + message + "</b>. "
            message += translate(
                "AddonsInstaller",
                "Please install the missing components first.")
        return ok, message
Ejemplo n.º 15
0
def checkShapeFileLibrary():
    """Looks for and/or installs the ShapeFile library"""

    try:
        import shapefile
    except:
        url = "https://raw.githubusercontent.com/GeospatialPython/pyshp/master/shapefile.py"
        if FreeCAD.GuiUp:
            import addonmanager_utilities
            import FreeCADGui
            from PySide import QtGui
            reply = QtGui.QMessageBox.question(
                FreeCADGui.getMainWindow(),
                translate("Arch", "Shapefile module not found"),
                translate(
                    "Arch",
                    "The shapefile python library was not found on your system. Would you like to download it now from <a href=\"https://github.com/GeospatialPython/pyshp\">https://github.com/GeospatialPython/pyshp</a>? It will be placed in your macros folder."
                ), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
                QtGui.QMessageBox.No)
            if reply == QtGui.QMessageBox.Yes:
                u = addonmanager_utilities.urlopen(url)
                if not u:
                    FreeCAD.Console.PrintError(
                        translate("Arch", "Error: Unable to download from:") +
                        " " + url + "\n")
                    return False
                b = u.read()
                p = FreeCAD.ParamGet(
                    "User parameter:BaseApp/Preferences/Macro")
                fp = p.GetString(
                    "MacroPath",
                    os.path.join(FreeCAD.getUserAppDataDir(), "Macros"))
                fp = os.path.join(fp, "shapefile.py")
                f = pythonopen(fp, "wb")
                f.write(b)
                f.close()
                try:
                    import shapefile
                except:
                    FreeCAD.Console.PrintError(
                        translate(
                            "Arch",
                            "Could not download shapefile module. Aborting.") +
                        "\n")
                    return False
            else:
                FreeCAD.Console.PrintError(
                    translate("Arch",
                              "Shapefile module not downloaded. Aborting.") +
                    "\n")
                return False
        else:
            FreeCAD.Console.PrintError(
                translate("Arch", "Shapefile module not found. Aborting.") +
                "\n")
            FreeCAD.Console.PrintMessage(
                translate(
                    "Arch",
                    "The shapefile library can be downloaded from the following URL and installed in your macros folder:"
                ) + "\n")
            FreeCAD.Console.PrintMessage(url)
            return False
    return True
Ejemplo n.º 16
0
    def fill_details_from_wiki(self, url):
        code = ""
        u = urlopen(url)
        if u is None:
            FreeCAD.Console.PrintWarning(
                translate(
                    "AddonsInstaller",
                    f"Could not connect to {url} - check connection and proxy settings",
                ) + "\n")
            return
        p = u.read()
        if isinstance(p, bytes):
            p = p.decode("utf-8")
        u.close()
        # check if the macro page has its code hosted elsewhere, download if
        # needed
        if "rawcodeurl" in p:
            rawcodeurl = re.findall('rawcodeurl.*?href="(http.*?)">', p)
            if rawcodeurl:
                rawcodeurl = rawcodeurl[0]
                u2 = urlopen(rawcodeurl)
                if u2 is None:
                    FreeCAD.Console.PrintWarning(
                        translate(
                            "AddonsInstaller",
                            "Unable to open macro code URL {rawcodeurl}",
                        ) + "\n")
                    return
                response = ""
                block = 8192
                while True:
                    data = u2.read(block)
                    if not data:
                        break
                    if isinstance(data, bytes):
                        data = data.decode("utf-8")
                    response += data
                if response:
                    code = response
                u2.close()
        if not code:
            code = re.findall(r"<pre>(.*?)</pre>", p.replace("\n", "--endl--"))
            if code:
                # take the biggest code block
                code = sorted(code, key=len)[-1]
                code = code.replace("--endl--", "\n")
                # Clean HTML escape codes.
                code = unescape(code)
                code = code.replace(b"\xc2\xa0".decode("utf-8"), " ")
            else:
                FreeCAD.Console.PrintWarning(
                    translate("AddonsInstaller",
                              "Unable to fetch the code of this macro.") +
                    "\n")

        desc = re.findall(
            r"<td class=\"ctEven left macro-description\">(.*?)</td>",
            p.replace("\n", " "),
        )
        if desc:
            desc = desc[0]
        else:
            FreeCAD.Console.PrintWarning(
                translate(
                    "AddonsInstaller",
                    f"Unable to retrieve a description from the wiki for macro {self.name}",
                ) + "\n")
            desc = "No description available"
        self.desc = desc
        self.comment, _, _ = desc.partition(
            "<br")  # Up to the first line break
        self.comment = re.sub("<.*?>", "", self.comment)  # Strip any tags
        self.url = url
        if isinstance(code, list):
            flat_code = ""
            for chunk in code:
                flat_code += chunk
            code = flat_code
        self.code = code
        self.fill_details_from_code(self.code)
        if not self.author:
            self.author = self.parse_desc("Author: ")
        if not self.date:
            self.date = self.parse_desc("Last modified: ")
Ejemplo n.º 17
0
    def run(self):

        self.progressbar_show.emit(True)
        self.info_label.emit(translate("AddonsInstaller", "Retrieving description..."))
        if len(self.repos[self.idx]) == 4:
            desc = self.repos[self.idx][3]
        else:
            u = None
            url = self.repos[self.idx][1]
            self.info_label.emit(translate("AddonsInstaller", "Retrieving info from") + ' ' + str(url))
            desc = ""
            # get the README if possible
            readmeurl = utils.getReadmeUrl(url)
            if not readmeurl:
                print("Debug: README not found for",url)
            u = utils.urlopen(readmeurl)
            if not u:
                print("Debug: README not found at",readmeurl)
            if u:
                p = u.read()
                if sys.version_info.major >= 3 and isinstance(p, bytes):
                    p = p.decode("utf-8")
                u.close()
                readmeregex = utils.getReadmeRegex(url)
                if readmeregex:
                    readme = re.findall(readmeregex,p,flags=re.MULTILINE|re.DOTALL)
                    if readme:
                        desc += readme[0]
            if not desc:
                # fall back to the description text
                u = utils.urlopen(url)
                if not u:
                    self.progressbar_show.emit(False)
                    self.stop = True
                    return
                p = u.read()
                if sys.version_info.major >= 3 and isinstance(p, bytes):
                    p = p.decode("utf-8")
                u.close()
                descregex = utils.getDescRegex(url)
                if descregex:
                    desc = re.findall(descregex,p)
                    if desc:
                        desc = "<br/>"+desc[0]
            if not desc:
                desc = "Unable to retrieve addon description"
            self.repos[self.idx].append(desc)
            self.addon_repos.emit(self.repos)
        # Addon is installed so lets check if it has an update
        if self.repos[self.idx][2] == 1:
            upd = False
            # checking for updates
            if not NOGIT:
                try:
                    import git
                except:
                    pass
                else:
                    repo = self.repos[self.idx]
                    clonedir = FreeCAD.getUserAppDataDir() + os.sep + "Mod" + os.sep + repo[0]
                    if os.path.exists(clonedir):
                        if not os.path.exists(clonedir + os.sep + '.git'):
                            # Repair addon installed with raw download
                            bare_repo = git.Repo.clone_from(repo[1], clonedir + os.sep + '.git', bare=True)
                            try:
                                with bare_repo.config_writer() as cw:
                                    cw.set('core', 'bare', False)
                            except AttributeError:
                                FreeCAD.Console.PrintWarning(translate("AddonsInstaller", "Outdated GitPython detected, consider upgrading with pip.")+"\n")
                                cw = bare_repo.config_writer()
                                cw.set('core', 'bare', False)
                                del cw
                            repo = git.Repo(clonedir)
                            repo.head.reset('--hard')
                        gitrepo = git.Git(clonedir)
                        gitrepo.fetch()
                        if "git pull" in gitrepo.status():
                            upd = True
            # If there is an update pending, lets user know via the UI
            if upd:
                message = "<div style=\"width: 100%;text-align: center;background: #75AFFD;\"><br/><strong style=\"background: #397FF7;color: #FFFFFF;\">" + translate("AddonsInstaller", "An update is available for this addon.") 
                message += "</strong><br/></div><hr/>" + desc + '<br/><br/>Addon repository: <a href="' + self.repos[self.idx][1] + '">' + self.repos[self.idx][1] + '</a>'
                self.repos[self.idx][2] = 3 # mark as already installed AND already checked for updates AND update is available
            # If there isn't, indicate that this addon is already installed
            else:
                message = "<div style=\"width: 100%;text-align: center;background: #C1FEB2;\"><br/><strong style=\"background: #00B629;color: #FFFFFF;\">" + translate("AddonsInstaller", "This addon is already installed.") + "</strong><br/></div><hr/>" 
                message += desc + '<br/><br/>Addon repository: <a href="' + self.repos[self.idx][1] + '">' + self.repos[self.idx][1] + '</a>'
                self.repos[self.idx][2] = 2 # mark as already installed AND already checked for updates
            # Let the user know the install path for this addon
            message += '<br/>' + translate("AddonInstaller","Installed location")+": "+ FreeCAD.getUserAppDataDir() + os.sep + "Mod" + os.sep + self.repos[self.idx][0]
            self.addon_repos.emit(self.repos)
        elif self.repos[self.idx][2] == 2:
            message = "<div style=\"width: 100%;text-align: center;background: #C1FEB2;\"><br/><strong style=\"background: #00B629;color: #FFFFFF;\">" + translate("AddonsInstaller", "This addon is already installed.") + "</strong><br></div><hr/>"
            message += desc + '<br/><br/>Addon repository: <a href="' + self.repos[self.idx][1] + '">' + self.repos[self.idx][1] + '</a>'
            message += '<br/>' + translate("AddonInstaller","Installed location")+": "+ FreeCAD.getUserAppDataDir() + os.sep + "Mod" + os.sep + self.repos[self.idx][0]
        elif self.repos[self.idx][2] == 3:
            message = "<div style=\"width: 100%;text-align: center;background: #75AFFD;\"><br/><strong style=\"background: #397FF7;color: #FFFFFF;\">" + translate("AddonsInstaller", "An update is available for this addon.") 
            message += "</strong><br/></div><hr/>" + desc + '<br/><br/>Addon repository: <a href="' + self.repos[self.idx][1] + '">' + self.repos[self.idx][1] + '</a>'
            message += '<br/>' + translate("AddonInstaller","Installed location")+": "+ FreeCAD.getUserAppDataDir() + os.sep + "Mod" + os.sep + self.repos[self.idx][0]
        else:
            message = desc + '<br/><br/>Addon repository: <a href="' + self.repos[self.idx][1] + '">' + self.repos[self.idx][1] + '</a>'

        # If the Addon is obsolete, let the user know through the Addon UI
        if self.repos[self.idx][0] in OBSOLETE:
            message = " <div style=\"width: 100%; text-align:center; background: #FFB3B3;\"><strong style=\"color: #FFFFFF; background: #FF0000;\">"+translate("AddonsInstaller","This addon is marked as obsolete")+"</strong><br/><br/>"
            message += translate("AddonsInstaller","This usually means it is no longer maintained, and some more advanced addon in this list provides the same functionality.")+"<br/></div><hr/>" + desc

        # If the Addon is Python 2 only, let the user know through the Addon UI
        if self.repos[self.idx][0] in PY2ONLY:
            message = " <div style=\"width: 100%; text-align:center; background: #ffe9b3;\"><strong style=\"color: #FFFFFF; background: #ff8000;\">"+translate("AddonsInstaller","This addon is marked as Python 2 Only")+"</strong><br/><br/>"
            message += translate("AddonsInstaller","This workbench may no longer be maintained and installing it on a Python 3 system will more than likely result in errors at startup or while in use.")+"<br/></div><hr/>" + desc

        self.info_label.emit( message )
        self.progressbar_show.emit(False)
        self.mustLoadImages = True
        l = self.loadImages( message, self.repos[self.idx][1], self.repos[self.idx][0])
        if l:
            self.info_label.emit( l )
        self.stop = True
Ejemplo n.º 18
0
    def run(self):

        self.progressbar_show.emit(True)
        self.info_label.emit(
            translate("AddonsInstaller", "Retrieving description..."))
        if len(self.repos[self.idx]) == 4:
            desc = self.repos[self.idx][3]
        else:
            u = None
            url = self.repos[self.idx][1]
            self.info_label.emit(
                translate("AddonsInstaller", "Retrieving info from") + ' ' +
                str(url))
            desc = ""
            # get the README if possible
            readmeurl = utils.getReadmeUrl(url)
            if not readmeurl:
                print("Debug: README not found for", url)
            u = utils.urlopen(readmeurl)
            if not u:
                print("Debug: README not found at", readmeurl)
            if u:
                p = u.read()
                if sys.version_info.major >= 3 and isinstance(p, bytes):
                    p = p.decode("utf-8")
                u.close()
                readmeregex = utils.getReadmeRegex(url)
                if readmeregex:
                    readme = re.findall(readmeregex,
                                        p,
                                        flags=re.MULTILINE | re.DOTALL)
                    if readme:
                        desc += readme[0]
            if not desc:
                # fall back to the description text
                u = utils.urlopen(url)
                if not u:
                    self.progressbar_show.emit(False)
                    self.stop = True
                    return
                p = u.read()
                if sys.version_info.major >= 3 and isinstance(p, bytes):
                    p = p.decode("utf-8")
                u.close()
                descregex = utils.getDescRegex(url)
                if descregex:
                    desc = re.findall(descregex, p)
                    if desc:
                        desc = "<br/>" + desc[0]
            if not desc:
                desc = "Unable to retrieve addon description"
            self.repos[self.idx].append(desc)
            self.addon_repos.emit(self.repos)
        if self.repos[self.idx][2] == 1:
            upd = False
            # checking for updates
            if not NOGIT:
                try:
                    import git
                except:
                    pass
                else:
                    repo = self.repos[self.idx]
                    clonedir = FreeCAD.getUserAppDataDir(
                    ) + os.sep + "Mod" + os.sep + repo[0]
                    if os.path.exists(clonedir):
                        if not os.path.exists(clonedir + os.sep + '.git'):
                            # Repair addon installed with raw download
                            bare_repo = git.Repo.clone_from(repo[1],
                                                            clonedir + os.sep +
                                                            '.git',
                                                            bare=True)
                            try:
                                with bare_repo.config_writer() as cw:
                                    cw.set('core', 'bare', False)
                            except AttributeError:
                                FreeCAD.Console.PrintWarning(
                                    translate(
                                        "AddonsInstaller",
                                        "Outdated GitPython detected, consider upgrading with pip."
                                    ) + "\n")
                                cw = bare_repo.config_writer()
                                cw.set('core', 'bare', False)
                                del cw
                            repo = git.Repo(clonedir)
                            repo.head.reset('--hard')
                        gitrepo = git.Git(clonedir)
                        gitrepo.fetch()
                        if "git pull" in gitrepo.status():
                            upd = True
            if upd:
                message = "<strong style=\"background: #B65A00;\">" + translate(
                    "AddonsInstaller", "An update is available for this addon."
                ) + "</strong><br>" + desc + '<br/><br/>Addon repository: <a href="' + self.repos[
                    self.idx][1] + '">' + self.repos[self.idx][1] + '</a>'
            else:
                message = "<strong style=\"background: #00B629;\">" + translate(
                    "AddonsInstaller", "This addon is already installed."
                ) + "</strong><br>" + desc + '<br/><br/>Addon repository: <a href="' + self.repos[
                    self.idx][1] + '">' + self.repos[self.idx][1] + '</a>'
            self.repos[self.idx][
                2] = 2  # mark as already installed AND already checked for updates
            self.addon_repos.emit(self.repos)
        else:
            message = desc + '<br/><br/>Addon repository: <a href="' + self.repos[
                self.idx][1] + '">' + self.repos[self.idx][1] + '</a>'

        if self.repos[self.idx][0] in OBSOLETE:
            message = " <strong style=\"background: #FF0000;\">" + translate(
                "AddonsInstaller", "This addon is marked as obsolete"
            ) + "</strong><br/><br/>" + translate(
                "AddonsInstaller",
                "This usually means it is no longer maintained, and some more advanced addon in this list provides the same functionality."
            ) + "<br/><br/>" + message

        self.info_label.emit(message)
        self.progressbar_show.emit(False)
        l = self.loadImages(message, self.repos[self.idx][1],
                            self.repos[self.idx][0])
        if l:
            self.info_label.emit(l)
        self.stop = True
Ejemplo n.º 19
0
    def loadImages(self, message, url, wbName):
        "checks if the given page contains images and downloads them"

        # QTextBrowser cannot display online images. So we download them
        # here, and replace the image link in the html code with the
        # downloaded version

        imagepaths = re.findall("<img.*?src=\"(.*?)\"", message)
        if imagepaths:
            storedimages = []
            store = os.path.join(FreeCAD.getUserAppDataDir(), "AddonManager",
                                 "Images")
            if not os.path.exists(store):
                os.makedirs(store)
            for path in imagepaths:
                if not self.mustLoadImages:
                    return None
                origpath = path
                if "?" in path:
                    # remove everything after the ?
                    path = path.split("?")[0]
                if not path.startswith("http"):
                    path = utils.getserver(url) + path
                name = path.split("/")[-1]
                if name and path.startswith("http"):
                    storename = os.path.join(store, name)
                    if len(storename) >= 260:
                        remainChars = 259 - (len(store) + len(wbName) + 1)
                        storename = os.path.join(store,
                                                 wbName + name[-remainChars:])
                    if not os.path.exists(storename):
                        try:
                            u = utils.urlopen(path)
                            imagedata = u.read()
                            u.close()
                        except Exception:
                            print(
                                "AddonManager: Debug: Error retrieving image from",
                                path)
                        else:
                            try:
                                f = open(storename, "wb")
                            except OSError:
                                # ecryptfs (and probably not only ecryptfs) has lower length limit for path
                                storename = storename[-140:]
                                f = open(storename, "wb")
                            f.write(imagedata)
                            f.close()
                            # resize the image to 300x300px if needed
                            img = QtGui.QImage(storename)
                            if (img.width() > 300) or (img.height() > 300):
                                pix = QtGui.QPixmap()
                                pix = pix.fromImage(
                                    img.scaled(300, 300,
                                               QtCore.Qt.KeepAspectRatio,
                                               QtCore.Qt.FastTransformation))
                                pix.save(storename, "jpeg", 100)
                    message = message.replace(
                        "src=\"" + origpath,
                        "src=\"file:///" + storename.replace("\\", "/"))
            # print(message)
            return message
        return None
Ejemplo n.º 20
0
    def run(self):

        self.progressbar_show.emit(True)
        self.info_label.emit(
            translate("AddonsInstaller", "Retrieving description..."))
        if len(self.repos[self.idx]) == 4:
            desc = self.repos[self.idx][3]
        else:
            u = None
            url = self.repos[self.idx][1]
            self.info_label.emit(
                translate("AddonsInstaller", "Retrieving info from") + " " +
                str(url))
            desc = ""
            regex = utils.get_readme_regex(url)
            if regex:
                # extract readme from html via regex
                readmeurl = utils.get_readme_html_url(url)
                if not readmeurl:
                    print("Debug: README not found for", url)
                u = utils.urlopen(readmeurl)
                if not u:
                    print("Debug: README not found at", readmeurl)
                u = utils.urlopen(readmeurl)
                if u:
                    p = u.read()
                    if sys.version_info.major >= 3 and isinstance(p, bytes):
                        p = p.decode("utf-8")
                    u.close()
                    readme = re.findall(regex,
                                        p,
                                        flags=re.MULTILINE | re.DOTALL)
                    if readme:
                        desc = readme[0]
                else:
                    print("Debug: README not found at", readmeurl)
            else:
                # convert raw markdown using lib
                readmeurl = utils.get_readme_url(url)
                if not readmeurl:
                    print("Debug: README not found for", url)
                u = utils.urlopen(readmeurl)
                if u:
                    p = u.read()
                    if sys.version_info.major >= 3 and isinstance(p, bytes):
                        p = p.decode("utf-8")
                    u.close()
                    desc = utils.fix_relative_links(
                        p,
                        readmeurl.rsplit("/README.md")[0])
                    if NOMARKDOWN or not have_markdown:
                        desc = markdown.markdown(desc,
                                                 extensions=["md_in_html"])
                    else:
                        message = """
<div style="width: 100%; text-align:center;background: #91bbe0;">
    <strong style="color: #FFFFFF;">
"""
                        message += translate("AddonsInstaller",
                                             "Raw markdown displayed")
                        message += "</strong><br/><br/>"
                        message += translate(
                            "AddonsInstaller",
                            "Python Markdown library is missing.")
                        message += "<br/></div><hr/><pre>" + desc + "</pre>"
                        desc = message
                else:
                    print("Debug: README not found at", readmeurl)
            if desc == "":
                # fall back to the description text
                u = utils.urlopen(url)
                if not u:
                    self.progressbar_show.emit(False)
                    self.stop = True
                    return
                p = u.read()
                if sys.version_info.major >= 3 and isinstance(p, bytes):
                    p = p.decode("utf-8")
                u.close()
                descregex = utils.get_desc_regex(url)
                if descregex:
                    desc = re.findall(descregex, p)
                    if desc:
                        desc = desc[0]
            if not desc:
                desc = "Unable to retrieve addon description"
            self.repos[self.idx].append(desc)
            self.addon_repos.emit(self.repos)
        # Addon is installed so lets check if it has an update
        if self.repos[self.idx][2] == 1:
            upd = False
            # checking for updates
            if not NOGIT and have_git:
                repo = self.repos[self.idx]
                clonedir = FreeCAD.getUserAppDataDir(
                ) + os.sep + "Mod" + os.sep + repo[0]
                if os.path.exists(clonedir):
                    if not os.path.exists(clonedir + os.sep + ".git"):
                        # Repair addon installed with raw download
                        bare_repo = git.Repo.clone_from(repo[1],
                                                        clonedir + os.sep +
                                                        ".git",
                                                        bare=True)
                        try:
                            with bare_repo.config_writer() as cw:
                                cw.set("core", "bare", False)
                        except AttributeError:
                            FreeCAD.Console.PrintWarning(
                                translate(
                                    "AddonsInstaller",
                                    "Outdated GitPython detected, "
                                    "consider upgrading with pip.") + "\n")
                            cw = bare_repo.config_writer()
                            cw.set("core", "bare", False)
                            del cw
                        repo = git.Repo(clonedir)
                        repo.head.reset("--hard")
                    gitrepo = git.Git(clonedir)
                    gitrepo.fetch()
                    if "git pull" in gitrepo.status():
                        upd = True
            # If there is an update pending, lets user know via the UI
            if upd:
                message = """
<div style="width: 100%;text-align: center;background: #75AFFD;">
    <br/>
    <strong style="background: #397FF7;color: #FFFFFF;">
"""
                message += translate("AddonsInstaller",
                                     "An update is available for this addon.")
                message += "</strong><br/></div><hr/>" + desc + '<br/><br/>Addon repository: <a href="'
                message += self.repos[self.idx][1] + '">' + self.repos[
                    self.idx][1] + "</a>"
                # mark as already installed AND already checked for updates AND update is available
                self.repos[self.idx][2] = 3
            # If there isn't, indicate that this addon is already installed
            else:
                message = """
<div style="width: 100%;text-align: center;background: #C1FEB2;">
    <br/>
    <strong style="background: #00B629;color: #FFFFFF;">
"""
                message += translate("AddonsInstaller",
                                     "This addon is already installed.")
                message += "</strong><br/></div><hr/>" + desc
                message += '<br/><br/>Addon repository: <a href="'
                message += self.repos[self.idx][1] + '">' + self.repos[
                    self.idx][1] + "</a>"
                self.repos[self.idx][
                    2] = 2  # mark as already installed AND already checked for updates
            # Let the user know the install path for this addon
            message += "<br/>" + translate("AddonInstaller",
                                           "Installed location") + ": "
            message += FreeCAD.getUserAppDataDir(
            ) + os.sep + "Mod" + os.sep + self.repos[self.idx][0]
            self.addon_repos.emit(self.repos)
        elif self.repos[self.idx][2] == 2:
            message = """
<div style="width: 100%;text-align: center;background: #C1FEB2;">
    <br/>
    <strong style="background: #00B629;color: #FFFFFF;">
"""
            message += translate("AddonsInstaller",
                                 "This addon is already installed.")
            message += "</strong><br></div><hr/>" + desc
            message += '<br/><br/>Addon repository: <a href="'
            message += self.repos[self.idx][1] + '">' + self.repos[
                self.idx][1] + "</a>"
            message += "<br/>" + translate("AddonInstaller",
                                           "Installed location") + ": "
            message += FreeCAD.getUserAppDataDir(
            ) + os.sep + "Mod" + os.sep + self.repos[self.idx][0]
        elif self.repos[self.idx][2] == 3:
            message = """
<div style="width: 100%;text-align: center;background: #75AFFD;">
    <br/>
    <strong style="background: #397FF7;color: #FFFFFF;">
"""
            message += translate("AddonsInstaller",
                                 "An update is available for this addon.")
            message += "</strong><br/></div><hr/>" + desc + '<br/><br/>Addon repository: <a href="'
            message += self.repos[self.idx][1] + '">' + self.repos[
                self.idx][1] + "</a>"
            message += "<br/>" + translate("AddonInstaller",
                                           "Installed location") + ": "
            message += FreeCAD.getUserAppDataDir(
            ) + os.sep + "Mod" + os.sep + self.repos[self.idx][0]
        else:
            message = desc + '<br/><br/>Addon repository: <a href="'
            message += self.repos[self.idx][1] + '">' + self.repos[
                self.idx][1] + '</a>'

        # If the Addon is obsolete, let the user know through the Addon UI
        if self.repos[self.idx][0] in obsolete:
            message = """
<div style="width: 100%; text-align:center; background: #FFB3B3;">
    <strong style="color: #FFFFFF; background: #FF0000;">
"""
            message += translate(
                "AddonsInstaller",
                "This addon is marked as obsolete") + "</strong><br/><br/>"
            message += translate(
                "AddonsInstaller",
                "This usually means it is no longer maintained, "
                "and some more advanced addon in this list "
                "provides the same functionality.") + "<br/></div><hr/>" + desc

        # If the Addon is Python 2 only, let the user know through the Addon UI
        if self.repos[self.idx][0] in py2only:
            message = """
<div style="width: 100%; text-align:center; background: #ffe9b3;">
    <strong style="color: #FFFFFF; background: #ff8000;">
"""
            message += translate("AddonsInstaller",
                                 "This addon is marked as Python 2 Only"
                                 ) + "</strong><br/><br/>"
            message += translate(
                "AddonsInstaller",
                "This workbench may no longer be maintained and "
                "installing it on a Python 3 system will more than "
                "likely result in errors at startup or while in use.")
            message += "<br/></div><hr/>" + desc

        self.info_label.emit(message)
        self.progressbar_show.emit(False)
        self.mustLoadImages = True
        label = self.loadImages(message, self.repos[self.idx][1],
                                self.repos[self.idx][0])
        if label:
            self.info_label.emit(label)
        self.stop = True
Ejemplo n.º 21
0
    def run(self):

        "populates the list of addons"

        self.progressbar_show.emit(True)
        u = utils.urlopen("https://github.com/FreeCAD/FreeCAD-addons")
        if not u:
            self.progressbar_show.emit(False)
            self.done.emit()
            self.stop = True
            return
        p = u.read()
        if sys.version_info.major >= 3 and isinstance(p, bytes):
            p = p.decode("utf-8")
        u.close()
        p = p.replace("\n", " ")
        p = re.findall("octicon-file-submodule(.*?)message", p)
        basedir = FreeCAD.getUserAppDataDir()
        moddir = basedir + os.sep + "Mod"
        repos = []
        # querying official addons
        for l in p:
            #name = re.findall("data-skip-pjax=\"true\">(.*?)<",l)[0]
            res = re.findall("title=\"(.*?) @", l)
            if res:
                name = res[0]
            else:
                print("AddonMananger: Debug: couldn't find title in", l)
                continue
            self.info_label.emit(name)
            #url = re.findall("title=\"(.*?) @",l)[0]
            url = utils.getRepoUrl(l)
            if url:
                addondir = moddir + os.sep + name
                #print ("found:",name," at ",url)
                if os.path.exists(addondir) and os.listdir(addondir):
                    # make sure the folder exists and it contains files!
                    state = 1
                else:
                    state = 0
                repos.append([name, url, state])
        # querying custom addons
        customaddons = FreeCAD.ParamGet(
            "User parameter:BaseApp/Preferences/Addons").GetString(
                "CustomRepositories", "").split("\n")
        for url in customaddons:
            if url:
                name = url.split("/")[-1]
                if name.lower().endswith(".git"):
                    name = name[:-4]
                addondir = moddir + os.sep + name
                if not os.path.exists(addondir):
                    state = 0
                else:
                    state = 1
                repos.append([name, url, state])
        if not repos:
            self.info_label.emit(
                translate("AddonsInstaller", "Unable to download addon list."))
        else:
            repos = sorted(repos, key=lambda s: s[0].lower())
            for repo in repos:
                self.addon_repo.emit(repo)
            self.info_label.emit(
                translate("AddonsInstaller", "Workbenches list was updated."))
        git_exe = utils.checkGitBinary()
        import platform
        try:
            out = os.popen(git_exe + ' --version', 'r')
        except:
            if platform.system() == 'Windows':
                FreeCAD.Console.PrintLog(
                    translate(
                        "AddonsInstaller",
                        "The Git executable has not been found in the path.") +
                    "\n")
            else:
                FreeCAD.Console.PrintLog(
                    translate("AddonsInstaller",
                              "The Git binary has not been found.") + "\n")
        from distutils.version import StrictVersion
        if 'out' in locals():
            out_string = out.read()
            out.close()
            result = re.search('(\d+\.\d+\.\d+)', out_string)
            git_version = StrictVersion(result.group(1))
            if platform.system() == 'Windows':
                FreeCAD.Console.PrintLog(
                    translate("AddonsInstaller",
                              "Found Git executable version ") +
                    str(git_version) + "\n")
            else:
                FreeCAD.Console.PrintLog(
                    translate("AddonsInstaller", "Found Git binary version ") +
                    str(git_version) + "\n")
        else:
            git_version = StrictVersion("0.0.0")
        self.progressbar_show.emit(False)
        self.done.emit()
        self.stop = True