Working on Web admin

This commit is contained in:
Jérôme Schneider 2012-04-19 08:58:52 +02:00
parent 35511b68bb
commit d960db5816
13 changed files with 987 additions and 437 deletions

View file

@ -71,13 +71,21 @@ class AddressBook extends \Flake\Core\Model\Db {
$oMorpho->add(new \Formal\Element\Text(array(
"prop" => "uri",
"label" => "Address Book token ID",
"validation" => "required,tokenid"
"validation" => "required,tokenid",
"popover" => array(
"title" => "Address Book token ID",
"content" => "The unique identifier for this address book.",
)
)));
$oMorpho->add(new \Formal\Element\Text(array(
"prop" => "displayname",
"label" => "Display name",
"validation" => "required"
"validation" => "required",
"popover" => array(
"title" => "Display name",
"content" => "This is the name that will be displayed in your CardDAV client.",
)
)));
$oMorpho->add(new \Formal\Element\Text(array(
@ -86,7 +94,12 @@ class AddressBook extends \Flake\Core\Model\Db {
"validation" => "required"
)));
if(!$this->floating()) {
if($this->floating()) {
$oMorpho->element("uri")->setOption(
"help",
"Allowed characters are digits, lowercase letters and the dash symbol '-'."
);
} else {
$oMorpho->element("uri")->setOption("readonly", TRUE);
}

View file

@ -113,13 +113,21 @@ class Calendar extends \Flake\Core\Model\Db {
$oMorpho->add(new \Formal\Element\Text(array(
"prop" => "uri",
"label" => "Calendar token ID",
"validation" => "required,tokenid"
"validation" => "required,tokenid",
"popover" => array(
"title" => "Calendar token ID",
"content" => "The unique identifier for this calendar.",
)
)));
$oMorpho->add(new \Formal\Element\Text(array(
"prop" => "displayname",
"label" => "Display name",
"validation" => "required"
"validation" => "required",
"popover" => array(
"title" => "Display name",
"content" => "This is the name that will be displayed in your CalDAV client.",
)
)));
$oMorpho->add(new \Formal\Element\Text(array(
@ -130,10 +138,16 @@ class Calendar extends \Flake\Core\Model\Db {
$oMorpho->add(new \Formal\Element\Checkbox(array(
"prop" => "todos",
"label" => "Todos"
"label" => "Todos",
"help" => "If checked, todos will be enabled on this calendar.",
)));
if(!$this->floating()) {
if($this->floating()) {
$oMorpho->element("uri")->setOption(
"help",
"Allowed characters are digits, lowercase letters and the dash symbol '-'."
);
} else {
$oMorpho->element("uri")->setOption("readonly", TRUE);
}

View file

@ -52,7 +52,7 @@ class Config extends \Flake\Core\Model\NoDb {
"type" => "integer",
),
"BAIKAL_ADMIN_PASSWORDHASH" => array(
"type" => "calculated",
"type" => "string",
)
);
@ -133,6 +133,8 @@ class Config extends \Flake\Core\Model\NoDb {
}
$aRes[$sConstant] = $sValue;
} elseif($iNbRes === 0) {
throw new \Exception("Baikal\Model\Config->parseConfig(): Unable to find constant '" . $prop . "' in config file");
}
}
@ -155,415 +157,12 @@ class Config extends \Flake\Core\Model\NoDb {
"prop" => "BAIKAL_TIMEZONE",
"label" => "Time zone",
"validation" => "required",
"options" => array(
"Africa/Abidjan",
"Africa/Accra",
"Africa/Addis_Ababa",
"Africa/Algiers",
"Africa/Asmara",
"Africa/Bamako",
"Africa/Bangui",
"Africa/Banjul",
"Africa/Bissau",
"Africa/Blantyre",
"Africa/Brazzaville",
"Africa/Bujumbura",
"Africa/Cairo",
"Africa/Casablanca",
"Africa/Ceuta",
"Africa/Conakry",
"Africa/Dakar",
"Africa/Dar_es_Salaam",
"Africa/Djibouti",
"Africa/Douala",
"Africa/El_Aaiun",
"Africa/Freetown",
"Africa/Gaborone",
"Africa/Harare",
"Africa/Johannesburg",
"Africa/Juba",
"Africa/Kampala",
"Africa/Khartoum",
"Africa/Kigali",
"Africa/Kinshasa",
"Africa/Lagos",
"Africa/Libreville",
"Africa/Lome",
"Africa/Luanda",
"Africa/Lubumbashi",
"Africa/Lusaka",
"Africa/Malabo",
"Africa/Maputo",
"Africa/Maseru",
"Africa/Mbabane",
"Africa/Mogadishu",
"Africa/Monrovia",
"Africa/Nairobi",
"Africa/Ndjamena",
"Africa/Niamey",
"Africa/Nouakchott",
"Africa/Ouagadougou",
"Africa/Porto-Novo",
"Africa/Sao_Tome",
"Africa/Tripoli",
"Africa/Tunis",
"Africa/Windhoek",
"America/Adak",
"America/Anchorage",
"America/Anguilla",
"America/Antigua",
"America/Araguaina",
"America/Argentina/Buenos_Aires",
"America/Argentina/Catamarca",
"America/Argentina/Cordoba",
"America/Argentina/Jujuy",
"America/Argentina/La_Rioja",
"America/Argentina/Mendoza",
"America/Argentina/Rio_Gallegos",
"America/Argentina/Salta",
"America/Argentina/San_Juan",
"America/Argentina/San_Luis",
"America/Argentina/Tucuman",
"America/Argentina/Ushuaia",
"America/Aruba",
"America/Asuncion",
"America/Atikokan",
"America/Bahia",
"America/Barbados",
"America/Belem",
"America/Belize",
"America/Blanc-Sablon",
"America/Boa_Vista",
"America/Bogota",
"America/Boise",
"America/Cambridge_Bay",
"America/Campo_Grande",
"America/Cancun",
"America/Caracas",
"America/Cayenne",
"America/Cayman",
"America/Chicago",
"America/Chihuahua",
"America/Costa_Rica",
"America/Cuiaba",
"America/Curacao",
"America/Danmarkshavn",
"America/Dawson",
"America/Dawson_Creek",
"America/Denver",
"America/Detroit",
"America/Dominica",
"America/Edmonton",
"America/Eirunepe",
"America/El_Salvador",
"America/Felipe_Carrillo",
"America/Fortaleza",
"America/Glace_Bay",
"America/Godthab",
"America/Goose_Bay",
"America/Grand_Turk",
"America/Grenada",
"America/Guadeloupe",
"America/Guatemala",
"America/Guayaquil",
"America/Guyana",
"America/Halifax",
"America/Havana",
"America/Hermosillo",
"America/Indiana/Indianapolis",
"America/Indiana/Knox",
"America/Indiana/Marengo",
"America/Indiana/Petersburg",
"America/Indiana/Tell_City",
"America/Indiana/Vevay",
"America/Indiana/Vincennes",
"America/Indiana/Winamac",
"America/Inuvik",
"America/Iqaluit",
"America/Jamaica",
"America/Juneau",
"America/Kentucky/Louisville",
"America/Kentucky/Monticello",
"America/La_Paz",
"America/Lima",
"America/Los_Angeles",
"America/Maceio",
"America/Managua",
"America/Manaus",
"America/Marigot",
"America/Martinique",
"America/Matamoros",
"America/Mazatlan",
"America/Menominee",
"America/Merida",
"America/Mexico_City",
"America/Miquelon",
"America/Moncton",
"America/Monterrey",
"America/Montevideo",
"America/Montreal",
"America/Montserrat",
"America/Nassau",
"America/New_York",
"America/Nipigon",
"America/Nome",
"America/Noronha",
"America/North_Dakota/Center",
"America/North_Dakota/New_Salem",
"America/Ojinaga",
"America/Panama",
"America/Pangnirtung",
"America/Paramaribo",
"America/Phoenix",
"America/Port-au-Prince",
"America/Porto_Velho",
"America/Port_of_Spain",
"America/Puerto_Rico",
"America/Rainy_River",
"America/Rankin_Inlet",
"America/Recife",
"America/Regina",
"America/Resolute",
"America/Rio_Branco",
"America/Santarem",
"America/Santa_Isabel",
"America/Santiago",
"America/Santo_Domingo",
"America/Sao_Paulo",
"America/Scoresbysund",
"America/Shiprock",
"America/St_Barthelemy",
"America/St_Johns",
"America/St_Kitts",
"America/St_Lucia",
"America/St_Thomas",
"America/St_Vincent",
"America/Swift_Current",
"America/Tegucigalpa",
"America/Thule",
"America/Thunder_Bay",
"America/Tijuana",
"America/Toronto",
"America/Tortola",
"America/Vancouver",
"America/Whitehorse",
"America/Winnipeg",
"America/Yakutat",
"America/Yellowknife",
"Antarctica/Casey",
"Antarctica/Davis",
"Antarctica/DumontDUrville",
"Antarctica/Mawson",
"Antarctica/McMurdo",
"Antarctica/Palmer",
"Antarctica/Rothera",
"Antarctica/South_Pole",
"Antarctica/Syowa",
"Antarctica/Vostok",
"Arctic/Longyearbyen",
"Asia/Aden",
"Asia/Almaty",
"Asia/Amman",
"Asia/Anadyr",
"Asia/Aqtau",
"Asia/Aqtobe",
"Asia/Ashgabat",
"Asia/Baghdad",
"Asia/Bahrain",
"Asia/Baku",
"Asia/Bangkok",
"Asia/Beirut",
"Asia/Bishkek",
"Asia/Brunei",
"Asia/Choibalsan",
"Asia/Chongqing",
"Asia/Colombo",
"Asia/Damascus",
"Asia/Dhaka",
"Asia/Dili",
"Asia/Dubai",
"Asia/Dushanbe",
"Asia/Gaza",
"Asia/Harbin",
"Asia/Hong_Kong",
"Asia/Hovd",
"Asia/Ho_Chi_Minh",
"Asia/Irkutsk",
"Asia/Jakarta",
"Asia/Jayapura",
"Asia/Jerusalem",
"Asia/Kabul",
"Asia/Kamchatka",
"Asia/Karachi",
"Asia/Kashgar",
"Asia/Kathmandu",
"Asia/Kolkata",
"Asia/Krasnoyarsk",
"Asia/Kuala_Lumpur",
"Asia/Kuching",
"Asia/Kuwait",
"Asia/Macau",
"Asia/Magadan",
"Asia/Makassar",
"Asia/Manila",
"Asia/Muscat",
"Asia/Nicosia",
"Asia/Novokuznetsk",
"Asia/Novosibirsk",
"Asia/Omsk",
"Asia/Oral",
"Asia/Phnom_Penh",
"Asia/Pontianak",
"Asia/Pyongyang",
"Asia/Qatar",
"Asia/Qyzylorda",
"Asia/Rangoon",
"Asia/Riyadh",
"Asia/Sakhalin",
"Asia/Samarkand",
"Asia/Seoul",
"Asia/Shanghai",
"Asia/Singapore",
"Asia/Taipei",
"Asia/Tashkent",
"Asia/Tbilisi",
"Asia/Tehran",
"Asia/Thimphu",
"Asia/Tokyo",
"Asia/Ulaanbaatar",
"Asia/Urumqi",
"Asia/Vientiane",
"Asia/Vladivostok",
"Asia/Yakutsk",
"Asia/Yekaterinburg",
"Asia/Yerevan",
"Atlantic/Azores",
"Atlantic/Bermuda",
"Atlantic/Canary",
"Atlantic/Cape_Verde",
"Atlantic/Faroe",
"Atlantic/Madeira",
"Atlantic/Reykjavik",
"Atlantic/South_Georgia",
"Atlantic/Stanley",
"Atlantic/St_Helena",
"Australia/Adelaide",
"Australia/Brisbane",
"Australia/Broken_Hill",
"Australia/Currie",
"Australia/Darwin",
"Australia/Eucla",
"Australia/Hobart",
"Australia/Lindeman",
"Australia/Lord_Howe",
"Australia/Melbourne",
"Australia/Perth",
"Australia/Sydney",
"Europe/Amsterdam",
"Europe/Andorra",
"Europe/Athens",
"Europe/Belgrade",
"Europe/Berlin",
"Europe/Bratislava",
"Europe/Brussels",
"Europe/Bucharest",
"Europe/Budapest",
"Europe/Chisinau",
"Europe/Copenhagen",
"Europe/Dublin",
"Europe/Gibraltar",
"Europe/Guernsey",
"Europe/Helsinki",
"Europe/Isle_of_Man",
"Europe/Istanbul",
"Europe/Jersey",
"Europe/Kaliningrad",
"Europe/Kiev",
"Europe/Lisbon",
"Europe/Ljubljana",
"Europe/London",
"Europe/Luxembourg",
"Europe/Madrid",
"Europe/Malta",
"Europe/Mariehamn",
"Europe/Minsk",
"Europe/Monaco",
"Europe/Moscow",
"Europe/Oslo",
"Europe/Paris",
"Europe/Podgorica",
"Europe/Prague",
"Europe/Riga",
"Europe/Rome",
"Europe/Samara",
"Europe/San_Marino",
"Europe/Sarajevo",
"Europe/Simferopol",
"Europe/Skopje",
"Europe/Sofia",
"Europe/Stockholm",
"Europe/Tallinn",
"Europe/Tirane",
"Europe/Uzhgorod",
"Europe/Vaduz",
"Europe/Vatican",
"Europe/Vienna",
"Europe/Vilnius",
"Europe/Volgograd",
"Europe/Warsaw",
"Europe/Zagreb",
"Europe/Zaporozhye",
"Europe/Zurich",
"Indian/Antananarivo",
"Indian/Chagos",
"Indian/Christmas",
"Indian/Cocos",
"Indian/Comoro",
"Indian/Kerguelen",
"Indian/Mahe",
"Indian/Maldives",
"Indian/Mauritius",
"Indian/Mayotte",
"Indian/Reunion",
"Pacific/Apia",
"Pacific/Auckland",
"Pacific/Chatham",
"Pacific/Easter",
"Pacific/Efate",
"Pacific/Enderbury",
"Pacific/Fakaofo",
"Pacific/Fiji",
"Pacific/Funafuti",
"Pacific/Galapagos",
"Pacific/Gambier",
"Pacific/Guadalcanal",
"Pacific/Guam",
"Pacific/Honolulu",
"Pacific/Johnston",
"Pacific/Kiritimati",
"Pacific/Kosrae",
"Pacific/Kwajalein",
"Pacific/Majuro",
"Pacific/Marquesas",
"Pacific/Midway",
"Pacific/Nauru",
"Pacific/Niue",
"Pacific/Norfolk",
"Pacific/Noumea",
"Pacific/Pago_Pago",
"Pacific/Palau",
"Pacific/Pitcairn",
"Pacific/Ponape",
"Pacific/Port_Moresby",
"Pacific/Rarotonga",
"Pacific/Saipan",
"Pacific/Tahiti",
"Pacific/Tarawa",
"Pacific/Tongatapu",
"Pacific/Truk",
"Pacific/Wake",
"Pacific/Wallis",
)
"options" => self::timezones(),
"help" => "Time zone of the server",
"popover" => array(
"title" => "Time zone",
"content" => "Time zone of the server",
),
)));
$oMorpho->add(new \Formal\Element\Checkbox(array(
@ -578,7 +177,11 @@ class Config extends \Flake\Core\Model\NoDb {
$oMorpho->add(new \Formal\Element\Checkbox(array(
"prop" => "BAIKAL_ADMIN_ENABLED",
"label" => "Enable Web Admin"
"label" => "Enable Web Admin",
"popover" => array(
"title" => "Warning !",
"content" => "If disabled, you'll lose access to this very admin interface !",
),
)));
$oMorpho->add(new \Formal\Element\Checkbox(array(
@ -600,10 +203,12 @@ class Config extends \Flake\Core\Model\NoDb {
$oMorpho->add(new \Formal\Element\Password(array(
"prop" => "BAIKAL_ADMIN_PASSWORDHASH_CONFIRM",
"label" => "Web admin password confirm",
"label" => "Web admin password confirmation",
"placeholder" => $sNotice,
"validation" => "sameas:BAIKAL_ADMIN_PASSWORDHASH",
)));
return $oMorpho;
}
@ -636,23 +241,90 @@ class Config extends \Flake\Core\Model\NoDb {
for($k = ($iLines - 1); $k >= 0; $k--) {
if(preg_match($sPattern, $aLines[$k])) {
debug($aLines[$k]);
# Found the last matching line
$sValue = $this->aData[$prop];
$bCalculated = (isset($this->aConstants[$prop]["type"]["calculated"]) && $this->aConstants[$prop]["type"]["calculated"] === TRUE);
switch($this->aConstants[$prop]["type"]) {
case "string": {
$sValue = '"' . addcslashes($sValue, "\"\\\0\n\r") . '"'; # Add quotes, and escape " and all string-termination chars
break;
}
case "integer": {
$sValue = intval($sValue); # Cast as integer
break;
}
case "boolean": {
if(intval($sValue) === 1) { # Note as a BOOLEAN PHP constant
$sValue = "TRUE";
} else {
$sValue = "FALSE";
}
break;
}
default: {
$sValue = '""';
break;
}
}
$aLines[$k] = "define(\"" . $prop . "\", " . $sValue . ");";
}
}
}
$sLines = implode("\n", $aLines);
$sSandboxedCode = str_replace(array("<?php", "<?", "?>"), "", $sLines);
$sRand = (string)rand();
$sCode = "if(0) {" . $sSandboxedCode . "}; echo '" . $sRand . "';";
ob_start();
eval($sCode);
$sRes = ob_get_contents();
ob_end_clean();
if($sRes !== $sRand) {
throw new \Exception("Parse error in new config file. Aborting, nothing has been changed.");
}
# We asserted that the syntax is OK;
# We now check that all the constants are present, and with the right value
$aNewConfig = $this->parseConfig($sLines);
$aWrittenConfig = $this->aData;
asort($aNewConfig);
asort($aWrittenConfig);
if($aNewConfig != $aWrittenConfig) {
throw new \Exception("New config does not correspond to expected config. Aborting, nothing has been changed.");
}
file_put_contents($this->sConfigFilePath, $sLines);
}
public function set($sProp, $sValue) {
if($sProp === "BAIKAL_ADMIN_PASSWORDHASH_CONFIRM") {
return;
if($sProp === "BAIKAL_ADMIN_PASSWORDHASH" || $sProp === "BAIKAL_ADMIN_PASSWORDHASH_CONFIRM") {
# Special handling for password and passwordconfirm
if($sProp === "BAIKAL_ADMIN_PASSWORDHASH" && $sValue !== "") {
parent::set(
"BAIKAL_ADMIN_PASSWORDHASH",
\BaikalAdmin\Core\Auth::hashAdminPassword($sValue)
);
}
return $this;
}
parent::set($sProp, $sValue);
}
public function get($sProp) {
if($sProp === "BAIKAL_ADMIN_PASSWORDHASH_CONFIRM") {
return "yyyy";
if($sProp === "BAIKAL_ADMIN_PASSWORDHASH" || $sProp === "BAIKAL_ADMIN_PASSWORDHASH_CONFIRM") {
return "";
}
return parent::get($sProp);
@ -661,4 +333,419 @@ class Config extends \Flake\Core\Model\NoDb {
public function destroy() {
}
protected static function timezones() {
$aZones = array(
"Africa/Abidjan",
"Africa/Accra",
"Africa/Addis_Ababa",
"Africa/Algiers",
"Africa/Asmara",
"Africa/Bamako",
"Africa/Bangui",
"Africa/Banjul",
"Africa/Bissau",
"Africa/Blantyre",
"Africa/Brazzaville",
"Africa/Bujumbura",
"Africa/Cairo",
"Africa/Casablanca",
"Africa/Ceuta",
"Africa/Conakry",
"Africa/Dakar",
"Africa/Dar_es_Salaam",
"Africa/Djibouti",
"Africa/Douala",
"Africa/El_Aaiun",
"Africa/Freetown",
"Africa/Gaborone",
"Africa/Harare",
"Africa/Johannesburg",
"Africa/Juba",
"Africa/Kampala",
"Africa/Khartoum",
"Africa/Kigali",
"Africa/Kinshasa",
"Africa/Lagos",
"Africa/Libreville",
"Africa/Lome",
"Africa/Luanda",
"Africa/Lubumbashi",
"Africa/Lusaka",
"Africa/Malabo",
"Africa/Maputo",
"Africa/Maseru",
"Africa/Mbabane",
"Africa/Mogadishu",
"Africa/Monrovia",
"Africa/Nairobi",
"Africa/Ndjamena",
"Africa/Niamey",
"Africa/Nouakchott",
"Africa/Ouagadougou",
"Africa/Porto-Novo",
"Africa/Sao_Tome",
"Africa/Tripoli",
"Africa/Tunis",
"Africa/Windhoek",
"America/Adak",
"America/Anchorage",
"America/Anguilla",
"America/Antigua",
"America/Araguaina",
"America/Argentina/Buenos_Aires",
"America/Argentina/Catamarca",
"America/Argentina/Cordoba",
"America/Argentina/Jujuy",
"America/Argentina/La_Rioja",
"America/Argentina/Mendoza",
"America/Argentina/Rio_Gallegos",
"America/Argentina/Salta",
"America/Argentina/San_Juan",
"America/Argentina/San_Luis",
"America/Argentina/Tucuman",
"America/Argentina/Ushuaia",
"America/Aruba",
"America/Asuncion",
"America/Atikokan",
"America/Bahia",
"America/Barbados",
"America/Belem",
"America/Belize",
"America/Blanc-Sablon",
"America/Boa_Vista",
"America/Bogota",
"America/Boise",
"America/Cambridge_Bay",
"America/Campo_Grande",
"America/Cancun",
"America/Caracas",
"America/Cayenne",
"America/Cayman",
"America/Chicago",
"America/Chihuahua",
"America/Costa_Rica",
"America/Cuiaba",
"America/Curacao",
"America/Danmarkshavn",
"America/Dawson",
"America/Dawson_Creek",
"America/Denver",
"America/Detroit",
"America/Dominica",
"America/Edmonton",
"America/Eirunepe",
"America/El_Salvador",
"America/Felipe_Carrillo",
"America/Fortaleza",
"America/Glace_Bay",
"America/Godthab",
"America/Goose_Bay",
"America/Grand_Turk",
"America/Grenada",
"America/Guadeloupe",
"America/Guatemala",
"America/Guayaquil",
"America/Guyana",
"America/Halifax",
"America/Havana",
"America/Hermosillo",
"America/Indiana/Indianapolis",
"America/Indiana/Knox",
"America/Indiana/Marengo",
"America/Indiana/Petersburg",
"America/Indiana/Tell_City",
"America/Indiana/Vevay",
"America/Indiana/Vincennes",
"America/Indiana/Winamac",
"America/Inuvik",
"America/Iqaluit",
"America/Jamaica",
"America/Juneau",
"America/Kentucky/Louisville",
"America/Kentucky/Monticello",
"America/La_Paz",
"America/Lima",
"America/Los_Angeles",
"America/Maceio",
"America/Managua",
"America/Manaus",
"America/Marigot",
"America/Martinique",
"America/Matamoros",
"America/Mazatlan",
"America/Menominee",
"America/Merida",
"America/Mexico_City",
"America/Miquelon",
"America/Moncton",
"America/Monterrey",
"America/Montevideo",
"America/Montreal",
"America/Montserrat",
"America/Nassau",
"America/New_York",
"America/Nipigon",
"America/Nome",
"America/Noronha",
"America/North_Dakota/Center",
"America/North_Dakota/New_Salem",
"America/Ojinaga",
"America/Panama",
"America/Pangnirtung",
"America/Paramaribo",
"America/Phoenix",
"America/Port-au-Prince",
"America/Porto_Velho",
"America/Port_of_Spain",
"America/Puerto_Rico",
"America/Rainy_River",
"America/Rankin_Inlet",
"America/Recife",
"America/Regina",
"America/Resolute",
"America/Rio_Branco",
"America/Santarem",
"America/Santa_Isabel",
"America/Santiago",
"America/Santo_Domingo",
"America/Sao_Paulo",
"America/Scoresbysund",
"America/Shiprock",
"America/St_Barthelemy",
"America/St_Johns",
"America/St_Kitts",
"America/St_Lucia",
"America/St_Thomas",
"America/St_Vincent",
"America/Swift_Current",
"America/Tegucigalpa",
"America/Thule",
"America/Thunder_Bay",
"America/Tijuana",
"America/Toronto",
"America/Tortola",
"America/Vancouver",
"America/Whitehorse",
"America/Winnipeg",
"America/Yakutat",
"America/Yellowknife",
"Antarctica/Casey",
"Antarctica/Davis",
"Antarctica/DumontDUrville",
"Antarctica/Mawson",
"Antarctica/McMurdo",
"Antarctica/Palmer",
"Antarctica/Rothera",
"Antarctica/South_Pole",
"Antarctica/Syowa",
"Antarctica/Vostok",
"Arctic/Longyearbyen",
"Asia/Aden",
"Asia/Almaty",
"Asia/Amman",
"Asia/Anadyr",
"Asia/Aqtau",
"Asia/Aqtobe",
"Asia/Ashgabat",
"Asia/Baghdad",
"Asia/Bahrain",
"Asia/Baku",
"Asia/Bangkok",
"Asia/Beirut",
"Asia/Bishkek",
"Asia/Brunei",
"Asia/Choibalsan",
"Asia/Chongqing",
"Asia/Colombo",
"Asia/Damascus",
"Asia/Dhaka",
"Asia/Dili",
"Asia/Dubai",
"Asia/Dushanbe",
"Asia/Gaza",
"Asia/Harbin",
"Asia/Hong_Kong",
"Asia/Hovd",
"Asia/Ho_Chi_Minh",
"Asia/Irkutsk",
"Asia/Jakarta",
"Asia/Jayapura",
"Asia/Jerusalem",
"Asia/Kabul",
"Asia/Kamchatka",
"Asia/Karachi",
"Asia/Kashgar",
"Asia/Kathmandu",
"Asia/Kolkata",
"Asia/Krasnoyarsk",
"Asia/Kuala_Lumpur",
"Asia/Kuching",
"Asia/Kuwait",
"Asia/Macau",
"Asia/Magadan",
"Asia/Makassar",
"Asia/Manila",
"Asia/Muscat",
"Asia/Nicosia",
"Asia/Novokuznetsk",
"Asia/Novosibirsk",
"Asia/Omsk",
"Asia/Oral",
"Asia/Phnom_Penh",
"Asia/Pontianak",
"Asia/Pyongyang",
"Asia/Qatar",
"Asia/Qyzylorda",
"Asia/Rangoon",
"Asia/Riyadh",
"Asia/Sakhalin",
"Asia/Samarkand",
"Asia/Seoul",
"Asia/Shanghai",
"Asia/Singapore",
"Asia/Taipei",
"Asia/Tashkent",
"Asia/Tbilisi",
"Asia/Tehran",
"Asia/Thimphu",
"Asia/Tokyo",
"Asia/Ulaanbaatar",
"Asia/Urumqi",
"Asia/Vientiane",
"Asia/Vladivostok",
"Asia/Yakutsk",
"Asia/Yekaterinburg",
"Asia/Yerevan",
"Atlantic/Azores",
"Atlantic/Bermuda",
"Atlantic/Canary",
"Atlantic/Cape_Verde",
"Atlantic/Faroe",
"Atlantic/Madeira",
"Atlantic/Reykjavik",
"Atlantic/South_Georgia",
"Atlantic/Stanley",
"Atlantic/St_Helena",
"Australia/Adelaide",
"Australia/Brisbane",
"Australia/Broken_Hill",
"Australia/Currie",
"Australia/Darwin",
"Australia/Eucla",
"Australia/Hobart",
"Australia/Lindeman",
"Australia/Lord_Howe",
"Australia/Melbourne",
"Australia/Perth",
"Australia/Sydney",
"Europe/Amsterdam",
"Europe/Andorra",
"Europe/Athens",
"Europe/Belgrade",
"Europe/Berlin",
"Europe/Bratislava",
"Europe/Brussels",
"Europe/Bucharest",
"Europe/Budapest",
"Europe/Chisinau",
"Europe/Copenhagen",
"Europe/Dublin",
"Europe/Gibraltar",
"Europe/Guernsey",
"Europe/Helsinki",
"Europe/Isle_of_Man",
"Europe/Istanbul",
"Europe/Jersey",
"Europe/Kaliningrad",
"Europe/Kiev",
"Europe/Lisbon",
"Europe/Ljubljana",
"Europe/London",
"Europe/Luxembourg",
"Europe/Madrid",
"Europe/Malta",
"Europe/Mariehamn",
"Europe/Minsk",
"Europe/Monaco",
"Europe/Moscow",
"Europe/Oslo",
"Europe/Paris",
"Europe/Podgorica",
"Europe/Prague",
"Europe/Riga",
"Europe/Rome",
"Europe/Samara",
"Europe/San_Marino",
"Europe/Sarajevo",
"Europe/Simferopol",
"Europe/Skopje",
"Europe/Sofia",
"Europe/Stockholm",
"Europe/Tallinn",
"Europe/Tirane",
"Europe/Uzhgorod",
"Europe/Vaduz",
"Europe/Vatican",
"Europe/Vienna",
"Europe/Vilnius",
"Europe/Volgograd",
"Europe/Warsaw",
"Europe/Zagreb",
"Europe/Zaporozhye",
"Europe/Zurich",
"Indian/Antananarivo",
"Indian/Chagos",
"Indian/Christmas",
"Indian/Cocos",
"Indian/Comoro",
"Indian/Kerguelen",
"Indian/Mahe",
"Indian/Maldives",
"Indian/Mauritius",
"Indian/Mayotte",
"Indian/Reunion",
"Pacific/Apia",
"Pacific/Auckland",
"Pacific/Chatham",
"Pacific/Easter",
"Pacific/Efate",
"Pacific/Enderbury",
"Pacific/Fakaofo",
"Pacific/Fiji",
"Pacific/Funafuti",
"Pacific/Galapagos",
"Pacific/Gambier",
"Pacific/Guadalcanal",
"Pacific/Guam",
"Pacific/Honolulu",
"Pacific/Johnston",
"Pacific/Kiritimati",
"Pacific/Kosrae",
"Pacific/Kwajalein",
"Pacific/Majuro",
"Pacific/Marquesas",
"Pacific/Midway",
"Pacific/Nauru",
"Pacific/Niue",
"Pacific/Norfolk",
"Pacific/Noumea",
"Pacific/Pago_Pago",
"Pacific/Palau",
"Pacific/Pitcairn",
"Pacific/Ponape",
"Pacific/Port_Moresby",
"Pacific/Rarotonga",
"Pacific/Saipan",
"Pacific/Tahiti",
"Pacific/Tarawa",
"Pacific/Tongatapu",
"Pacific/Truk",
"Pacific/Wake",
"Pacific/Wallis",
);
reset($aZones);
return $aZones;
}
}

View file

@ -204,13 +204,21 @@ class User extends \Flake\Core\Model\Db {
$oMorpho->add(new \Formal\Element\Text(array(
"prop" => "username",
"label" => "Username",
"validation" => "required,tokenid,unique"
"validation" => "required,tokenid,unique",
"popover" => array(
"title" => "Username",
"content" => "The login for this user account.<br />It has to be unique.",
)
)));
$oMorpho->add(new \Formal\Element\Text(array(
"prop" => "displayname",
"label" => "Display name",
"validation" => "required"
"validation" => "required",
"popover" => array(
"title" => "Display name",
"content" => "This is the name that will be displayed in your CalDAV/CardDAV clients.",
)
)));
$oMorpho->add(new \Formal\Element\Text(array(
@ -231,11 +239,22 @@ class User extends \Flake\Core\Model\Db {
)));
if($this->floating()) {
$oMorpho->element("username")->setOption("help", "Allowed characters are digits, lowercase letters and the dash symbol '-'.");
$oMorpho->element("password")->setOption("validation", "required");
} else {
$sNotice = "-- Leave empty to keep current password --";
$oMorpho->element("username")->setOption("readonly", true);
$oMorpho->element("password")->setOption("popover", array(
"title" => "Password",
"content" => "Write something here only if you want to change the user password."
));
$oMorpho->element("passwordconfirm")->setOption("popover", array(
"title" => "Confirm password",
"content" => "Write something here only if you want to change the user password."
));
$oMorpho->element("password")->setOption("placeholder", $sNotice);
$oMorpho->element("passwordconfirm")->setOption("placeholder", $sNotice);
}

View file

@ -11,7 +11,7 @@
<link href="res/core/BaikalAdmin/Templates/Page/style.css" rel="stylesheet" />
<link href="res/core/BaikalAdmin/GlyphiconsPro/glyphpro.css" rel="stylesheet" />
<link href="res/core/BaikalAdmin/GlyphiconsPro/glyphpro-2x.css" rel="stylesheet" />
<!--link href="res/core/TwitterBootstrap/css/bootstrap-responsive.css" rel="stylesheet" /-->
<link href="res/core/TwitterBootstrap/css/bootstrap-responsive.css" rel="stylesheet" />
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
@ -26,6 +26,9 @@
{Payload}
</div> <!-- /container -->
<!--a rel="tooltip" title="first tooltip">hello</a>
<a class="popover" title="first popover" data-content="hello&lt;br&gt;World">hello</a-->
<div class="mq0"></div>
<div class="mq1"></div>
<div class="mq2"></div>
@ -41,6 +44,15 @@
<!-- Placed at the end of the document so the pages load faster -->
<script src="res/core/TwitterBootstrap/js/jquery-1.7.1.min.js"></script>
<script src="res/core/TwitterBootstrap/js/bootstrap.min.js"></script>
<script src="res/core/TwitterBootstrap/js/bootstrap-tooltip.js"></script>
<script src="res/core/TwitterBootstrap/js/bootstrap-popover.js"></script>
<script type="text/javascript">
$("[rel=tooltip]").tooltip();
$(".popover-hover").popover();
$(".popover-focus").popover({
trigger: 'focus'
});
</script>
{javascript}
</body>
</html>

View file

@ -32,7 +32,7 @@ class Message {
public static function error($sMessage) {
$sHtml =<<<HTML
<div id="confirm" class="alert alert-block alert-error">
<div id="message" class="alert alert-block alert-error">
<h3 class="alert-heading">Validation error</h3>
{$sMessage}
</div>
@ -42,7 +42,7 @@ HTML;
public static function notice($sMessage) {
$sHtml =<<<HTML
<div id="info" class="alert alert-info">
<div id="message" class="alert alert-info">
<a class="close" data-dismiss="alert" href="#">&times;</a>
{$sMessage}
</div>
@ -52,7 +52,7 @@ HTML;
public static function warningConfirmMessage($sHeader, $sDescription, $sActionUrl, $sActionLabel, $sCancelUrl, $sCancelLabel="Cancel") {
$sHtml =<<<HTML
<div id="confirm" class="alert alert-block alert-error">
<div id="message" class="alert alert-block alert-error">
<!--a class="close" data-dismiss="alert" href="#">&times;</a-->
<h3 class="alert-heading">{$sHeader}</h3>
{$sDescription}

View file

@ -33,6 +33,8 @@ abstract class Element {
"validation" => "",
"error" => FALSE,
"placeholder" => "",
"help" => "",
"popover" => "",
);
protected $sValue = "";

View file

@ -43,6 +43,8 @@ class Checkbox extends \Formal\Element {
$checked = ($value === TRUE ? " checked=\"checked\" " : "");
$label = $this->option("label");
$prop = $this->option("prop");
$helpblock = "";
$popover = "";
if($this->option("readonly") === TRUE) {
$inputclass .= " disabled";
@ -53,12 +55,22 @@ class Checkbox extends \Formal\Element {
$groupclass .= " error";
}
if(($sHelp = trim($this->option("help"))) !== "") {
$helpblock = "<p class=\"help-block\">" . $sHelp . "</p>";
}
if(($aPopover = $this->option("popover")) !== "") {
$inputclass .= " popover-hover ";
$popover = " title=\"" . htmlspecialchars($aPopover["title"]) . "\" ";
$popover .= " data-content=\"" . htmlspecialchars($aPopover["content"]) . "\" ";
}
$sHtml =<<<HTML
<div class="control-group{$groupclass}">
<label class="control-label" for="{$prop}">{$label}</label>
<div class="controls">
<input type="checkbox" class="input-xlarge{$inputclass}" id="{$prop}" name="{$prop}" value="1"{$checked}{$disabled}/>
<input type="checkbox" class="input-xlarge{$inputclass}" id="{$prop}" name="{$prop}" value="1"{$checked}{$disabled}{$popover}/>
{$helpblock}
</div>
</div>
HTML;

View file

@ -37,6 +37,8 @@ class Listbox extends \Formal\Element {
$value = $this->value();
$label = $this->option("label");
$prop = $this->option("prop");
$helpblock = "";
$popover = "";
if($this->option("readonly") === TRUE) {
$inputclass .= " disabled";
@ -52,6 +54,16 @@ class Listbox extends \Formal\Element {
throw new \Exception("\Formal\Element\Listbox->render(): 'options' has to be an array.");
}
if(($sHelp = trim($this->option("help"))) !== "") {
$helpblock = "<p class=\"help-block\">" . $sHelp . "</p>";
}
if(($aPopover = $this->option("popover")) !== "") {
$inputclass .= " popover-focus ";
$popover = " title=\"" . htmlspecialchars($aPopover["title"]) . "\" ";
$popover .= " data-content=\"" . htmlspecialchars($aPopover["content"]) . "\" ";
}
$clientvalue = htmlspecialchars($value);
$aRenderedOptions = array();
@ -80,9 +92,10 @@ class Listbox extends \Formal\Element {
<div class="control-group{$groupclass}">
<label class="control-label" for="{$prop}">{$label}</label>
<div class="controls">
<select class="{$inputclass}" id="{$prop}" name="{$prop}"{$disabled}>
<select class="{$inputclass}" id="{$prop}" name="{$prop}"{$disabled}{$popover}>
{$sRenderedOptions}
</select>
{$helpblock}
</div>
</div>
HTML;

View file

@ -41,7 +41,9 @@ class Text extends \Formal\Element {
$value = $this->value();
$label = $this->option("label");
$prop = $this->option("prop");
$placeholder = $this->option("placeholder");
$placeholder = "";
$helpblock = "";
$popover = "";
if($this->option("readonly") === TRUE) {
$inputclass .= " disabled";
@ -60,11 +62,22 @@ class Text extends \Formal\Element {
$sInputType = $this->inputtype();
if(($sHelp = trim($this->option("help"))) !== "") {
$helpblock = "<p class=\"help-block\">" . $sHelp . "</p>";
}
if(($aPopover = $this->option("popover")) !== "") {
$inputclass .= " popover-focus ";
$popover = " title=\"" . htmlspecialchars($aPopover["title"]) . "\" ";
$popover .= " data-content=\"" . htmlspecialchars($aPopover["content"]) . "\" ";
}
$sHtml =<<<HTML
<div class="control-group{$groupclass}">
<label class="control-label" for="{$prop}">{$label}</label>
<div class="controls">
<input type="{$sInputType}" class="input-xlarge{$inputclass}" id="{$prop}" name="{$prop}" value="{$clientvalue}"{$disabled}{$placeholder}/>
<input type="{$sInputType}" class="input-xlarge{$inputclass}" id="{$prop}" name="{$prop}" value="{$clientvalue}"{$disabled}{$placeholder}{$popover}/>
{$helpblock}
</div>
</div>
HTML;

View file

@ -0,0 +1,95 @@
/* ===========================================================
* bootstrap-popover.js v2.0.2
* http://twitter.github.com/bootstrap/javascript.html#popovers
* ===========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =========================================================== */
!function( $ ) {
"use strict"
var Popover = function ( element, options ) {
this.init('popover', element, options)
}
/* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js
========================================== */
Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, {
constructor: Popover
, setContent: function () {
var $tip = this.tip()
, title = this.getTitle()
, content = this.getContent()
$tip.find('.popover-title')[ $.type(title) == 'object' ? 'append' : 'html' ](title)
$tip.find('.popover-content > *')[ $.type(content) == 'object' ? 'append' : 'html' ](content)
$tip.removeClass('fade top bottom left right in')
}
, hasContent: function () {
return this.getTitle() || this.getContent()
}
, getContent: function () {
var content
, $e = this.$element
, o = this.options
content = $e.attr('data-content')
|| (typeof o.content == 'function' ? o.content.call($e[0]) : o.content)
content = content.toString().replace(/(^\s*|\s*$)/, "")
return content
}
, tip: function() {
if (!this.$tip) {
this.$tip = $(this.options.template)
}
return this.$tip
}
})
/* POPOVER PLUGIN DEFINITION
* ======================= */
$.fn.popover = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('popover')
, options = typeof option == 'object' && option
if (!data) $this.data('popover', (data = new Popover(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.popover.Constructor = Popover
$.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, {
placement: 'right'
, content: ''
, template: '<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'
})
}( window.jQuery );

View file

@ -0,0 +1,270 @@
/* ===========================================================
* bootstrap-tooltip.js v2.0.2
* http://twitter.github.com/bootstrap/javascript.html#tooltips
* Inspired by the original jQuery.tipsy by Jason Frame
* ===========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================== */
!function( $ ) {
"use strict"
/* TOOLTIP PUBLIC CLASS DEFINITION
* =============================== */
var Tooltip = function ( element, options ) {
this.init('tooltip', element, options)
}
Tooltip.prototype = {
constructor: Tooltip
, init: function ( type, element, options ) {
var eventIn
, eventOut
this.type = type
this.$element = $(element)
this.options = this.getOptions(options)
this.enabled = true
if (this.options.trigger != 'manual') {
eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus'
eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur'
this.$element.on(eventIn, this.options.selector, $.proxy(this.enter, this))
this.$element.on(eventOut, this.options.selector, $.proxy(this.leave, this))
}
this.options.selector ?
(this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
this.fixTitle()
}
, getOptions: function ( options ) {
options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data())
if (options.delay && typeof options.delay == 'number') {
options.delay = {
show: options.delay
, hide: options.delay
}
}
return options
}
, enter: function ( e ) {
var self = $(e.currentTarget)[this.type](this._options).data(this.type)
if (!self.options.delay || !self.options.delay.show) {
self.show()
} else {
self.hoverState = 'in'
setTimeout(function() {
if (self.hoverState == 'in') {
self.show()
}
}, self.options.delay.show)
}
}
, leave: function ( e ) {
var self = $(e.currentTarget)[this.type](this._options).data(this.type)
if (!self.options.delay || !self.options.delay.hide) {
self.hide()
} else {
self.hoverState = 'out'
setTimeout(function() {
if (self.hoverState == 'out') {
self.hide()
}
}, self.options.delay.hide)
}
}
, show: function () {
var $tip
, inside
, pos
, actualWidth
, actualHeight
, placement
, tp
if (this.hasContent() && this.enabled) {
$tip = this.tip()
this.setContent()
if (this.options.animation) {
$tip.addClass('fade')
}
placement = typeof this.options.placement == 'function' ?
this.options.placement.call(this, $tip[0], this.$element[0]) :
this.options.placement
inside = /in/.test(placement)
$tip
.remove()
.css({ top: 0, left: 0, display: 'block' })
.appendTo(inside ? this.$element : document.body)
pos = this.getPosition(inside)
actualWidth = $tip[0].offsetWidth
actualHeight = $tip[0].offsetHeight
switch (inside ? placement.split(' ')[1] : placement) {
case 'bottom':
tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
break
case 'top':
tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
break
case 'left':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
break
case 'right':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
break
}
$tip
.css(tp)
.addClass(placement)
.addClass('in')
}
}
, setContent: function () {
var $tip = this.tip()
$tip.find('.tooltip-inner').html(this.getTitle())
$tip.removeClass('fade in top bottom left right')
}
, hide: function () {
var that = this
, $tip = this.tip()
$tip.removeClass('in')
function removeWithAnimation() {
var timeout = setTimeout(function () {
$tip.off($.support.transition.end).remove()
}, 500)
$tip.one($.support.transition.end, function () {
clearTimeout(timeout)
$tip.remove()
})
}
$.support.transition && this.$tip.hasClass('fade') ?
removeWithAnimation() :
$tip.remove()
}
, fixTitle: function () {
var $e = this.$element
if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
$e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
}
}
, hasContent: function () {
return this.getTitle()
}
, getPosition: function (inside) {
return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), {
width: this.$element[0].offsetWidth
, height: this.$element[0].offsetHeight
})
}
, getTitle: function () {
var title
, $e = this.$element
, o = this.options
title = $e.attr('data-original-title')
|| (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
title = (title || '').toString().replace(/(^\s*|\s*$)/, "")
return title
}
, tip: function () {
return this.$tip = this.$tip || $(this.options.template)
}
, validate: function () {
if (!this.$element[0].parentNode) {
this.hide()
this.$element = null
this.options = null
}
}
, enable: function () {
this.enabled = true
}
, disable: function () {
this.enabled = false
}
, toggleEnabled: function () {
this.enabled = !this.enabled
}
, toggle: function () {
this[this.tip().hasClass('in') ? 'hide' : 'show']()
}
}
/* TOOLTIP PLUGIN DEFINITION
* ========================= */
$.fn.tooltip = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('tooltip')
, options = typeof option == 'object' && option
if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.tooltip.Constructor = Tooltip
$.fn.tooltip.defaults = {
animation: true
, delay: 0
, selector: false
, placement: 'top'
, trigger: 'hover'
, title: ''
, template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
}
}( window.jQuery );

Binary file not shown.