(function () { var identityDecoder = function (src) { return src; }; var identityEncoder = function (src) { return src; }; var mapDecoder = function (src) { var map = {}; var lines = src.split(/\n/); $.each(lines, function () { var m = this.match(/^(\S+)\s+(\S+)$/); if (m) { map[ m[1] ] = m[2]; } }); return map; }; var mapEncoder = function (map) { var lines = []; $.each(map, function (key) { lines.push(key + " " + this); }); return lines.join("\n"); }; var boolDecoder = function (src) { return src == "*"; }; var boolEncoder = function (bool) { return bool ? "*" : ""; }; var decoder_of = { siteName : identityDecoder, baseURI : identityDecoder, defaultPage: identityDecoder, styleSheet : identityDecoder, languages : mapDecoder, globalLock : boolDecoder }; var encoder_of = { siteName : identityEncoder, baseURI : identityEncoder, defaultPage: identityEncoder, styleSheet : identityEncoder, languages : mapEncoder, globalLock : boolEncoder }; var cachedConf = null; var isValidBaseURI = function (str) { parseUri.options.strictMode = true; var uri = parseUri(str); return (uri.protocol != "" && uri.authority != "" && uri.path != "" && uri.path.match(/\/$/) && uri.query == "" && uri.anchor == ""); }; // FIXME: Don't let user to manipulate directly this structure. // FIXME: Values may include spaces. var isValidMap = function (src) { return src.match(/^(?:\S+\s+\S+(?:\n\S+\s+\S+)*\n?)?$/) != null; }; Rakka.getUserList = function () { var users = []; $.ajax({ type: "GET", url: Rakka.baseURI + "users", async: false, beforeSend: function (req) { Rakka.setAuthorization(req); }, success: function (xml) { $(xml).find("user").each(function () { users.push(this.getAttribute("id")); }); }, error: function (req) { throw new Error(req.status + " " + req.statusText); } }); return users; }; Rakka.getSystemConfig = function () { if (cachedConf != null) { return cachedConf; } var conf = {}; cachedConf = conf; $.ajax({ type : "GET", url : Rakka.baseURI + "systemConfig", async : false, success: function (xml) { $(xml).find("value").each(function () { var path = this.getAttribute("path"); var decoder = decoder_of[path]; if (decoder == null) { throw new Error("unknown config path: " + path); } else { conf[path] = decoder($(this).text()); } }); }, error : function (req) { throw new Error(req.status + " " + req.statusText); } }); return conf; }; var appendConfigPanel = function ($area) { $area.append($.H1({}, "Configuration")); var fldSiteName = $.INPUT({type: "text"}); var fldBaseURI = $.INPUT({type: "text"}); var fldDefaultPage = $.INPUT({type: "text"}); var fldStyleSheet = $.INPUT({type: "text"}); var fldLanguages = $.TEXTAREA({}); var chkGlobalLock = $.INPUT({type: "checkbox"}); var btnSave = $.INPUT({type: "button", value: "Save changes"}); var btnRevert = $.INPUT({type: "button", value: "Revert changes"}); var makeClean = function () { $(btnSave).attr({disabled: "disabled"}); $(btnRevert).attr({disabled: "disabled"}); }; var updateConfig = function () { var conf = Rakka.getSystemConfig(); fldSiteName.value = conf.siteName; fldBaseURI.value = conf.baseURI; fldDefaultPage.value = conf.defaultPage; fldStyleSheet.value = conf.styleSheet; fldLanguages.value = encoder_of.languages(conf.languages); chkGlobalLock.checked = conf.globalLock; makeClean(); }; updateConfig(); var validate = function () { var isValid = (function () { if (!isValidBaseURI(fldBaseURI.value)) { return false; } if (fldDefaultPage.value.match(Rakka.rePageName) == null) { return false; } if (fldStyleSheet.value.match(Rakka.rePageName) == null) { return false; } if (!isValidMap(fldLanguages.value)) { return false; } return true; })(); $(btnSave).attr({disabled: (isValid ? "" : "disabled")}); }; var makeDirty = function () { $(btnRevert).attr({disabled: ""}); validate(); }; $(btnSave).click(function () { var NS = "http://cielonegro.org/schema/Rakka/Config/1.0"; var doc = document.implementation.createDocument(NS, "systemConfig", null); var sc = doc.documentElement; var mkValue = function (path, value) { var elem = doc.createElementNS(NS, "value"); elem.setAttribute("path", path); elem.appendChild(doc.createTextNode(value)); return elem; }; sc.appendChild(mkValue("siteName" , fldSiteName.value)); sc.appendChild(mkValue("baseURI" , fldBaseURI.value)); sc.appendChild(mkValue("defaultPage", fldDefaultPage.value)); sc.appendChild(mkValue("styleSheet" , fldStyleSheet.value)); sc.appendChild(mkValue("languages" , fldLanguages.value)); sc.appendChild(mkValue("globalLock" , encoder_of["globalLock"](chkGlobalLock.checked))); Rakka.displayWaitingMessage("Submitting... please wait."); var url = Rakka.baseURI + "systemConfig"; $.ajax({ type : "PUT", url : url, contentType: "text/xml", data : doc, processData: false, beforeSend : function (req) { Rakka.setAuthorization(req); }, success : function () { cachedConf = null; Rakka.hideWaitingMessage(); makeClean(); }, error : function (req) { Rakka.hideWaitingMessage(); // FIXME: better error handling var $area = Rakka.switchScreen(); $area.text("Error: " + req.status + " " + req.statusText); } }); }); $(btnRevert).click(function () { if (window.confirm("Do you really want to discard changes?")) { updateConfig(); } }); var configPanel = $.TABLE({className: "pageEditor"}, $.TBODY({}, $.TR({}, $.TH({}, "Site name"), $.TD({}, fldSiteName)), $.TR({}, $.TH({}, "Base URI"), $.TD({}, fldBaseURI)), $.TR({}, $.TH({}, "Default page"), $.TD({}, fldDefaultPage)), $.TR({}, $.TH({}, "Style sheet"), $.TD({}, fldStyleSheet)), $.TR({}, $.TH({}, "Languages"), $.TD({}, fldLanguages)), $.TR({}, $.TH({}, "Global lock"), $.TD({}, $.LABEL({}, chkGlobalLock, "Disallow guest users to edit pages."))), $.TR({}, $.TH({}), $.TD({}, btnSave, btnRevert)))); $(fldSiteName) .add(fldBaseURI) .add(fldDefaultPage) .add(fldStyleSheet) .add(fldLanguages) .keyup(makeDirty) .change(makeDirty); $area.append(configPanel); }; var appendUsersPanel = function ($area) { $area.append($.H2({}, "Users")); $area.append($.H3({}, "Existing Users")); var tbody = $.TBODY(); var usersPanel = $.TABLE({className: "pageEditor"}, $.THEAD({}, $.TR({}, $.TH({}, "User ID"), $.TH({}, "Change Password"), $.TH({}, "Delete User"))), tbody); var updateUserList = function () { var users = Rakka.getUserList(); $(tbody).empty(); for (var i = 0; i < users.length; i++) { var pass1 = $.INPUT({type: "password"}); var pass2 = $.INPUT({type: "password"}); var change = $.INPUT({type: "button", value: "Change"}); var chpass = $.TABLE({className: "pageEditor"}, $.TR({}, $.TD({}, pass1), $.TD({rowSpan: 2}, change)), $.TR({}, $.TD({}, pass2))); var delUser = $.INPUT({type: "button", value: "Delete"}); var validatePassword = function () { var isValid = (function () { if (pass1.value == "") { return false; } if (pass1.value != pass2.value) { return false; } return true; })(); $(change).attr({disabled: (isValid ? "" : "disabled")}); }; validatePassword(); $(pass1) .add(pass2) .change(validatePassword) .keyup(validatePassword); $.each(users, function () { var id = users[i]; var tr = $.TR({}, $.TD({}, id), $.TD({}, chpass), $.TD({}, delUser) ); tbody.appendChild(tr); }); } }; updateUserList(); $area.append(usersPanel); $area.append($.H3({}, "Add new user")); var userID = $.INPUT({type: "text"}); var pass1 = $.INPUT({type: "password"}); var pass2 = $.INPUT({type: "password"}); var addUser = $.INPUT({type: "button", value: "Add"}); var addUserPanel = $.TABLE({className: "pageEditor"}, $.TR({}, $.TH({}, "User ID"), $.TD({}, userID)), $.TR({}, $.TH({}, "Password"), $.TD({}, pass1)), $.TR({}, $.TH({}, "Password (retype)"), $.TD({}, pass2)), $.TR({}, $.TH({}), $.TD({}, addUser))); $area.append(addUserPanel); }; Rakka.showConfigPanel = function () { var $area = Rakka.switchScreen(); appendConfigPanel($area); appendUsersPanel($area); }; $(document).ready(function () { $("input.configButton") .click(function () { Rakka.showConfigPanel(); }); }); })();