Merge pull request #8111 from zzmp/feat/domain-hours-metadata

add operating hours to domain
This commit is contained in:
Brad Hefta-Gaub 2016-06-21 13:14:13 -07:00 committed by GitHub
commit 5dcf5031a3
5 changed files with 361 additions and 28 deletions

View file

@ -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"
}
]
}
]
},

View file

@ -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 += "<td class='key'>" + rowIndexOrName + "</td>"
}
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 += "<td class='" + Settings.DATA_COL_CLASS + "'name='" + col.name + "'>"
+ "<input type='checkbox' class='form-control table-checkbox' "
+ "name='" + colName + "'" + (colValue ? " checked" : "") + " /></td>";
} else if (isArray && col.type === "time" && col.editable) {
html += "<td class='" + Settings.DATA_COL_CLASS + "'name='" + col.name + "'>"
+ "<input type='time' class='form-control table-time' "
+ "name='" + colName + "' value='" + (colValue || col.default || "00:00") + "' /></td>";
} else {
// Use a hidden input so that the values are posted.
html += "<td class='" + Settings.DATA_COL_CLASS + "' name='" + colName + "'>"
@ -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")
}

View file

@ -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));
DomainServer* server = static_cast<DomainServer*>(domainServer);
@ -67,6 +82,7 @@ DomainMetadata::DomainMetadata(QObject* domainServer) : QObject(domainServer) {
this, static_cast<void(DomainMetadata::*)()>(&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<QVariantList>());
auto& targetList = *static_cast<QVariantList*>(target.data());
// if/when multiple ranges are allowed, this list will need to be iterated
assert(targetList[0].canConvert<QVariantMap>());
auto& targetMap = *static_cast<QVariantMap*>(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<QString>());
auto close = deltaMap.find(Hours::CLOSE);
if (close != deltaMap.end()) {
targetMap[Hours::CLOSE] = close.value();
}
assert(targetMap[Hours::CLOSE].canConvert<QString>());
}
void DomainMetadata::descriptorsChanged() {
const QString CAPACITY = "security.maximum_user_capacity";
// get descriptors
assert(_metadata[DESCRIPTORS].canConvert<QVariantMap>());
auto& state = *static_cast<QVariantMap*>(_metadata[DESCRIPTORS].data());
auto settings = static_cast<DomainServer*>(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<QVariantMap>());
auto& hours = *static_cast<QVariantMap*>(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<QVariantMap>());
auto& state = *static_cast<QVariantMap*>(_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<QVariantMap>());
auto& users = *static_cast<QVariantMap*>(_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<LimitedNodeList>()->getSessionUUID();
if (!domainID.isNull()) {
static const QString DOMAIN_UPDATE = "/api/v1/domains/%1";
DependencyManager::get<AccountManager>()->sendRequest(DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(domainID)),
QString path { DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(domainID)) };
DependencyManager::get<AccountManager>()->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
}
}

View file

@ -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);

View file

@ -21,6 +21,8 @@
#include <QtCore/QUrl>
#include <QtCore/QUrlQuery>
#include <QTimeZone>
#include <Assignment.h>
#include <HifiConfigVariantMap.h>
#include <HTTPConnection.h>
@ -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();