mirror of
https://github.com/overte-org/overte.git
synced 2025-07-23 13:04:07 +02:00
Merge pull request #7543 from sethalves/fix-scriptname-case
try again on local-scripts
This commit is contained in:
commit
0f9a6bb37b
5 changed files with 43 additions and 45 deletions
|
@ -220,7 +220,7 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QUrl url = expandScriptUrl(normalizeScriptURL(scriptURL));
|
QUrl url = expandScriptUrl(scriptURL);
|
||||||
_fileNameString = url.toString();
|
_fileNameString = url.toString();
|
||||||
_isReloading = reload;
|
_isReloading = reload;
|
||||||
|
|
||||||
|
@ -847,7 +847,7 @@ QUrl ScriptEngine::resolvePath(const QString& include) const {
|
||||||
QUrl url(include);
|
QUrl url(include);
|
||||||
// first lets check to see if it's already a full URL
|
// first lets check to see if it's already a full URL
|
||||||
if (!url.scheme().isEmpty()) {
|
if (!url.scheme().isEmpty()) {
|
||||||
return expandScriptUrl(normalizeScriptURL(url));
|
return expandScriptUrl(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
// we apparently weren't a fully qualified url, so, let's assume we're relative
|
// we apparently weren't a fully qualified url, so, let's assume we're relative
|
||||||
|
@ -864,7 +864,7 @@ QUrl ScriptEngine::resolvePath(const QString& include) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
// at this point we should have a legitimate fully qualified URL for our parent
|
// at this point we should have a legitimate fully qualified URL for our parent
|
||||||
url = expandScriptUrl(normalizeScriptURL(parentURL.resolved(url)));
|
url = expandScriptUrl(parentURL.resolved(url));
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,13 +47,6 @@ QUrl normalizeScriptURL(const QUrl& rawScriptURL) {
|
||||||
QUrl fullNormal = rawScriptURL;
|
QUrl fullNormal = rawScriptURL;
|
||||||
QUrl defaultScriptLoc = defaultScriptsLocation();
|
QUrl defaultScriptLoc = defaultScriptsLocation();
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
|
||||||
#else
|
|
||||||
// Force lowercase on file scripts because of drive letter weirdness.
|
|
||||||
if (rawScriptURL.isLocalFile()) {
|
|
||||||
fullNormal.setPath(fullNormal.path().toLower());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// if this url is something "beneath" the default script url, replace the local path with ~
|
// if this url is something "beneath" the default script url, replace the local path with ~
|
||||||
if (fullNormal.scheme() == defaultScriptLoc.scheme() &&
|
if (fullNormal.scheme() == defaultScriptLoc.scheme() &&
|
||||||
fullNormal.host() == defaultScriptLoc.host() &&
|
fullNormal.host() == defaultScriptLoc.host() &&
|
||||||
|
@ -69,7 +62,8 @@ QUrl normalizeScriptURL(const QUrl& rawScriptURL) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QUrl expandScriptUrl(const QUrl& normalizedScriptURL) {
|
QUrl expandScriptUrl(const QUrl& rawScriptURL) {
|
||||||
|
QUrl normalizedScriptURL = normalizeScriptURL(rawScriptURL);
|
||||||
if (normalizedScriptURL.scheme() == "http" ||
|
if (normalizedScriptURL.scheme() == "http" ||
|
||||||
normalizedScriptURL.scheme() == "https" ||
|
normalizedScriptURL.scheme() == "https" ||
|
||||||
normalizedScriptURL.scheme() == "atp") {
|
normalizedScriptURL.scheme() == "atp") {
|
||||||
|
@ -230,7 +224,7 @@ QVariantList ScriptEngines::getRunning() {
|
||||||
}
|
}
|
||||||
QVariantMap resultNode;
|
QVariantMap resultNode;
|
||||||
resultNode.insert("name", runningScriptURL.fileName());
|
resultNode.insert("name", runningScriptURL.fileName());
|
||||||
QUrl displayURL = expandScriptUrl(QUrl(runningScriptURL));
|
QUrl displayURL = expandScriptUrl(runningScriptURL);
|
||||||
QString displayURLString;
|
QString displayURLString;
|
||||||
if (displayURL.isLocalFile()) {
|
if (displayURL.isLocalFile()) {
|
||||||
displayURLString = displayURL.toLocalFile();
|
displayURLString = displayURL.toLocalFile();
|
||||||
|
@ -251,7 +245,7 @@ static const QString SETTINGS_KEY = "Settings";
|
||||||
|
|
||||||
void ScriptEngines::loadDefaultScripts() {
|
void ScriptEngines::loadDefaultScripts() {
|
||||||
QUrl defaultScriptsLoc = defaultScriptsLocation();
|
QUrl defaultScriptsLoc = defaultScriptsLocation();
|
||||||
defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "/scripts/defaultScripts.js");
|
defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "/defaultScripts.js");
|
||||||
loadScript(defaultScriptsLoc.toString());
|
loadScript(defaultScriptsLoc.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,7 +262,7 @@ void ScriptEngines::loadScripts() {
|
||||||
loadDefaultScripts();
|
loadDefaultScripts();
|
||||||
_firstRun.set(false);
|
_firstRun.set(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// loads all saved scripts
|
// loads all saved scripts
|
||||||
Settings settings;
|
Settings settings;
|
||||||
|
@ -310,7 +304,12 @@ void ScriptEngines::saveScripts() {
|
||||||
|
|
||||||
QStringList ScriptEngines::getRunningScripts() {
|
QStringList ScriptEngines::getRunningScripts() {
|
||||||
QReadLocker lock(&_scriptEnginesHashLock);
|
QReadLocker lock(&_scriptEnginesHashLock);
|
||||||
return _scriptEnginesHash.keys();
|
QList<QUrl> urls = _scriptEnginesHash.keys();
|
||||||
|
QStringList result;
|
||||||
|
for (auto url : urls) {
|
||||||
|
result.append(url.toString());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptEngines::stopAllScripts(bool restart) {
|
void ScriptEngines::stopAllScripts(bool restart) {
|
||||||
|
@ -318,7 +317,7 @@ void ScriptEngines::stopAllScripts(bool restart) {
|
||||||
if (restart) {
|
if (restart) {
|
||||||
// Delete all running scripts from cache so that they are re-downloaded when they are restarted
|
// Delete all running scripts from cache so that they are re-downloaded when they are restarted
|
||||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||||
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
|
for (QHash<QUrl, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
|
||||||
it != _scriptEnginesHash.constEnd(); it++) {
|
it != _scriptEnginesHash.constEnd(); it++) {
|
||||||
if (!it.value()->isFinished()) {
|
if (!it.value()->isFinished()) {
|
||||||
scriptCache->deleteScript(it.key());
|
scriptCache->deleteScript(it.key());
|
||||||
|
@ -327,7 +326,7 @@ void ScriptEngines::stopAllScripts(bool restart) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop and possibly restart all currently running scripts
|
// Stop and possibly restart all currently running scripts
|
||||||
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
|
for (QHash<QUrl, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
|
||||||
it != _scriptEnginesHash.constEnd(); it++) {
|
it != _scriptEnginesHash.constEnd(); it++) {
|
||||||
if (it.value()->isFinished()) {
|
if (it.value()->isFinished()) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -350,21 +349,20 @@ bool ScriptEngines::stopScript(const QString& rawScriptURL, bool restart) {
|
||||||
if (!scriptURL.isValid()) {
|
if (!scriptURL.isValid()) {
|
||||||
scriptURL = normalizeScriptURL(QUrl::fromLocalFile(rawScriptURL));
|
scriptURL = normalizeScriptURL(QUrl::fromLocalFile(rawScriptURL));
|
||||||
}
|
}
|
||||||
const QString scriptURLString = scriptURL.toString();
|
|
||||||
|
|
||||||
QReadLocker lock(&_scriptEnginesHashLock);
|
QReadLocker lock(&_scriptEnginesHashLock);
|
||||||
if (_scriptEnginesHash.contains(scriptURLString)) {
|
if (_scriptEnginesHash.contains(scriptURL)) {
|
||||||
ScriptEngine* scriptEngine = _scriptEnginesHash[scriptURLString];
|
ScriptEngine* scriptEngine = _scriptEnginesHash[scriptURL];
|
||||||
if (restart) {
|
if (restart) {
|
||||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||||
scriptCache->deleteScript(QUrl(scriptURLString));
|
scriptCache->deleteScript(scriptURL);
|
||||||
connect(scriptEngine, &ScriptEngine::finished, this, [this](QString scriptName, ScriptEngine* engine) {
|
connect(scriptEngine, &ScriptEngine::finished, this, [this](QString scriptName, ScriptEngine* engine) {
|
||||||
reloadScript(scriptName);
|
reloadScript(scriptName);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
scriptEngine->stop();
|
scriptEngine->stop();
|
||||||
stoppedScript = true;
|
stoppedScript = true;
|
||||||
qCDebug(scriptengine) << "stopping script..." << scriptURLString;
|
qCDebug(scriptengine) << "stopping script..." << scriptURL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return stoppedScript;
|
return stoppedScript;
|
||||||
|
@ -379,7 +377,7 @@ void ScriptEngines::setScriptsLocation(const QString& scriptsLocation) {
|
||||||
_scriptsModel.updateScriptsLocation(scriptsLocation);
|
_scriptsModel.updateScriptsLocation(scriptsLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptEngines::reloadAllScripts() {
|
void ScriptEngines::reloadAllScripts() {
|
||||||
DependencyManager::get<ScriptCache>()->clearCache();
|
DependencyManager::get<ScriptCache>()->clearCache();
|
||||||
emit scriptsReloading();
|
emit scriptsReloading();
|
||||||
stopAllScripts(true);
|
stopAllScripts(true);
|
||||||
|
@ -409,7 +407,7 @@ ScriptEngine* ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserL
|
||||||
scriptUrl = normalizeScriptURL(scriptFilename);
|
scriptUrl = normalizeScriptURL(scriptFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto scriptEngine = getScriptEngine(scriptUrl.toString());
|
auto scriptEngine = getScriptEngine(scriptUrl);
|
||||||
if (scriptEngine) {
|
if (scriptEngine) {
|
||||||
return scriptEngine;
|
return scriptEngine;
|
||||||
}
|
}
|
||||||
|
@ -435,12 +433,12 @@ ScriptEngine* ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserL
|
||||||
return scriptEngine;
|
return scriptEngine;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptEngine* ScriptEngines::getScriptEngine(const QString& rawScriptURL) {
|
ScriptEngine* ScriptEngines::getScriptEngine(const QUrl& rawScriptURL) {
|
||||||
ScriptEngine* result = nullptr;
|
ScriptEngine* result = nullptr;
|
||||||
{
|
{
|
||||||
QReadLocker lock(&_scriptEnginesHashLock);
|
QReadLocker lock(&_scriptEnginesHashLock);
|
||||||
const QString scriptURLString = normalizeScriptURL(QUrl(rawScriptURL)).toString();
|
const QUrl scriptURL = normalizeScriptURL(rawScriptURL);
|
||||||
auto it = _scriptEnginesHash.find(scriptURLString);
|
auto it = _scriptEnginesHash.find(scriptURL);
|
||||||
if (it != _scriptEnginesHash.end()) {
|
if (it != _scriptEnginesHash.end()) {
|
||||||
result = it.value();
|
result = it.value();
|
||||||
}
|
}
|
||||||
|
@ -459,8 +457,7 @@ void ScriptEngines::onScriptEngineLoaded(const QString& rawScriptURL) {
|
||||||
QWriteLocker lock(&_scriptEnginesHashLock);
|
QWriteLocker lock(&_scriptEnginesHashLock);
|
||||||
QUrl url = QUrl(rawScriptURL);
|
QUrl url = QUrl(rawScriptURL);
|
||||||
QUrl normalized = normalizeScriptURL(url);
|
QUrl normalized = normalizeScriptURL(url);
|
||||||
const QString scriptURLString = normalized.toString();
|
_scriptEnginesHash.insertMulti(normalized, scriptEngine);
|
||||||
_scriptEnginesHash.insertMulti(scriptURLString, scriptEngine);
|
|
||||||
}
|
}
|
||||||
emit scriptCountChanged();
|
emit scriptCountChanged();
|
||||||
}
|
}
|
||||||
|
@ -486,8 +483,8 @@ void ScriptEngines::onScriptFinished(const QString& rawScriptURL, ScriptEngine*
|
||||||
bool removed = false;
|
bool removed = false;
|
||||||
{
|
{
|
||||||
QWriteLocker lock(&_scriptEnginesHashLock);
|
QWriteLocker lock(&_scriptEnginesHashLock);
|
||||||
const QString scriptURLString = normalizeScriptURL(QUrl(rawScriptURL)).toString();
|
const QUrl scriptURL = normalizeScriptURL(QUrl(rawScriptURL));
|
||||||
for (auto it = _scriptEnginesHash.find(scriptURLString); it != _scriptEnginesHash.end(); ++it) {
|
for (auto it = _scriptEnginesHash.find(scriptURL); it != _scriptEnginesHash.end(); ++it) {
|
||||||
if (it.value() == engine) {
|
if (it.value() == engine) {
|
||||||
_scriptEnginesHash.erase(it);
|
_scriptEnginesHash.erase(it);
|
||||||
removed = true;
|
removed = true;
|
||||||
|
|
|
@ -45,7 +45,7 @@ public:
|
||||||
void loadDefaultScripts();
|
void loadDefaultScripts();
|
||||||
void setScriptsLocation(const QString& scriptsLocation);
|
void setScriptsLocation(const QString& scriptsLocation);
|
||||||
QStringList getRunningScripts();
|
QStringList getRunningScripts();
|
||||||
ScriptEngine* getScriptEngine(const QString& scriptHash);
|
ScriptEngine* getScriptEngine(const QUrl& scriptHash);
|
||||||
|
|
||||||
ScriptsModel* scriptsModel() { return &_scriptsModel; };
|
ScriptsModel* scriptsModel() { return &_scriptsModel; };
|
||||||
ScriptsModelFilter* scriptsModelFilter() { return &_scriptsModelFilter; };
|
ScriptsModelFilter* scriptsModelFilter() { return &_scriptsModelFilter; };
|
||||||
|
@ -65,12 +65,12 @@ public:
|
||||||
// Called at shutdown time
|
// Called at shutdown time
|
||||||
void shutdownScripting();
|
void shutdownScripting();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void scriptCountChanged();
|
void scriptCountChanged();
|
||||||
void scriptsReloading();
|
void scriptsReloading();
|
||||||
void scriptLoadError(const QString& filename, const QString& error);
|
void scriptLoadError(const QString& filename, const QString& error);
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void onScriptFinished(const QString& fileNameString, ScriptEngine* engine);
|
void onScriptFinished(const QString& fileNameString, ScriptEngine* engine);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -86,7 +86,7 @@ protected:
|
||||||
|
|
||||||
Setting::Handle<bool> _firstRun { "firstRun", true };
|
Setting::Handle<bool> _firstRun { "firstRun", true };
|
||||||
QReadWriteLock _scriptEnginesHashLock;
|
QReadWriteLock _scriptEnginesHashLock;
|
||||||
QHash<QString, ScriptEngine*> _scriptEnginesHash;
|
QHash<QUrl, ScriptEngine*> _scriptEnginesHash;
|
||||||
QSet<ScriptEngine*> _allKnownScriptEngines;
|
QSet<ScriptEngine*> _allKnownScriptEngines;
|
||||||
QMutex _allScriptsMutex;
|
QMutex _allScriptsMutex;
|
||||||
std::atomic<bool> _stoppingAllScripts { false };
|
std::atomic<bool> _stoppingAllScripts { false };
|
||||||
|
@ -97,6 +97,6 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
QUrl normalizeScriptURL(const QUrl& rawScriptURL);
|
QUrl normalizeScriptURL(const QUrl& rawScriptURL);
|
||||||
QUrl expandScriptUrl(const QUrl& normalizedScriptURL);
|
QUrl expandScriptUrl(const QUrl& rawScriptURL);
|
||||||
|
|
||||||
#endif // hifi_ScriptEngine_h
|
#endif // hifi_ScriptEngine_h
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
#define __STR1__(x) __STR2__(x)
|
#define __STR1__(x) __STR2__(x)
|
||||||
#define __LOC__ __FILE__ "(" __STR1__(__LINE__) ") : Warning Msg: "
|
#define __LOC__ __FILE__ "(" __STR1__(__LINE__) ") : Warning Msg: "
|
||||||
|
|
||||||
static const QString MODELS_LOCATION = "scripts/";
|
|
||||||
static const QString PREFIX_PARAMETER_NAME = "prefix";
|
static const QString PREFIX_PARAMETER_NAME = "prefix";
|
||||||
static const QString MARKER_PARAMETER_NAME = "marker";
|
static const QString MARKER_PARAMETER_NAME = "marker";
|
||||||
static const QString IS_TRUNCATED_NAME = "IsTruncated";
|
static const QString IS_TRUNCATED_NAME = "IsTruncated";
|
||||||
|
@ -41,7 +40,7 @@ TreeNodeBase::TreeNodeBase(TreeNodeFolder* parent, const QString& name, TreeNode
|
||||||
TreeNodeScript::TreeNodeScript(const QString& localPath, const QString& fullPath, ScriptOrigin origin) :
|
TreeNodeScript::TreeNodeScript(const QString& localPath, const QString& fullPath, ScriptOrigin origin) :
|
||||||
TreeNodeBase(NULL, localPath.split("/").last(), TREE_NODE_TYPE_SCRIPT),
|
TreeNodeBase(NULL, localPath.split("/").last(), TREE_NODE_TYPE_SCRIPT),
|
||||||
_localPath(localPath),
|
_localPath(localPath),
|
||||||
_fullPath(fullPath),
|
_fullPath(expandScriptUrl(QUrl(fullPath)).toString()),
|
||||||
_origin(origin) {
|
_origin(origin) {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -159,9 +158,11 @@ void ScriptsModel::requestDefaultFiles(QString marker) {
|
||||||
|
|
||||||
if (url.isLocalFile()) {
|
if (url.isLocalFile()) {
|
||||||
// if the url indicates a local directory, use QDirIterator
|
// if the url indicates a local directory, use QDirIterator
|
||||||
// QString localDir = url.toLocalFile() + "/scripts";
|
QString localDir = expandScriptUrl(url).toLocalFile();
|
||||||
QString localDir = expandScriptUrl(url).toLocalFile() + "/scripts";
|
|
||||||
int localDirPartCount = localDir.split("/").size();
|
int localDirPartCount = localDir.split("/").size();
|
||||||
|
if (localDir.endsWith("/")) {
|
||||||
|
localDirPartCount--;
|
||||||
|
}
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
localDirPartCount++; // one for the drive letter
|
localDirPartCount++; // one for the drive letter
|
||||||
#endif
|
#endif
|
||||||
|
@ -176,7 +177,7 @@ void ScriptsModel::requestDefaultFiles(QString marker) {
|
||||||
} else {
|
} else {
|
||||||
// the url indicates http(s), use QNetworkRequest
|
// the url indicates http(s), use QNetworkRequest
|
||||||
QUrlQuery query;
|
QUrlQuery query;
|
||||||
query.addQueryItem(PREFIX_PARAMETER_NAME, MODELS_LOCATION);
|
query.addQueryItem(PREFIX_PARAMETER_NAME, ".");
|
||||||
if (!marker.isEmpty()) {
|
if (!marker.isEmpty()) {
|
||||||
query.addQueryItem(MARKER_PARAMETER_NAME, marker);
|
query.addQueryItem(MARKER_PARAMETER_NAME, marker);
|
||||||
}
|
}
|
||||||
|
@ -240,7 +241,7 @@ bool ScriptsModel::parseXML(QByteArray xmlFile) {
|
||||||
if (jsRegex.exactMatch(xml.text().toString())) {
|
if (jsRegex.exactMatch(xml.text().toString())) {
|
||||||
QString localPath = lastKey.split("/").mid(1).join("/");
|
QString localPath = lastKey.split("/").mid(1).join("/");
|
||||||
QUrl fullPath = defaultScriptsLocation();
|
QUrl fullPath = defaultScriptsLocation();
|
||||||
fullPath.setPath(fullPath.path() + "/" + lastKey);
|
fullPath.setPath(fullPath.path() + lastKey);
|
||||||
const QString fullPathStr = normalizeScriptURL(fullPath).toString();
|
const QString fullPathStr = normalizeScriptURL(fullPath).toString();
|
||||||
_treeNodes.append(new TreeNodeScript(localPath, fullPathStr, SCRIPT_ORIGIN_DEFAULT));
|
_treeNodes.append(new TreeNodeScript(localPath, fullPathStr, SCRIPT_ORIGIN_DEFAULT));
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,11 +57,11 @@ QString findMostRecentFileExtension(const QString& originalFileName, QVector<QSt
|
||||||
|
|
||||||
QUrl defaultScriptsLocation() {
|
QUrl defaultScriptsLocation() {
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
return QUrl(("file:///" + QCoreApplication::applicationDirPath()).toLower());
|
return QUrl(("file:///" + QCoreApplication::applicationDirPath()).toLower() + "/scripts");
|
||||||
#elif defined(Q_OS_OSX)
|
#elif defined(Q_OS_OSX)
|
||||||
return QUrl(("file://" + QCoreApplication::applicationDirPath() + "/../Resources").toLower());
|
return QUrl(("file://" + QCoreApplication::applicationDirPath() + "/../Resources/scripts").toLower());
|
||||||
#else
|
#else
|
||||||
// return "http://s3.amazonaws.com/hifi-public";
|
// return "http://s3.amazonaws.com/hifi-public";
|
||||||
return QUrl("file://" + QCoreApplication::applicationDirPath());
|
return QUrl("file://" + QCoreApplication::applicationDirPath() + "/scripts");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue