diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 67aa53a8d2..9e99e96283 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -117,6 +117,7 @@ #include "ExpressionParser.h" #include "Transactions.h" #include +#include #include #include "Link.h" @@ -307,6 +308,7 @@ Application::Application(std::map &mConfig) Base::Interpreter().addType(&Base::TypePy ::Type,pBaseModule,"TypeId"); Base::Interpreter().addType(&App::MaterialPy::Type, pAppModule, "Material"); + Base::Interpreter().addType(&App::MetadataPy::Type, pAppModule, "Metadata"); // Add document types Base::Interpreter().addType(&App::PropertyContainerPy::Type, pAppModule, "PropertyContainer"); diff --git a/src/App/Metadata.cpp b/src/App/Metadata.cpp index 31a8853c5a..ce2f5ec6df 100644 --- a/src/App/Metadata.cpp +++ b/src/App/Metadata.cpp @@ -182,6 +182,11 @@ std::string Metadata::classname() const return _classname; } +boost::filesystem::path App::Metadata::subdirectory() const +{ + return _subdirectory; +} + std::vector Metadata::file() const { return _file; @@ -285,6 +290,11 @@ void Metadata::setClassname(const std::string& name) _classname = name; } +void App::Metadata::setSubdirectory(const boost::filesystem::path& path) +{ + _subdirectory = path; +} + void Metadata::addFile(const fs::path& path) { _file.push_back(path); @@ -482,6 +492,9 @@ void Metadata::appendToElement(DOMElement* root) const case Meta::UrlType::documentation: typeAsString = "documentation"; break; } addAttribute(element, "type", typeAsString); + if (url.type == Meta::UrlType::repository) { + addAttribute(element, "branch", url.branch); + } } } @@ -507,6 +520,8 @@ void Metadata::appendToElement(DOMElement* root) const appendSimpleXMLNode(root, "classname", _classname); + appendSimpleXMLNode(root, "subdirectory", _subdirectory.string()); + for (const auto& file : _file) appendSimpleXMLNode(root, "file", file.string()); @@ -572,6 +587,8 @@ void Metadata::parseVersion1(const DOMNode* startNode) _file.emplace_back(StrXUTF8(element->getTextContent()).str); else if (tagString == "classname") _classname = StrXUTF8(element->getTextContent()).str; + else if (tagString == "subdirectory") + _subdirectory = StrXUTF8(element->getTextContent()).str; else if (tagString == "icon") _icon = fs::path(StrXUTF8(element->getTextContent()).str); else if (tagString == "content") @@ -652,6 +669,10 @@ Meta::Url::Url(const XERCES_CPP_NAMESPACE::DOMElement* e) type = UrlType::readme; else if (typeAttribute == "documentation") type = UrlType::documentation; + + if (type == UrlType::repository) + branch = StrXUTF8(e->getAttribute(XUTF8Str("branch").unicodeForm())).str; + location = StrXUTF8(e->getTextContent()).str; } diff --git a/src/App/Metadata.h b/src/App/Metadata.h index 787dbf6ce9..992369bce1 100644 --- a/src/App/Metadata.h +++ b/src/App/Metadata.h @@ -77,7 +77,7 @@ namespace App { /** * \struct Url - * \brief A URL, including type information (e.g. website, repository, or bugtracker, in package.xml v3) + * \brief A URL, including type information (e.g. website, repository, or bugtracker, in package.xml) */ struct AppExport Url { Url() = default; @@ -85,6 +85,7 @@ namespace App { explicit Url(const XERCES_CPP_NAMESPACE::DOMElement* e); std::string location; //< The actual URL, including protocol UrlType type; //< What kind of URL this is + std::string branch; //< If it's a repository, which branch to use }; /** @@ -194,6 +195,7 @@ namespace App { std::vector tag() const; //< Zero or more text tags related to this package. boost::filesystem::path icon() const; //< Path to an icon file. std::string classname() const; //< Recognized for convenience -- generally only used by Workbenches. + boost::filesystem::path subdirectory() const; //< Optional, override the default subdirectory name for this item. std::vector file() const; //< Arbitrary files associated with this package or content item. Meta::Version freecadmin() const; //< The minimum FreeCAD version. Meta::Version freecadmax() const; //< The maximum FreeCAD version. @@ -246,6 +248,7 @@ namespace App { void addTag(const std::string& tag); void setIcon(const boost::filesystem::path& path); void setClassname(const std::string& name); + void setSubdirectory(const boost::filesystem::path& path); void addFile(const boost::filesystem::path& path); void addContentItem(const std::string& tag, const Metadata& item); void setFreeCADMin(const Meta::Version& version); @@ -277,6 +280,7 @@ namespace App { std::vector _tag; boost::filesystem::path _icon; std::string _classname; + boost::filesystem::path _subdirectory; std::vector _file; Meta::Version _freecadmin; Meta::Version _freecadmax; diff --git a/src/App/MetadataPy.xml b/src/App/MetadataPy.xml index 8b211c59a0..3b9f84417b 100644 --- a/src/App/MetadataPy.xml +++ b/src/App/MetadataPy.xml @@ -56,7 +56,7 @@ - + List of URLs as objects with 'location' and 'urltype' string attributes, where urltype is one of: @@ -67,7 +67,7 @@ * documentation - + @@ -120,6 +120,12 @@ + + + String: the name of the subdirectory this content item is located in. If empty, the item is in a directory named the same as the content item. + + + A list of files associated with this item -- the meaning of each file is implementation-defined diff --git a/src/App/MetadataPyImp.cpp b/src/App/MetadataPyImp.cpp index 32a83a26a7..c96a1b93f5 100644 --- a/src/App/MetadataPyImp.cpp +++ b/src/App/MetadataPyImp.cpp @@ -49,10 +49,16 @@ PyObject* MetadataPy::PyMake(struct _typeobject*, PyObject* args, PyObject*) // { // create a new instance of MetadataPy and the Twin object const char* filename; - if (!PyArg_ParseTuple(args, "s!", &filename)) + if (!PyArg_ParseTuple(args, "s", &filename)) return nullptr; - - return new MetadataPy(new Metadata(filename)); + try { + auto md = new Metadata(filename); + return new MetadataPy(md); + } + catch (...) { + PyErr_SetString(Base::BaseExceptionFreeCADError, "Failed to create Metadata object"); + return nullptr; + } } // constructor method @@ -65,7 +71,7 @@ int MetadataPy::PyInit(PyObject* args, PyObject* /*kwd*/) // Main class constructor -- takes a file path, loads the metadata from it PyErr_Clear(); const char* file; - if (PyArg_ParseTuple(args, "s!", &file)) { + if (PyArg_ParseTuple(args, "s", &file)) { App::Metadata* a = new Metadata(file); *(getMetadataPtr()) = *a; return 0; @@ -104,9 +110,9 @@ Py::Object MetadataPy::getMaintainer(void) const auto maintainers = getMetadataPtr()->maintainer(); Py::List pyMaintainers; for (const auto& m : maintainers) { - Py::Object pyMaintainer; - pyMaintainer.setAttr("name", Py::String(m.name)); - pyMaintainer.setAttr("email", Py::String(m.email)); + Py::Dict pyMaintainer; + pyMaintainer["name"] = Py::String(m.name); + pyMaintainer["email"] = Py::String(m.email); pyMaintainers.append(pyMaintainer); } return pyMaintainers; @@ -117,9 +123,9 @@ Py::Object MetadataPy::getAuthor(void) const auto authors = getMetadataPtr()->author(); Py::List pyAuthors; for (const auto& a : authors) { - Py::Object pyAuthor; - pyAuthor.setAttr("name", Py::String(a.name)); - pyAuthor.setAttr("email", Py::String(a.email)); + Py::Dict pyAuthor; + pyAuthor["name"] = Py::String(a.name); + pyAuthor["email"] = Py::String(a.email); pyAuthors.append(pyAuthor); } return pyAuthors; @@ -130,28 +136,30 @@ Py::Object MetadataPy::getLicense(void) const auto licenses = getMetadataPtr()->license(); Py::List pyLicenses; for (const auto& lic : licenses) { - Py::Object pyLicense; - pyLicense.setAttr("name", Py::String(lic.name)); - pyLicense.setAttr("file", Py::String(lic.file.string())); + Py::Dict pyLicense; + pyLicense["name"] = Py::String(lic.name); + pyLicense["file"] = Py::String(lic.file.string()); pyLicenses.append(pyLicense); } return pyLicenses; } -Py::Object MetadataPy::getUrl(void) const +Py::Object MetadataPy::getUrls(void) const { auto urls = getMetadataPtr()->url (); Py::List pyUrls; for (const auto& url : urls) { - Py::Object pyUrl; - pyUrl.setAttr("location", Py::String(url.location)); + Py::Dict pyUrl; + pyUrl["location"] = Py::String(url.location); switch (url.type) { - case Meta::UrlType::website: pyUrl.setAttr("type", Py::String("website")); break; - case Meta::UrlType::repository: pyUrl.setAttr("type", Py::String("repository")); break; - case Meta::UrlType::bugtracker: pyUrl.setAttr("type", Py::String("bugtracker")); break; - case Meta::UrlType::readme: pyUrl.setAttr("type", Py::String("readme")); break; - case Meta::UrlType::documentation: pyUrl.setAttr("type", Py::String("documentation")); break; + case Meta::UrlType::website: pyUrl["type"] = Py::String("website"); break; + case Meta::UrlType::repository: pyUrl["type"] = Py::String("repository"); break; + case Meta::UrlType::bugtracker: pyUrl["type"] = Py::String("bugtracker"); break; + case Meta::UrlType::readme: pyUrl["type"] = Py::String("readme"); break; + case Meta::UrlType::documentation: pyUrl["type"] = Py::String("documentation"); break; } + if (url.type == Meta::UrlType::repository) + pyUrl["branch"] = Py::String(url.branch); pyUrls.append(pyUrl); } return pyUrls; @@ -159,14 +167,14 @@ Py::Object MetadataPy::getUrl(void) const Py::Object dependencyToPyObject(const Meta::Dependency& d) { - Py::Object pyDependency; - pyDependency.setAttr("package",Py::String(d.package)); - pyDependency.setAttr("version_lt", Py::String(d.version_lt)); - pyDependency.setAttr("version_lte", Py::String(d.version_lte)); - pyDependency.setAttr("version_eq", Py::String(d.version_eq)); - pyDependency.setAttr("version_gt", Py::String(d.version_gt)); - pyDependency.setAttr("version_gte", Py::String(d.version_gte)); - pyDependency.setAttr("condition", Py::String(d.condition)); + Py::Dict pyDependency; + pyDependency["package"] = Py::String(d.package); + pyDependency["version_lt"] = Py::String(d.version_lt); + pyDependency["version_lte"] = Py::String(d.version_lte); + pyDependency["version_eq"] = Py::String(d.version_eq); + pyDependency["version_gt"] = Py::String(d.version_gt); + pyDependency["version_gte"] = Py::String(d.version_gte); + pyDependency["condition"] = Py::String(d.condition); return pyDependency; } @@ -222,6 +230,11 @@ Py::Object MetadataPy::getClassname(void) const return Py::String(getMetadataPtr()->classname()); } +Py::Object MetadataPy::getSubdirectory(void) const +{ + return Py::String(getMetadataPtr()->subdirectory().string()); +} + Py::Object MetadataPy::getFile(void) const { auto files = getMetadataPtr()->file(); @@ -262,13 +275,13 @@ PyObject* MetadataPy::getGenericMetadata(PyObject* args) auto gm = (*getMetadataPtr())[name]; auto pyGenericMetadata = new Py::List; for (const auto& item : gm) { - Py::Object pyItem; - pyItem.setAttr("contents", Py::String(item.contents)); + Py::Dict pyItem; + pyItem["contents"] = Py::String(item.contents); Py::Dict pyAttributes; for (const auto& attribute : item.attributes) { pyAttributes[attribute.first] = Py::String(attribute.second); } - pyItem.setAttr("attributes", pyAttributes); + pyItem["attributes"] = pyAttributes; pyGenericMetadata->append(pyItem); } return pyGenericMetadata->ptr();