27#include <simgear/misc/sg_dir.hxx>
28#include <simgear/misc/sg_path.hxx>
29#include <simgear/misc/strutils.hxx>
30#include <simgear/nasal/cppbind/Ghost.hxx>
31#include <simgear/nasal/cppbind/NasalHash.hxx>
32#include <simgear/nasal/naref.h>
33#include <simgear/props/props.hxx>
34#include <simgear/props/props_io.hxx>
47namespace strutils = simgear::strutils;
65 _detail(std::move(detail))
91 std::string minFGVersionRequired, std::string maxFGVersionRequired,
92 SGPropertyNode* addonNode)
96 _basePath(std::move(basePath)),
97 _storagePath(
globals->get_fg_home() / (
"Export/Addons/" + _id)),
98 _minFGVersionRequired(std::move(minFGVersionRequired)),
99 _maxFGVersionRequired(std::move(maxFGVersionRequired)),
100 _addonNode(addonNode)
102 if (_minFGVersionRequired.empty()) {
104 _minFGVersionRequired =
"2017.4.0";
107 if (_maxFGVersionRequired.empty()) {
108 _maxFGVersionRequired =
"none";
119{ _name = addonName; }
127 _version.reset(ptr_traits::makeStrongRef(addonVersion));
134{ _authors = addonAuthors; }
137{
return _maintainers; }
140{ _maintainers = addonMaintainers; }
143{
return _shortDescription; }
146{ _shortDescription = addonShortDescription; }
149{
return _longDescription; }
152{ _longDescription = addonLongDescription; }
155{
return _licenseDesignation; }
158{ _licenseDesignation = addonLicenseDesignation; }
161{
return _licenseFile; }
164{ _licenseFile = addonLicenseFile; }
167{
return _licenseUrl; }
170{ _licenseUrl = addonLicenseUrl; }
176{ _tags = addonTags; }
182{ _basePath = addonBasePath; }
185{
return _storagePath; }
189 if (_storagePath.exists()) {
190 if (!_storagePath.isDir()) {
192 "Unable to create add-on storage directory because the entry already "
193 "exists, but is not a directory: '" + _storagePath.utf8Str() +
"'";
198 SG_LOG(SG_GENERAL, SG_POPUP, msg);
202 const SGPath authorizedPath = SGPath(_storagePath).validate(
204 if (authorizedPath.isNull()) {
206 "Unable to create add-on storage directory because of the FlightGear "
207 "security policy (refused by SGPath::validate()): '" +
208 _storagePath.utf8Str() +
"'";
209 SG_LOG(SG_GENERAL, SG_POPUP, msg);
212 simgear::Dir(authorizedPath).create(0777);
228 if (strutils::starts_with(relativePath,
"/")) {
230 "addon-specific resource path '" + relativePath +
"' shouldn't start "
234 return "[addon=" +
getId() +
"]" + relativePath;
238{
return _minFGVersionRequired; }
241{ _minFGVersionRequired = minFGVersionRequired; }
244{
return _maxFGVersionRequired; }
248 if (maxFGVersionRequired.empty()) {
249 _maxFGVersionRequired =
"none";
251 _maxFGVersionRequired = maxFGVersionRequired;
259{ _homePage = addonHomePage; }
262{
return _downloadUrl; }
265{ _downloadUrl = addonDownloadUrl; }
268{
return _supportUrl; }
271{ _supportUrl = addonSupportUrl; }
274{
return _codeRepositoryUrl; }
277{ _codeRepositoryUrl = addonCodeRepositoryUrl; }
280{
return _triggerProperty; }
283{ _triggerProperty = addonTriggerProperty; }
286{
return _addonNode; }
289{ _addonNode = SGPropertyNode_ptr(addonNode); }
299 return { _addonNode->getChild(
"loaded", 0, 1) };
303{
return _loadSequenceNumber; }
306{ _loadSequenceNumber = num; }
310 std::multimap<UrlType, QualifiedUrl> res;
312 auto appendIfNonEmpty = [&res](
UrlType type,
string url,
string detail =
"") {
314 res.emplace(type,
QualifiedUrl(type, std::move(url), std::move(detail)));
318 for (
const auto&
author: _authors) {
322 for (
const auto& maint: _maintainers) {
336{
return _menubarNodes; }
339{ _menubarNodes = menubarNodes; }
343 SGPropertyNode* menuRootNode =
fgGetNode(
"/sim/menubar/default",
true);
346 SGPropertyNode* childNode = menuRootNode->addChild(
"menu");
347 ::copyProperties(node.ptr(), childNode);
353 std::ostringstream oss;
354 oss <<
"addon '" << _id <<
"' (version = " << *_version
355 <<
", base path = '" << _basePath.utf8Str()
356 <<
"', minFGVersionRequired = '" << _minFGVersionRequired
357 <<
"', maxFGVersionRequired = '" << _maxFGVersionRequired <<
"')";
363SGPath Addon::getMetadataFile(
const SGPath& addonPath)
368SGPath Addon::getMetadataFile()
const
379 Addon addon{std::move(metadata.
id), std::move(metadata.
version), addonPath,
382 addon.setName(std::move(metadata.
name));
383 addon.setAuthors(std::move(metadata.
authors));
384 addon.setMaintainers(std::move(metadata.
maintainers));
388 addon.setLicenseFile(std::move(metadata.
licenseFile));
389 addon.setLicenseUrl(std::move(metadata.
licenseUrl));
390 addon.setTags(std::move(metadata.
tags));
391 addon.setHomePage(std::move(metadata.
homePage));
392 addon.setDownloadUrl(std::move(metadata.
downloadUrl));
393 addon.setSupportUrl(std::move(metadata.
supportUrl));
396 SGPath menuFile = addonPath /
"addon-menubar-items.xml";
398 if (menuFile.exists()) {
399 addon.setMenubarNodes(readMenubarItems(menuFile));
406std::vector<SGPropertyNode_ptr>
407Addon::readMenubarItems(
const SGPath& menuFile)
409 SGPropertyNode rootNode;
412 readProperties(menuFile, &rootNode);
413 }
catch (
const sg_exception &e) {
414 throw errors::error_loading_menubar_items_file(
415 "unable to load add-on menu bar items from file '" +
416 menuFile.utf8Str() +
"': " + e.getFormattedMessage());
420 SGPropertyNode *metaNode = rootNode.getChild(
"meta");
421 if (metaNode ==
nullptr) {
422 throw errors::error_loading_menubar_items_file(
423 "no /meta node found in add-on menu bar items file '" +
424 menuFile.utf8Str() +
"'");
428 SGPropertyNode *fileTypeNode = metaNode->getChild(
"file-type");
429 if (fileTypeNode ==
nullptr) {
430 throw errors::error_loading_menubar_items_file(
431 "no /meta/file-type node found in add-on menu bar items file '" +
432 menuFile.utf8Str() +
"'");
435 string fileType = fileTypeNode->getStringValue();
436 if (fileType !=
"FlightGear add-on menu bar items") {
437 throw errors::error_loading_menubar_items_file(
438 "Invalid /meta/file-type value for add-on menu bar items file '" +
439 menuFile.utf8Str() +
"': '" + fileType +
"' "
440 "(expected 'FlightGear add-on menu bar items')");
444 SGPropertyNode *fmtVersionNode = metaNode->getChild(
"format-version");
445 if (fmtVersionNode ==
nullptr) {
446 throw errors::error_loading_menubar_items_file(
447 "no /meta/format-version node found in add-on menu bar items file '" +
448 menuFile.utf8Str() +
"'");
451 int formatVersion = fmtVersionNode->getIntValue();
452 if (formatVersion != 1) {
453 throw errors::error_loading_menubar_items_file(
454 "unknown format version in add-on menu bar items file '" +
455 menuFile.utf8Str() +
"': " + std::to_string(formatVersion));
458 SG_LOG(SG_GENERAL, SG_DEBUG,
459 "Loaded add-on menu bar items from '" << menuFile.utf8Str() +
"'");
461 SGPropertyNode *menubarItemsNode = rootNode.getChild(
"menubar-items");
462 std::vector<SGPropertyNode_ptr> res;
464 if (menubarItemsNode !=
nullptr) {
465 res = menubarItemsNode->getChildren(
"menu");
482 nasal::Ghost<AddonRef>::init(
"addons.Addon")
511 return os << addon.
str();
naRef wrappedPropsNode(SGPropertyNode *aProps)
create Nasal props.Node for an SGPropertyNode* This is the actual ghost, wrapped in a Nasal sugar cla...
void retranslate()
update string values (description, etc) based on the active locale
SGPropertyNode_ptr getLoadedFlagNode() const
void setLicenseFile(const SGPath &addonLicenseFile)
naRef getAddonPropsNode() const
void setLongDescription(const std::string &addonLongDescription)
void setSupportUrl(const std::string &addonSupportUrl)
void setCodeRepositoryUrl(const std::string &addonCodeRepositoryUrl)
void setBasePath(const SGPath &addonBasePath)
void setLicenseUrl(const std::string &addonLicenseUrl)
Addon(std::string id, AddonVersion version=AddonVersion(), SGPath basePath=SGPath(), std::string minFGVersionRequired="", std::string maxFGVersionRequired="", SGPropertyNode *addonNode=nullptr)
std::string getMaxFGVersionRequired() const
void setLoadSequenceNumber(int num)
void setMaintainers(const std::vector< MaintainerRef > &addonMaintainers)
std::string getSupportUrl() const
std::string getShortDescription() const
std::string getMinFGVersionRequired() const
void setMinFGVersionRequired(const std::string &minFGVersionRequired)
void setAddonNode(SGPropertyNode *addonNode)
void setTriggerProperty(const std::string &addonTriggerProperty)
int getLoadSequenceNumber() const
void setName(const std::string &addonName)
SGPath getStoragePath() const
void setMaxFGVersionRequired(const std::string &maxFGVersionRequired)
SGPath getBasePath() const
std::multimap< UrlType, QualifiedUrl > getUrls() const
void setDownloadUrl(const std::string &addonDownloadUrl)
static Addon fromAddonDir(const SGPath &addonPath)
std::string getTriggerProperty() const
std::string getHomePage() const
SGPath getLicenseFile() const
void setVersion(const AddonVersion &addonVersion)
void addToFGMenubar() const
std::string getDownloadUrl() const
AddonVersionRef getVersion() const
std::vector< AuthorRef > getAuthors() const
std::vector< MaintainerRef > getMaintainers() const
std::vector< SGPropertyNode_ptr > getMenubarNodes() const
std::string getName() const
std::string getLicenseUrl() const
std::string getId() const
std::string getLongDescription() const
void setAuthors(const std::vector< AuthorRef > &addonAuthors)
SGPropertyNode_ptr getAddonNode() const
static void setupGhost(nasal::Hash &addonsModule)
std::string resourcePath(const std::string &relativePath) const
void setHomePage(const std::string &addonHomePage)
void setTags(const std::vector< std::string > &addonTags)
void setLicenseDesignation(const std::string &addonLicenseDesignation)
SGPath createStorageDir() const
std::string getLicenseDesignation() const
std::string getCodeRepositoryUrl() const
std::vector< std::string > getTags() const
void setShortDescription(const std::string &addonShortDescription)
void setMenubarNodes(const std::vector< SGPropertyNode_ptr > &menubarNodes)
std::string getUrl() const
QualifiedUrl(UrlType type, std::string url, std::string detail="")
void setUrl(const std::string &url)
void setDetail(const std::string &detail)
void setType(UrlType type)
std::string getDetail() const
std::ostream & operator<<(std::ostream &os, const Addon &addon)
SGSharedPtr< AddonVersion > AddonVersionRef
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.