diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json
index dce6c3449f..59fa9c4f3d 100644
--- a/domain-server/resources/describe-settings.json
+++ b/domain-server/resources/describe-settings.json
@@ -1,5 +1,5 @@
{
- "version": 1.4,
+ "version": 1.5,
"settings": [
{
"name": "metaverse",
@@ -141,6 +141,218 @@
"can_set": true
}
]
+ },
+ {
+ "label": "Operating Hours",
+ "help": "\"Open\" domains can be searched using their operating hours. Hours are entered in the local timezone, selected below.",
+
+ "name": "weekday_hours",
+ "caption": "Weekday Hours (Monday-Friday)",
+ "type": "table",
+ "can_add_new_rows": false,
+ "columns": [
+ {
+ "name": "open",
+ "label": "Opening Time",
+ "type": "time",
+ "default": "00:00",
+ "editable": true
+ },
+ {
+ "name": "close",
+ "label": "Closing Time",
+ "type": "time",
+ "default": "23:59",
+ "editable": true
+ }
+ ]
+ },
+ {
+ "name": "weekend_hours",
+ "label": "Weekend Hours (Saturday/Sunday)",
+ "type": "table",
+ "can_add_new_rows": false,
+ "columns": [
+ {
+ "name": "open",
+ "label": "Opening Time",
+ "type": "time",
+ "default": "00:00",
+ "editable": true
+ },
+ {
+ "name": "close",
+ "label": "Closing Time",
+ "type": "time",
+ "default": "23:59",
+ "editable": true
+ }
+ ]
+ },
+ {
+ "label": "Time Zone",
+ "name": "utc_offset",
+ "caption": "Time Zone",
+ "help": "This server's time zone. Used to define your server's operating hours.",
+ "type": "select",
+ "options": [
+ {
+ "value": "-12",
+ "label": "UTC-12:00"
+ },
+ {
+ "value": "-11",
+ "label": "UTC-11:00"
+ },
+ {
+ "value": "-10",
+ "label": "UTC-10:00"
+ },
+ {
+ "value": "-9.5",
+ "label": "UTC-09:30"
+ },
+ {
+ "value": "-9",
+ "label": "UTC-09:00"
+ },
+ {
+ "value": "-8",
+ "label": "UTC-08:00"
+ },
+ {
+ "value": "-7",
+ "label": "UTC-07:00"
+ },
+ {
+ "value": "-6",
+ "label": "UTC-06:00"
+ },
+ {
+ "value": "-5",
+ "label": "UTC-05:00"
+ },
+ {
+ "value": "-4",
+ "label": "UTC-04:00"
+ },
+ {
+ "value": "-3.5",
+ "label": "UTC-03:30"
+ },
+ {
+ "value": "-3",
+ "label": "UTC-03:00"
+ },
+ {
+ "value": "-2",
+ "label": "UTC-02:00"
+ },
+ {
+ "value": "-1",
+ "label": "UTC-01:00"
+ },
+ {
+ "value": "",
+ "label": "UTC±00:00"
+ },
+ {
+ "value": "1",
+ "label": "UTC+01:00"
+ },
+ {
+ "value": "2",
+ "label": "UTC+02:00"
+ },
+ {
+ "value": "3",
+ "label": "UTC+03:00"
+ },
+ {
+ "value": "3.5",
+ "label": "UTC+03:30"
+ },
+ {
+ "value": "4",
+ "label": "UTC+04:00"
+ },
+ {
+ "value": "4.5",
+ "label": "UTC+04:30"
+ },
+ {
+ "value": "5",
+ "label": "UTC+05:00"
+ },
+ {
+ "value": "5.5",
+ "label": "UTC+05:30"
+ },
+ {
+ "value": "5.75",
+ "label": "UTC+05:45"
+ },
+ {
+ "value": "6",
+ "label": "UTC+06:00"
+ },
+ {
+ "value": "6.5",
+ "label": "UTC+06:30"
+ },
+ {
+ "value": "7",
+ "label": "UTC+07:00"
+ },
+ {
+ "value": "8",
+ "label": "UTC+08:00"
+ },
+ {
+ "value": "8.5",
+ "label": "UTC+08:30"
+ },
+ {
+ "value": "8.75",
+ "label": "UTC+08:45"
+ },
+ {
+ "value": "9",
+ "label": "UTC+09:00"
+ },
+ {
+ "value": "9.5",
+ "label": "UTC+09:30"
+ },
+ {
+ "value": "10",
+ "label": "UTC+10:00"
+ },
+ {
+ "value": "10.5",
+ "label": "UTC+10:30"
+ },
+ {
+ "value": "11",
+ "label": "UTC+11:00"
+ },
+ {
+ "value": "12",
+ "label": "UTC+12:00"
+ },
+ {
+ "value": "12.75",
+ "label": "UTC+12:45"
+ },
+ {
+ "value": "13",
+ "label": "UTC+13:00"
+ },
+ {
+ "value": "14",
+ "label": "UTC+14:00"
+ }
+ ]
}
]
},
diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js
index aecc48b31f..c2cb2ecb80 100644
--- a/domain-server/resources/web/settings/js/settings.js
+++ b/domain-server/resources/web/settings/js/settings.js
@@ -243,6 +243,16 @@ $(document).ready(function(){
}
});
+ $('#' + Settings.FORM_ID).on('change', 'input.table-time', function() {
+ // Bootstrap switches in table: set the changed data attribute for all rows in table.
+ var row = $(this).closest('tr');
+ if (row.hasClass("value-row")) { // Don't set attribute on input row switches prior to it being added to table.
+ row.find('td.' + Settings.DATA_COL_CLASS + ' input').attr('data-changed', true);
+ updateDataChangedForSiblingRows(row, true);
+ badgeSidebarForDifferences($(this));
+ }
+ });
+
$('.advanced-toggle').click(function(){
Settings.showAdvanced = !Settings.showAdvanced
var advancedSelector = $('.' + Settings.ADVANCED_CLASS)
@@ -987,7 +997,7 @@ function makeTable(setting, keypath, setting_value, isLocked) {
html += "
" + rowIndexOrName + " | "
}
- var isNonDeletableRow = false;
+ var isNonDeletableRow = !setting.can_add_new_rows;
_.each(setting.columns, function(col) {
@@ -1007,6 +1017,10 @@ function makeTable(setting, keypath, setting_value, isLocked) {
html += ""
+ " | ";
+ } else if (isArray && col.type === "time" && col.editable) {
+ html += ""
+ + " | ";
} else {
// Use a hidden input so that the values are posted.
html += ""
@@ -1196,15 +1210,21 @@ function addTableRow(add_glyphicon) {
// Hide inputs
var input = $(element).find("input")
var isCheckbox = false;
+ var isTime = false;
if (input.hasClass("table-checkbox")) {
input = $(input).parent();
isCheckbox = true;
+ } else if (input.hasClass("table-time")) {
+ input = $(input).parent();
+ isTime = true;
}
var val = input.val();
if (isCheckbox) {
- val = $(input).find("input").is(':checked');
// don't hide the checkbox
+ val = $(input).find("input").is(':checked');
+ } else if (isTime) {
+ // don't hide the time
} else {
input.attr("type", "hidden")
}
diff --git a/domain-server/src/DomainMetadata.cpp b/domain-server/src/DomainMetadata.cpp
index d3eb7a97b0..f18aa8c71b 100644
--- a/domain-server/src/DomainMetadata.cpp
+++ b/domain-server/src/DomainMetadata.cpp
@@ -31,19 +31,28 @@ const QString DomainMetadata::Users::HOSTNAMES = "user_hostnames";
const QString DomainMetadata::DESCRIPTORS = "descriptors";
const QString DomainMetadata::Descriptors::DESCRIPTION = "description";
const QString DomainMetadata::Descriptors::CAPACITY = "capacity"; // parsed from security
-const QString DomainMetadata::Descriptors::HOURS = "hours";
const QString DomainMetadata::Descriptors::RESTRICTION = "restriction"; // parsed from ACL
const QString DomainMetadata::Descriptors::MATURITY = "maturity";
const QString DomainMetadata::Descriptors::HOSTS = "hosts";
const QString DomainMetadata::Descriptors::TAGS = "tags";
+const QString DomainMetadata::Descriptors::HOURS = "hours";
+const QString DomainMetadata::Descriptors::Hours::WEEKDAY = "weekday";
+const QString DomainMetadata::Descriptors::Hours::WEEKEND = "weekend";
+const QString DomainMetadata::Descriptors::Hours::UTC_OFFSET = "utc_offset";
+const QString DomainMetadata::Descriptors::Hours::OPEN = "open";
+const QString DomainMetadata::Descriptors::Hours::CLOSE = "close";
// descriptors metadata will appear as (JSON):
// { "description": String, // capped description
// "capacity": Number,
-// "hours": String, // UTF-8 representation of the week, split into 15" segments
// "restriction": String, // enum of either open, hifi, or acl
// "maturity": String, // enum corresponding to ESRB ratings
// "hosts": [ String ], // capped list of usernames
// "tags": [ String ], // capped list of tags
+// "hours": {
+// "utc_offset": Number,
+// "weekday": [ { "open": Time, "close": Time } ],
+// "weekend": [ { "open": Time, "close": Time } ],
+// }
// }
// metadata will appear as (JSON):
@@ -52,8 +61,14 @@ const QString DomainMetadata::Descriptors::TAGS = "tags";
// it is meant to be sent to and consumed by an external API
DomainMetadata::DomainMetadata(QObject* domainServer) : QObject(domainServer) {
- _metadata[USERS] = {};
- _metadata[DESCRIPTORS] = {};
+ // set up the structure necessary for casting during parsing (see parseHours, esp.)
+ _metadata[USERS] = QVariantMap {};
+ _metadata[DESCRIPTORS] = QVariantMap {
+ { Descriptors::HOURS, QVariantMap {
+ { Descriptors::Hours::WEEKDAY, QVariantList { QVariantMap{} } },
+ { Descriptors::Hours::WEEKEND, QVariantList { QVariantMap{} } }
+ } }
+ };
assert(dynamic_cast(domainServer));
DomainServer* server = static_cast(domainServer);
@@ -67,6 +82,7 @@ DomainMetadata::DomainMetadata(QObject* domainServer) : QObject(domainServer) {
this, static_cast(&DomainMetadata::securityChanged));
// initialize the descriptors
+ securityChanged(false);
descriptorsChanged();
}
@@ -80,27 +96,78 @@ QJsonObject DomainMetadata::get(const QString& group) {
return QJsonObject::fromVariantMap(_metadata[group].toMap());
}
+void parseHours(QVariant delta, QVariant& target) {
+ using Hours = DomainMetadata::Descriptors::Hours;
+
+ // hours should be of the form [ { open: Time, close: Time } ]
+ assert(target.canConvert());
+ auto& targetList = *static_cast(target.data());
+
+ // if/when multiple ranges are allowed, this list will need to be iterated
+ assert(targetList[0].canConvert());
+ auto& targetMap = *static_cast(targetList[0].data());
+
+ auto deltaMap = delta.toList()[0].toMap();
+ if (deltaMap.isEmpty()) {
+ return;
+ }
+
+ // merge delta into base
+ auto open = deltaMap.find(Hours::OPEN);
+ if (open != deltaMap.end()) {
+ targetMap[Hours::OPEN] = open.value();
+ }
+ assert(targetMap[Hours::OPEN].canConvert());
+ auto close = deltaMap.find(Hours::CLOSE);
+ if (close != deltaMap.end()) {
+ targetMap[Hours::CLOSE] = close.value();
+ }
+ assert(targetMap[Hours::CLOSE].canConvert());
+}
+
void DomainMetadata::descriptorsChanged() {
- const QString CAPACITY = "security.maximum_user_capacity";
+ // get descriptors
+ assert(_metadata[DESCRIPTORS].canConvert());
+ auto& state = *static_cast(_metadata[DESCRIPTORS].data());
auto settings = static_cast(parent())->_settingsManager.getSettingsMap();
+ auto descriptors = settings[DESCRIPTORS].toMap();
+
+ // copy simple descriptors (description/maturity)
+ state[Descriptors::DESCRIPTION] = descriptors[Descriptors::DESCRIPTION];
+ state[Descriptors::MATURITY] = descriptors[Descriptors::MATURITY];
+
+ // copy array descriptors (hosts/tags)
+ state[Descriptors::HOSTS] = descriptors[Descriptors::HOSTS].toList();
+ state[Descriptors::TAGS] = descriptors[Descriptors::TAGS].toList();
+
+ // parse capacity
+ const QString CAPACITY = "security.maximum_user_capacity";
const QVariant* capacityVariant = valueForKeyPath(settings, CAPACITY);
unsigned int capacity = capacityVariant ? capacityVariant->toUInt() : 0;
+ state[Descriptors::CAPACITY] = capacity;
- auto descriptors = settings[DESCRIPTORS].toMap();
- descriptors[Descriptors::CAPACITY] = capacity;
- _metadata[DESCRIPTORS] = descriptors;
-
- // update overwritten fields
- securityChanged(false);
+ // parse operating hours
+ const QString WEEKDAY_HOURS = "weekday_hours";
+ const QString WEEKEND_HOURS = "weekend_hours";
+ const QString UTC_OFFSET = "utc_offset";
+ assert(state[Descriptors::HOURS].canConvert());
+ auto& hours = *static_cast(state[Descriptors::HOURS].data());
+ parseHours(descriptors.take(WEEKDAY_HOURS), hours[Descriptors::Hours::WEEKDAY]);
+ parseHours(descriptors.take(WEEKEND_HOURS), hours[Descriptors::Hours::WEEKEND]);
+ hours[Descriptors::Hours::UTC_OFFSET] = descriptors.take(UTC_OFFSET);
#if DEV_BUILD || PR_BUILD
- qDebug() << "Domain metadata descriptors set:" << _metadata[DESCRIPTORS];
+ qDebug() << "Domain metadata descriptors set:" << QJsonObject::fromVariantMap(_metadata[DESCRIPTORS].toMap());
#endif
sendDescriptors();
}
void DomainMetadata::securityChanged(bool send) {
+ // get descriptors
+ assert(_metadata[DESCRIPTORS].canConvert());
+ auto& state = *static_cast(_metadata[DESCRIPTORS].data());
+
const QString RESTRICTION_OPEN = "open";
const QString RESTRICTION_ANON = "anon";
const QString RESTRICTION_HIFI = "hifi";
@@ -121,9 +188,7 @@ void DomainMetadata::securityChanged(bool send) {
restriction = RESTRICTION_ACL;
}
- auto descriptors = _metadata[DESCRIPTORS].toMap();
- descriptors[Descriptors::RESTRICTION] = restriction;
- _metadata[DESCRIPTORS] = descriptors;
+ state[Descriptors::RESTRICTION] = restriction;
#if DEV_BUILD || PR_BUILD
qDebug() << "Domain metadata restriction set:" << restriction;
@@ -176,15 +241,14 @@ void DomainMetadata::maybeUpdateUsers() {
}
});
- QVariantMap users = {
- { Users::NUM_TOTAL, numConnected },
- { Users::NUM_ANON, numConnectedAnonymously },
- { Users::HOSTNAMES, userHostnames }};
- _metadata[USERS] = users;
- ++_tic;
+ assert(_metadata[USERS].canConvert());
+ auto& users = *static_cast(_metadata[USERS].data());
+ users[Users::NUM_TOTAL] = numConnected;
+ users[Users::NUM_ANON] = numConnectedAnonymously;
+ users[Users::HOSTNAMES] = userHostnames;
#if DEV_BUILD || PR_BUILD
- qDebug() << "Domain metadata users updated:" << users;
+ qDebug() << "Domain metadata users set:" << QJsonObject::fromVariantMap(_metadata[USERS].toMap());
#endif
}
@@ -193,10 +257,16 @@ void DomainMetadata::sendDescriptors() {
const QUuid& domainID = DependencyManager::get()->getSessionUUID();
if (!domainID.isNull()) {
static const QString DOMAIN_UPDATE = "/api/v1/domains/%1";
- DependencyManager::get()->sendRequest(DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(domainID)),
+ QString path { DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(domainID)) };
+ DependencyManager::get()->sendRequest(path,
AccountManagerAuth::Required,
QNetworkAccessManager::PutOperation,
JSONCallbackParameters(),
domainUpdateJSON.toUtf8());
+
+#if DEV_BUILD || PR_BUILD
+ qDebug() << "Domain metadata sent to" << path;
+ qDebug() << "Domain metadata update:" << domainUpdateJSON;
+#endif
}
}
diff --git a/domain-server/src/DomainMetadata.h b/domain-server/src/DomainMetadata.h
index 0bfd6c611b..41f3a60832 100644
--- a/domain-server/src/DomainMetadata.h
+++ b/domain-server/src/DomainMetadata.h
@@ -35,11 +35,19 @@ public:
public:
static const QString DESCRIPTION;
static const QString CAPACITY;
- static const QString HOURS;
static const QString RESTRICTION;
static const QString MATURITY;
static const QString HOSTS;
static const QString TAGS;
+ static const QString HOURS;
+ class Hours {
+ public:
+ static const QString WEEKDAY;
+ static const QString WEEKEND;
+ static const QString UTC_OFFSET;
+ static const QString OPEN;
+ static const QString CLOSE;
+ };
};
DomainMetadata(QObject* domainServer);
diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp
index b03d2c07ae..543e61f485 100644
--- a/domain-server/src/DomainServerSettingsManager.cpp
+++ b/domain-server/src/DomainServerSettingsManager.cpp
@@ -21,6 +21,8 @@
#include
#include
+#include
+
#include
#include
#include
@@ -69,7 +71,7 @@ DomainServerSettingsManager::DomainServerSettingsManager() :
}
static const QString MISSING_SETTINGS_DESC_MSG =
- QString("Did not find settings decription in JSON at %1 - Unable to continue. domain-server will quit.\n%2 at %3")
+ QString("Did not find settings description in JSON at %1 - Unable to continue. domain-server will quit.\n%2 at %3")
.arg(SETTINGS_DESCRIPTION_RELATIVE_PATH).arg(parseError.errorString()).arg(parseError.offset);
static const int MISSING_SETTINGS_DESC_ERROR_CODE = 6;
@@ -258,6 +260,27 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
_standardAgentPermissions.clear();
_agentPermissions.clear();
}
+
+ if (oldVersion < 1.5) {
+ // This was prior to operating hours, so add default hours
+ static const QString WEEKDAY_HOURS{ "descriptors.weekday_hours" };
+ static const QString WEEKEND_HOURS{ "descriptors.weekend_hours" };
+ static const QString UTC_OFFSET{ "descriptors.utc_offset" };
+
+ QVariant* weekdayHours = valueForKeyPath(_configMap.getUserConfig(), WEEKDAY_HOURS, true);
+ QVariant* weekendHours = valueForKeyPath(_configMap.getUserConfig(), WEEKEND_HOURS, true);
+ QVariant* utcOffset = valueForKeyPath(_configMap.getUserConfig(), UTC_OFFSET, true);
+
+ *weekdayHours = QVariantList { QVariantMap{ { "open", QVariant("00:00") }, { "close", QVariant("23:59") } } };
+ *weekendHours = QVariantList { QVariantMap{ { "open", QVariant("00:00") }, { "close", QVariant("23:59") } } };
+ *utcOffset = QVariant(QTimeZone::systemTimeZone().offsetFromUtc(QDateTime::currentDateTime()) / (float)3600);
+
+ // write the new settings to file
+ persistToFile();
+
+ // reload the master and user config so the merged config is correct
+ _configMap.loadMasterAndUserConfig(_argumentList);
+ }
}
unpackPermissions();
|