Working on web admin

This commit is contained in:
Jérôme Schneider 2012-04-16 13:17:12 +02:00
parent 2133a265d1
commit 35511b68bb
31 changed files with 1566 additions and 57 deletions

View file

@ -0,0 +1,105 @@
<?php
/***************************************************************
* Copyright notice
*
* (c) 2012 Jérôme Schneider <mail@jeromeschneider.fr>
* All rights reserved
*
* http://baikal.codr.fr
*
* This script is part of the Baïkal Server project. The Baïkal
* Server project is free software; you can redistribute it
* and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
namespace Baikal\Model;
class AddressBook extends \Flake\Core\Model\Db {
const DATATABLE = "addressbooks";
const PRIMARYKEY = "id";
const LABELFIELD = "displayname";
protected $aData = array(
"principaluri" => "",
"displayname" => "",
"uri" => "",
"description" => "",
"ctag" => "",
);
public static function humanName() {
return "Address Book";
}
public static function icon() {
return "icon-book";
}
public static function mediumicon() {
return "glyph-adress-book";
}
public static function bigicon() {
return "glyph2x-adress-book";
}
public function getContactsBaseRequester() {
$oBaseRequester = \Baikal\Model\AddressBook\Contact::getBaseRequester();
$oBaseRequester->addClauseEquals(
"addressbookid",
$this->get("id")
);
return $oBaseRequester;
}
public function formMorphologyForThisModelInstance() {
$oMorpho = new \Formal\Form\Morphology();
$oMorpho->add(new \Formal\Element\Text(array(
"prop" => "uri",
"label" => "Address Book token ID",
"validation" => "required,tokenid"
)));
$oMorpho->add(new \Formal\Element\Text(array(
"prop" => "displayname",
"label" => "Display name",
"validation" => "required"
)));
$oMorpho->add(new \Formal\Element\Text(array(
"prop" => "description",
"label" => "Description",
"validation" => "required"
)));
if(!$this->floating()) {
$oMorpho->element("uri")->setOption("readonly", TRUE);
}
return $oMorpho;
}
public function destroy() {
$oContacts = $this->getContactsBaseRequester()->execute();
foreach($oContacts as $contact) {
$contact->destroy();
}
parent::destroy();
}
}

View file

@ -0,0 +1,40 @@
<?php
/***************************************************************
* Copyright notice
*
* (c) 2012 Jérôme Schneider <mail@jeromeschneider.fr>
* All rights reserved
*
* http://baikal.codr.fr
*
* This script is part of the Baïkal Server project. The Baïkal
* Server project is free software; you can redistribute it
* and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
namespace Baikal\Model\AddressBook;
class Contact extends \Flake\Core\Model\Db {
const DATATABLE = "cards";
const PRIMARYKEY = "id";
const LABELFIELD = "uri";
protected $aData = array(
"carddata" => "",
"uri" => "",
"addressbookid" => "",
"lastmodified" => "",
);
}

View file

@ -55,6 +55,16 @@ class Calendar extends \Flake\Core\Model\Db {
return "glyph2x-calendar";
}
public function getEventsBaseRequester() {
$oBaseRequester = \Baikal\Model\Calendar\Event::getBaseRequester();
$oBaseRequester->addClauseEquals(
"calendarid",
$this->get("id")
);
return $oBaseRequester;
}
public function get($sPropName) {
if($sPropName === "todos") {
@ -133,4 +143,15 @@ class Calendar extends \Flake\Core\Model\Db {
public function isDefault() {
return $this->get("uri") === "default";
}
public function destroy() {
$oEvents = $this->getEventsBaseRequester()->execute();
foreach($oEvents as $event) {
$event->destroy();
}
parent::destroy();
}
}

View file

@ -0,0 +1,40 @@
<?php
/***************************************************************
* Copyright notice
*
* (c) 2012 Jérôme Schneider <mail@jeromeschneider.fr>
* All rights reserved
*
* http://baikal.codr.fr
*
* This script is part of the Baïkal Server project. The Baïkal
* Server project is free software; you can redistribute it
* and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
namespace Baikal\Model\Calendar;
class Event extends \Flake\Core\Model\Db {
const DATATABLE = "calendarobjects";
const PRIMARYKEY = "id";
const LABELFIELD = "uri";
protected $aData = array(
"calendardata" => "",
"uri" => "",
"calendarid" => "",
"lastmodified" => "",
);
}

View file

@ -0,0 +1,664 @@
<?php
/***************************************************************
* Copyright notice
*
* (c) 2012 Jérôme Schneider <mail@jeromeschneider.fr>
* All rights reserved
*
* http://baikal.codr.fr
*
* This script is part of the Baïkal Server project. The Baïkal
* Server project is free software; you can redistribute it
* and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
namespace Baikal\Model;
class Config extends \Flake\Core\Model\NoDb {
protected $sConfigFilePath = "";
protected $aConstants = array(
"BAIKAL_TIMEZONE" => array(
"type" => "string",
),
# "BAIKAL_URI" => array(
# "type" => "string",
# ),
"BAIKAL_CARD_ENABLED" => array(
"type" => "boolean",
),
"BAIKAL_CAL_ENABLED" => array(
"type" => "boolean",
),
"BAIKAL_ADMIN_ENABLED" => array(
"type" => "boolean",
),
"BAIKAL_STANDALONE_ALLOWED" => array(
"type" => "boolean",
),
"BAIKAL_STANDALONE_PORT" => array(
"type" => "integer",
),
"BAIKAL_ADMIN_PASSWORDHASH" => array(
"type" => "calculated",
)
);
protected $aData = array(
"BAIKAL_TIMEZONE" => "",
"BAIKAL_CARD_ENABLED" => "",
"BAIKAL_CAL_ENABLED" => "",
"BAIKAL_TIMEZONE" => "",
"BAIKAL_CARD_ENABLED" => "",
"BAIKAL_CAL_ENABLED" => "",
"BAIKAL_ADMIN_ENABLED" => "",
"BAIKAL_STANDALONE_ALLOWED" => "",
"BAIKAL_STANDALONE_PORT" => "",
"BAIKAL_ADMIN_PASSWORDHASH" => ""
);
public function __construct($sConfigFilePath) {
# Note: no call to parent::__construct() to avoid erasing $this->aData
$this->sConfigFilePath = $sConfigFilePath;
$aConfig = $this->parseConfig(
$this->getConfigAsString()
);
foreach(array_keys($this->aData) as $sProp) {
if(array_key_exists($sProp, $aConfig)) {
$this->aData[$sProp] = $aConfig[$sProp];
}
}
}
protected function getConfigAsString() {
$sContent = file_get_contents($this->sConfigFilePath);
return str_replace(LF . CR, LF, $sContent);
}
protected function parseConfig($sString) {
$aRes = array();
foreach(array_keys($this->aConstants) as $sConstant) {
$aConstant = $this->aConstants[$sConstant];
$aMatches = array();
$sPattern = '/\s*define\(\s*["|\']' . $sConstant . '["|\']\s*\,\s*(.*?)\s*\);\s*/ix';
$iNbRes = preg_match_all(
$sPattern,
$sString,
$aMatches
);
if($iNbRes === 1) {
# Exactly one match
# O would be not enough, and > 1, to much to handle properly
$sValue = $aMatches[1][0]; # first capture (.*?), first occurence (anyway, we asserted that there's only one)
switch($aConstant["type"]) {
case "string": {
$sValue = substr($sValue, 1, -1); # Strip quotes
break;
}
case "integer": {
$sValue = intval($sValue); # Integer
break;
}
case "boolean": {
if(in_array(strtoupper(trim($sValue)), array("1", "TRUE"))) {
$sValue = TRUE;
} else {
$sValue = FALSE;
}
break;
}
default: {
# nothing
break;
}
}
$aRes[$sConstant] = $sValue;
}
}
reset($aRes);
return $aRes;
}
public function writable() {
return (
@file_exists($this->sConfigFilePath) &&
@is_file($this->sConfigFilePath) &&
@is_writable($this->sConfigFilePath)
);
}
public function formMorphologyForThisModelInstance() {
$oMorpho = new \Formal\Form\Morphology();
$oMorpho->add(new \Formal\Element\Listbox(array(
"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",
)
)));
$oMorpho->add(new \Formal\Element\Checkbox(array(
"prop" => "BAIKAL_CARD_ENABLED",
"label" => "Enable CardDAV"
)));
$oMorpho->add(new \Formal\Element\Checkbox(array(
"prop" => "BAIKAL_CAL_ENABLED",
"label" => "Enable CalDAV"
)));
$oMorpho->add(new \Formal\Element\Checkbox(array(
"prop" => "BAIKAL_ADMIN_ENABLED",
"label" => "Enable Web Admin"
)));
$oMorpho->add(new \Formal\Element\Checkbox(array(
"prop" => "BAIKAL_STANDALONE_ALLOWED",
"label" => "Allow Standalone Baïkal execution"
)));
$oMorpho->add(new \Formal\Element\Text(array(
"prop" => "BAIKAL_STANDALONE_PORT",
"label" => "Standalone Baïkal port"
)));
$sNotice = "-- Leave empty to keep current password --";
$oMorpho->add(new \Formal\Element\Password(array(
"prop" => "BAIKAL_ADMIN_PASSWORDHASH",
"label" => "Web admin password",
"placeholder" => $sNotice,
)));
$oMorpho->add(new \Formal\Element\Password(array(
"prop" => "BAIKAL_ADMIN_PASSWORDHASH_CONFIRM",
"label" => "Web admin password confirm",
"placeholder" => $sNotice,
)));
return $oMorpho;
}
public static function icon() {
return "icon-cog";
}
public static function mediumicon() {
return "glyph-cogwheel";
}
public static function bigicon() {
return "glyph2x-cogwheel";
}
public function label() {
return "Baïkal Settings";
}
public function floating() {
return FALSE;
}
public function persist() {
$aLines = explode(LF, $this->getConfigAsString());
foreach(array_keys($this->aData) as $prop) {
$iLines = count($aLines);
$sPattern = '/\s*define\(\s*["|\']' . $prop . '["|\']\s*\,\s*(.*?)\s*\);\s*/ix';
for($k = ($iLines - 1); $k >= 0; $k--) {
if(preg_match($sPattern, $aLines[$k])) {
debug($aLines[$k]);
}
}
}
}
public function set($sProp, $sValue) {
if($sProp === "BAIKAL_ADMIN_PASSWORDHASH_CONFIRM") {
return;
}
parent::set($sProp, $sValue);
}
public function get($sProp) {
if($sProp === "BAIKAL_ADMIN_PASSWORDHASH_CONFIRM") {
return "yyyy";
}
return parent::get($sProp);
}
public function destroy() {
}
}

View file

@ -48,14 +48,24 @@ class User extends \Flake\Core\Model\Db {
->first();
}
public function getCalendarsBaseRequester() {
$oCalendarBaseRequester = \Baikal\Model\Calendar::getBaseRequester();
$oCalendarBaseRequester->addClauseEquals(
public function getAddressBooksBaseRequester() {
$oBaseRequester = \Baikal\Model\AddressBook::getBaseRequester();
$oBaseRequester->addClauseEquals(
"principaluri",
"principals/" . $this->get("username")
);
return $oCalendarBaseRequester;
return $oBaseRequester;
}
public function getCalendarsBaseRequester() {
$oBaseRequester = \Baikal\Model\Calendar::getBaseRequester();
$oBaseRequester->addClauseEquals(
"principaluri",
"principals/" . $this->get("username")
);
return $oBaseRequester;
}
public function initFloating() {
@ -144,6 +154,24 @@ class User extends \Flake\Core\Model\Db {
);
$oDefaultCalendar->persist();
# Creating default address book for user
$oDefaultAddressBook = new \Baikal\Model\AddressBook();
$oDefaultAddressBook->set(
"principaluri",
"principals/" . $this->get("username")
)->set(
"displayname",
"Default Address Book"
)->set(
"uri",
"default"
)->set(
"description",
"Default Address Book for " . $this->get("displayname")
);
$oDefaultAddressBook->persist();
}
}
@ -153,6 +181,16 @@ class User extends \Flake\Core\Model\Db {
# Destroying identity principal
$this->oIdentityPrincipal->destroy();
$oCalendars = $this->getCalendarsBaseRequester()->execute();
foreach($oCalendars as $calendar) {
$calendar->destroy();
}
$oAddressBooks = $this->getAddressBooksBaseRequester()->execute();
foreach($oAddressBooks as $addressbook) {
$addressbook->destroy();
}
parent::destroy();
}

View file

@ -0,0 +1,82 @@
<?php
/***************************************************************
* Copyright notice
*
* (c) 2012 Jérôme Schneider <mail@jeromeschneider.fr>
* All rights reserved
*
* http://baikal.codr.fr
*
* This script is part of the Baïkal Server project. The Baïkal
* Server project is free software; you can redistribute it
* and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
namespace BaikalAdmin\Controller\Navigation;
class Bar extends \Flake\Core\Controller {
public function execute() {
}
public function render() {
$sCurrentRoute = $GLOBALS["ROUTER"]::getCurrentRoute();
$sActiveHome = $sActiveUsers = $sActiveSettings = "";
$sControllerForDefaultRoute = $GLOBALS["ROUTER"]::getControllerForRoute("default");
$sHomeLink = $sControllerForDefaultRoute::link();
$sUsersLink = \BaikalAdmin\Controller\Users::link();
$sSettingsLink = \BaikalAdmin\Controller\Settings::link();
if($sCurrentRoute === "default") {
$sActiveHome = "active";
}
if(
$sCurrentRoute === $GLOBALS["ROUTER"]::getRouteForController("\BaikalAdmin\Controller\Users") ||
$sCurrentRoute === $GLOBALS["ROUTER"]::getRouteForController("\BaikalAdmin\Controller\User\Calendars") ||
$sCurrentRoute === $GLOBALS["ROUTER"]::getRouteForController("\BaikalAdmin\Controller\User\AddressBooks")
) {
$sActiveUsers = "active";
}
if($sCurrentRoute === $GLOBALS["ROUTER"]::getRouteForController("\BaikalAdmin\Controller\Settings")) {
$sActiveSettings = "active";
}
$sHtml =<<<HTML
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<a class="brand" href="{$sHomeLink}">Baïkal Admin !!!!!</a>
<div class="nav-collapse">
<ul class="nav">
<li class="{$sActiveHome}"> <a href="{$sHomeLink}">Home</a></li>
<li class="{$sActiveUsers}"> <a href="{$sUsersLink}">Users and resources</a></li>
<li class="{$sActiveSettings}"> <a href="{$sSettingsLink}">Settings</a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</div>
</div>
HTML;
return $sHtml;
}
}

View file

@ -27,11 +27,28 @@
namespace BaikalAdmin\Controller;
class Settings extends \Flake\Core\Controller {
public function __construct() {
parent::__construct();
$this->oModel = new \Baikal\Model\Config(BAIKAL_PATH_SPECIFIC . "config.php");
# Assert that config file is writable
if(!$this->oModel->writable()) {
throw new \Exception("Config file is not writable;" . __FILE__ . " > " . __LINE__);
}
$this->oForm = $this->oModel->formForThisModelInstance(array(
"close" => FALSE
));
}
public function execute() {
if($this->oForm->submitted()) {
$this->oForm->execute();
}
}
public function render() {
return "<h2>Settings</h2>";
return $this->oForm->render();
}
}

View file

@ -0,0 +1,243 @@
<?php
/***************************************************************
* Copyright notice
*
* (c) 2012 Jérôme Schneider <mail@jeromeschneider.fr>
* All rights reserved
*
* http://baikal.codr.fr
*
* This script is part of the Baïkal Server project. The Baïkal
* Server project is free software; you can redistribute it
* and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
namespace BaikalAdmin\Controller\User;
class AddressBooks extends \Flake\Core\Controller {
protected $aMessages = array();
protected $oModel; # \Baikal\Model\Contact
protected $oUser; # \Baikal\Model\User
protected $oForm; # \Formal\Form
public function __construct() {
parent::__construct();
if(($iUser = $this->currentUserId()) === FALSE) {
throw new \Exception("BaikalAdmin\Controller\User\Contacts::render(): User get-parameter not found.");
}
$this->oUser = new \Baikal\Model\User($iUser);
if(($iModel = self::editRequested()) !== FALSE) {
$this->oModel = new \Baikal\Model\AddressBook($iModel);
$this->initForm();
}
if(self::newRequested()) {
# building floating object
$this->oModel = new \Baikal\Model\AddressBook();
$this->oModel->set(
"principaluri",
$this->oUser->get("uri")
);
$this->oModel->set(
"ctag",
"1"
);
$this->initForm();
}
}
public function execute() {
if(self::editRequested()) {
if($this->oForm->submitted()) {
$this->oForm->execute();
}
}
if(self::newRequested()) {
if($this->oForm->submitted()) {
$this->oForm->execute();
if($this->oForm->persisted()) {
$this->oForm->setOption(
"action",
$this->linkEdit(
$this->oForm->modelInstance()
)
);
}
}
}
if(($iModel = self::deleteRequested()) !== FALSE) {
if(self::deleteConfirmed() !== FALSE) {
# catching Exception thrown when model already destroyed
# happens when user refreshes page on delete-URL, for instance
try {
$oModel = new \Baikal\Model\AddressBook($iModel);
$oModel->destroy();
} catch(\Exception $e) {
# already deleted; silently discarding
}
# Redirecting to admin home
\Flake\Util\Tools::redirectUsingMeta(self::linkHome());
} else {
$oModel = new \Baikal\Model\AddressBook($iModel);
$this->aMessages[] = \Formal\Core\Message::warningConfirmMessage(
"Check twice, you're about to delete " . $oModel->label() . "</strong> from the database !",
"<p>You are about to delete a contact book and all it's visiting cards. This operation cannot be undone.</p><p>So, now that you know all that, what shall we do ?</p>",
self::linkDeleteConfirm($oModel),
"Delete <strong><i class='" . $oModel->icon() . " icon-white'></i> " . $oModel->label() . "</strong>",
self::linkHome()
);
}
}
}
public function render() {
$sHtml = "";
# Render list of users
$oAddressBooks = $this->oUser->getAddressBooksBaseRequester()->execute();
$oView = new \BaikalAdmin\View\AddressBooks\Listing();
$oView->setData("user", $this->oUser);
$oView->setData("addressbooks", $oAddressBooks);
$sHtml .= $oView->render();
# Render form
$sHtml .= "<a id='form'></a>";
$sMessages = implode("\n", $this->aMessages);
if(self::newRequested() || self::editRequested()) {
# We have to display the User form
$sHtml .= $this->oForm->render();
} else {
# No form is displayed; simply display messages, if any
$sHtml .= $sMessages;
}
return $sHtml;
}
protected function initForm() {
if($this->editRequested() || $this->newRequested()) {
$aOptions = array(
"closeurl" => $this->linkHome()
);
$this->oForm = $this->oModel->formForThisModelInstance($aOptions);
}
}
protected static function currentUserId() {
$aParams = $GLOBALS["ROUTER"]::getURLParams();
if(($iUser = intval($aParams[0])) === 0) {
return FALSE;
}
return $iUser;
}
protected static function newRequested() {
$aParams = $GLOBALS["ROUTER"]::getURLParams();
return $aParams[1] === "new";
}
protected static function editRequested() {
$aParams = $GLOBALS["ROUTER"]::getURLParams();
if(($aParams[1] === "edit") && intval($aParams[2]) > 0) {
return intval($aParams[2]);
}
return FALSE;
}
protected static function deleteRequested() {
$aParams = $GLOBALS["ROUTER"]::getURLParams();
if(($aParams[1] === "delete") && intval($aParams[2]) > 0) {
return intval($aParams[2]);
}
return FALSE;
}
protected static function deleteConfirmed() {
if(($iPrimary = self::deleteRequested()) === FALSE) {
return FALSE;
}
$aParams = $GLOBALS["ROUTER"]::getURLParams();
if($aParams[3] === "confirm") {
return $iPrimary;
}
return FALSE;
}
public static function linkNew() {
return $GLOBALS["ROUTER"]::buildRouteForController(
get_called_class(),
self::currentUserId(),
"new"
) . "#form";
}
public static function linkEdit(\Baikal\Model\AddressBook $oModel) {
return $GLOBALS["ROUTER"]::buildRouteForController(
get_called_class(),
self::currentUserId(),
"edit",
$oModel->get("id")
) . "#form";
}
public static function linkDelete(\Baikal\Model\AddressBook $oModel) {
return $GLOBALS["ROUTER"]::buildRouteForController(
get_called_class(),
self::currentUserId(),
"delete",
$oModel->get("id")
) . "#message";
}
public static function linkDeleteConfirm(\Baikal\Model\AddressBook $oModel) {
return $GLOBALS["ROUTER"]::buildRouteForController(
get_called_class(),
self::currentUserId(),
"delete",
$oModel->get("id"),
"confirm"
) . "#message";
}
public static function linkHome() {
return $GLOBALS["ROUTER"]::buildRouteForController(
get_called_class(),
self::currentUserId()
);
}
}

View file

@ -37,17 +37,17 @@ class Calendars extends \Flake\Core\Controller {
parent::__construct();
if(($iUser = $this->currentUserId()) === FALSE) {
throw new \Exception("BaikalAdmin\Controller\Details::render(): User get-parameter not found.");
throw new \Exception("BaikalAdmin\Controller\User\Calendars::render(): User get-parameter not found.");
}
$this->oUser = new \Baikal\Model\User($iUser);
if(($iCalendar = self::editRequested()) !== FALSE) {
$this->oModel = new \Baikal\Model\Calendar($iCalendar);
if(($iModel = self::editRequested()) !== FALSE) {
$this->oModel = new \Baikal\Model\Calendar($iModel);
$this->initForm();
}
if(($iUser = self::newRequested()) !== FALSE) {
if(self::newRequested()) {
# building floating object
$this->oModel = new \Baikal\Model\Calendar();
$this->oModel->set(

View file

@ -196,4 +196,8 @@ class Users extends \Flake\Core\Controller {
public static function linkCalendars(\Baikal\Model\User $user) {
return $GLOBALS["ROUTER"]::buildRouteForController('\BaikalAdmin\Controller\User\Calendars', $user->get("id"));
}
public static function linkAddressBooks(\Baikal\Model\User $user) {
return $GLOBALS["ROUTER"]::buildRouteForController('\BaikalAdmin\Controller\User\AddressBooks', $user->get("id"));
}
}

View file

@ -0,0 +1,47 @@
<style type="text/css">
.no-border-left { border-left: none !important;}
.col-displayname { width: 20%;}
.col-description { width: 55%;}
.col-actions { width: 25%;}
p.lead { line-height: 40px;}
table p {
margin-bottom: 0;
}
</style>
<header class="jumbotron subhead" id="overview">
<h1><i class="<?= \Baikal\Model\AddressBook::bigicon() ?>"></i>Address Books</h1>
<p class="lead">Manage Address Books for<i class="<?= $user->mediumicon() ?>"></i><strong><?= $user->label() ?></strong>.</p>
<p class="pull-left"><a href="<?= \BaikalAdmin\Controller\Users::link() ?>" class="btn"><i class="icon-chevron-left"></i> Back to users list</a></p>
<p class="pull-right"><a class="btn btn btn-inverse" href="<?= \BaikalAdmin\Controller\User\AddressBooks::linkNew() ?>"><i class="icon-white <?= \Baikal\Model\Calendar::icon() ?>"></i> + Add address book</a></p>
</header>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Display name</th>
<th>Description</th>
<th class="no-border-left"></th>
</tr>
</thead>
<tbody>
<?
foreach($addressbooks as $addressbook):
$linkedit = \BaikalAdmin\Controller\User\AddressBooks::linkEdit($addressbook);
$linkdelete = \BaikalAdmin\Controller\User\AddressBooks::linkDelete($addressbook);
?>
<tr>
<td class="col-displayname"><i class="<?= $addressbook->icon() ?>"></i><?= $addressbook->label() ?></td>
<td class="col-description"><?= $addressbook->get("description") ?></td>
<td class="col-actions no-border-left">
<p class="pull-right">
<nobr><a class="btn btn-primary" href="<?= $linkedit ?>"><i class="icon-edit icon-white"></i> Edit</a></nobr>
<nobr><a class="btn btn-danger" href="<?= $linkdelete ?>"><i class="icon-remove icon-white"></i> Delete</a></nobr>
</p>
</td>
</tr>
<? endforeach ?>
</tbody>
</table>

View file

@ -10,7 +10,7 @@
</style>
<header class="jumbotron subhead" id="overview">
<h1><i class="glyph2x-calendar"></i>Calendars</h1>
<h1><i class="<?= \Baikal\Model\Calendar::bigicon() ?>"></i>Calendars</h1>
<p class="lead">Manage Calendars for<i class="<?= $user->mediumicon() ?>"></i><strong><?= $user->label() ?></strong>.</p>
<p class="pull-left"><a href="<?= \BaikalAdmin\Controller\Users::link() ?>" class="btn"><i class="icon-chevron-left"></i> Back to users list</a></p>
<p class="pull-right"><a class="btn btn btn-inverse" href="<?= \BaikalAdmin\Controller\User\Calendars::linkNew() ?>"><i class="icon-white <?= \Baikal\Model\Calendar::icon() ?>"></i> + Add calendar</a></p>

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]>
@ -20,30 +20,22 @@
{head}
</head>
<body>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<a class="brand" href="{homelink}">Baïkal Admin</a>
<div class="nav-collapse">
<ul class="nav">
<li class="active"><a href="{homelink}">Home</a></li>
<li><a href="{userslink}">Users</a></li>
<li><a href="{settingslink}">Settings</a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</div>
</div>
{navbar}
<div class="container">
{Payload}
</div> <!-- /container -->
<div class="mq0"></div>
<div class="mq1"></div>
<div class="mq2"></div>
<div class="mq3"></div>
<div class="mq4"></div>
<div class="mq5"></div>
<div class="mq6"></div>
<div class="mq7"></div>
<div class="mq8"></div>
<div class="mq9"></div>
<!-- Le javascript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->

View file

@ -1,6 +1,11 @@
body {
padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
background: url('page-bg.png') repeat center top;
/* background: url('page-bg.png') repeat center top;*/
}
.table thead th {
background-color: #777;
color: white;
}
.table-striped tbody tr:nth-child(even) td, .table-striped tbody tr:nth-child(even) th {

View file

@ -19,7 +19,7 @@
foreach($users as $user):
$linkcalendars = \BaikalAdmin\Controller\Users::linkCalendars($user);
# $linkcontacts = \BaikalAdmin\Controller\Users::linkContacts($user);
$linkaddressbooks = \BaikalAdmin\Controller\Users::linkAddressBooks($user);
$linkedit = \BaikalAdmin\Controller\Users::linkEdit($user);
$linkdelete = \BaikalAdmin\Controller\Users::linkDelete($user);
$mailtouri = $user->getMailtoURI();
@ -27,13 +27,13 @@
<tr>
<td class="col-username">
<i class="icon-user"></i> <strong><?= $user->get("username"); ?></strong><br />
<i class="<?= \Baikal\Model\User::icon() ?>"></i> <strong><?= $user->get("username"); ?></strong><br />
<?= $user->get("displayname"); ?> <a href="<?= $mailtouri ?>">&lt;<?= $user->get("email"); ?>&gt;</a>
</td>
<td class="col-actions no-border-left">
<p class="pull-right">
<nobr><a class="btn" href="<?= $linkcalendars ?>"><i class="<?= \Baikal\Model\Calendar::icon() ?>"></i> Calendars</a></nobr>
<nobr><a class="btn" href="<?= $linkcontacts ?>"><i class="icon-book"></i> Contacts</a></nobr>
<nobr><a class="btn" href="<?= $linkaddressbooks ?>"><i class="icon-book"></i> Address Books</a></nobr>
<nobr><a class="btn btn-primary" href="<?= $linkedit ?>"><i class="icon-edit icon-white"></i> Edit</a></nobr>
<nobr><a class="btn btn-danger" href="<?= $linkdelete ?>"><i class="icon-remove icon-white"></i> Delete</a></nobr>
</p>

View file

@ -0,0 +1,34 @@
<?php
/***************************************************************
* Copyright notice
*
* (c) 2012 Jérôme Schneider <mail@jeromeschneider.fr>
* All rights reserved
*
* http://baikal.codr.fr
*
* This script is part of the Baïkal Server project. The Baïkal
* Server project is free software; you can redistribute it
* and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
namespace BaikalAdmin\Route\User;
class AddressBooks {
public static function execute(\Flake\Core\Render\Container &$oRenderContainer) {
$oRenderContainer->zone("Payload")->addBlock(new \BaikalAdmin\Controller\User\AddressBooks());
}
}

View file

@ -0,0 +1,30 @@
<?php
/***************************************************************
* Copyright notice
*
* (c) 2012 Jérôme Schneider <mail@jeromeschneider.fr>
* All rights reserved
*
* http://baikal.codr.fr
*
* This script is part of the Baïkal Server project. The Baïkal
* Server project is free software; you can redistribute it
* and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
namespace BaikalAdmin\View\AddressBooks;
class Listing extends \BaikalAdmin\Core\View {
}

View file

@ -30,6 +30,7 @@ $GLOBALS["ROUTES"] = array(
"default" => "\BaikalAdmin\Route\Dashboard",
"users" => "\BaikalAdmin\Route\Users",
"users/calendars" => "\BaikalAdmin\Route\User\Calendars",
"users/addressbooks" => "\BaikalAdmin\Route\User\AddressBooks",
"install" => "\BaikalAdmin\Route\Install",
"settings" => "\BaikalAdmin\Route\Settings"
);

View file

@ -43,18 +43,7 @@ $oPage->injectHTTPHeaders();
$oPage->setTitle("Baïkal Web Admin");
$oPage->setBaseUrl(BAIKAL_URI);
$sControllerForDefaultRoute = $GLOBALS["ROUTER"]::getControllerForRoute("default");
$oPage->zone("homelink")->addBlock(new \Flake\Controller\HtmlBlock(
$sControllerForDefaultRoute::link()
));
$oPage->zone("userslink")->addBlock(new \Flake\Controller\HtmlBlock(
\BaikalAdmin\Controller\Users::link()
));
$oPage->zone("settingslink")->addBlock(new \Flake\Controller\HtmlBlock(
\BaikalAdmin\Controller\Settings::link()
));
$oPage->zone("navbar")->addBlock(new \BaikalAdmin\Controller\Navigation\Bar());
# Route the request
$GLOBALS["ROUTER"]::route($oPage);

View file

@ -24,6 +24,14 @@
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
if(!defined('LF')) {
define('LF', chr(10));
}
if(!defined('CR')) {
define('CR', chr(13));
}
if(array_key_exists("SERVER_NAME", $_SERVER) && $_SERVER["SERVER_NAME"] === "mongoose") {
define("MONGOOSE_SERVER", TRUE);
} else {

View file

@ -72,6 +72,10 @@ abstract class Model extends \Flake\Core\FLObject {
return array_pop($aRes);
}
public function floating() {
return TRUE;
}
public function formForThisModelInstance($options = array()) {
$sClass = get_class($this);
$oForm = new \Formal\Form($sClass, $options);
@ -83,4 +87,8 @@ abstract class Model extends \Flake\Core\FLObject {
public function formMorphologyForThisModelInstance() {
throw new \Exception(get_class($this) . ": No form morphology provided for Model.");
}
public abstract function persist();
public abstract function destroy();
}

View file

@ -31,5 +31,4 @@ abstract class NoDb extends \Flake\Core\Model {
public function __construct($aData = array()) {
$this->aData = $aData;
}
}

View file

@ -604,4 +604,18 @@ TEST;
public static function router() {
return "\Flake\Util\Router\QuestionMarkRewrite";
}
public static function arrayIsAssoc($aArray) {
if(!is_array($aArray)) {
throw new \Exception("\Flake\Util\Tools::arrayIsAssoc(): parameter has to be an array.");
}
# Taken from http://stackoverflow.com/questions/173400/php-arrays-a-good-way-to-check-if-an-array-is-associative-or-sequential#answer-4254008
# count() will return 0 if numeric, and > 0 if assoc, even partially
return (bool)count(array_filter(array_keys($aArray), 'is_string'));
}
public static function arrayIsSeq($aArray) {
return !self::arrayIsAssoc($aArray);
}
}

View file

@ -26,7 +26,7 @@
namespace Formal;
class Element {
abstract class Element {
protected $aOptions = array(
"readonly" => FALSE,
@ -76,4 +76,6 @@ class Element {
public function __toString() {
return get_class($this) . "<" . $this->option("label") . ">";
}
public abstract function render();
}

View file

@ -56,7 +56,7 @@ class Checkbox extends \Formal\Element {
$sHtml =<<<HTML
<div class="control-group{$groupclass}">
<label class="control-label" for="displayname">{$label}</label>
<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}/>
</div>

View file

@ -0,0 +1,91 @@
<?php
/***************************************************************
* Copyright notice
*
* (c) 2012 Jérôme Schneider <mail@jeromeschneider.fr>
* All rights reserved
*
* http://baikal.codr.fr
*
* This script is part of the Baïkal Server project. The Baïkal
* Server project is free software; you can redistribute it
* and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
namespace Formal\Element;
class Listbox extends \Formal\Element {
public function render() {
$disabled = "";
$inputclass = "";
$groupclass = "";
$placeholder = "";
$value = $this->value();
$label = $this->option("label");
$prop = $this->option("prop");
if($this->option("readonly") === TRUE) {
$inputclass .= " disabled";
$disabled = " disabled";
}
if($this->option("error") === TRUE) {
$groupclass .= " error";
}
$aOptions = $this->option("options");
if(!is_array($aOptions)) {
throw new \Exception("\Formal\Element\Listbox->render(): 'options' has to be an array.");
}
$clientvalue = htmlspecialchars($value);
$aRenderedOptions = array();
if(\Flake\Util\Tools::arrayIsSeq($aOptions)) {
# Array is sequential
reset($aOptions);
foreach($aOptions as $sOptionValue) {
$selected = ($sOptionValue === $value) ? " selected=\"selected\"" : "";
$aRenderedOptions[] = "<option" . $selected . ">" . htmlspecialchars($sOptionValue) . "</option>";
}
} else {
# Array is associative
reset($aOptions);
foreach($aOptions as $sOptionValue => $sOptionCaption) {
$selected = ($sOptionValue === $value) ? " selected=\"selected\"" : "";
$aRenderedOptions[] = "<option value=\"" . htmlspecialchars($sOptionValue) . "\"" . $selected . ">" . htmlspecialchars($sOptionCaption) . "</option>";
}
}
reset($aRenderedOptions);
$sRenderedOptions = implode("\n", $aRenderedOptions);
unset($aRenderedOptions);
$sHtml =<<<HTML
<div class="control-group{$groupclass}">
<label class="control-label" for="{$prop}">{$label}</label>
<div class="controls">
<select class="{$inputclass}" id="{$prop}" name="{$prop}"{$disabled}>
{$sRenderedOptions}
</select>
</div>
</div>
HTML;
return $sHtml;
}
}

View file

@ -38,7 +38,7 @@ class Text extends \Formal\Element {
$groupclass = "";
$placeholder = "";
$value = htmlspecialchars($this->value());
$value = $this->value();
$label = $this->option("label");
$prop = $this->option("prop");
$placeholder = $this->option("placeholder");
@ -56,13 +56,15 @@ class Text extends \Formal\Element {
$placeholder = " placeholder=\"" . htmlspecialchars($sPlaceHolder) . "\" ";
}
$clientvalue = htmlspecialchars($value);
$sInputType = $this->inputtype();
$sHtml =<<<HTML
<div class="control-group{$groupclass}">
<label class="control-label" for="displayname">{$label}</label>
<label class="control-label" for="{$prop}">{$label}</label>
<div class="controls">
<input type="{$sInputType}" class="input-xlarge{$inputclass}" id="{$prop}" name="{$prop}" value="{$value}"{$disabled}{$placeholder}/>
<input type="{$sInputType}" class="input-xlarge{$inputclass}" id="{$prop}" name="{$prop}" value="{$clientvalue}"{$disabled}{$placeholder}/>
</div>
</div>
HTML;

View file

@ -31,6 +31,8 @@ class Form {
protected $sModelClass = "";
protected $aOptions = array(
"action" => "",
"close" => TRUE,
"closeurl" => "",
);
protected $oModelInstance = null;
protected $oElements = null;
@ -289,6 +291,7 @@ class Form {
$oElement->option("prop")
)
);
$aHtml[] = $oElement->render();
}
@ -319,7 +322,13 @@ class Form {
}
$sSubmittedFlagName = $this->sModelClass . "::submitted";
$sCloseUrl = $this->option("closeurl");
if($this->option("close") === TRUE) {
$sCloseUrl = $this->option("closeurl");
$sCloseButton = '<a class="btn" href="' . $sCloseUrl . '">Close</a>';
} else {
$sCloseButton = "";
}
$sActionUrl = $this->option("action");
$sHtml =<<<HTML
@ -331,7 +340,7 @@ class Form {
{$elements}
<div class="form-actions">
<button type="submit" class="btn btn-primary">Save changes</button>
<a class="btn" href="{$sCloseUrl}">Close</a>
{$sCloseButton}
</div>
</fieldset>
</form>

View file

@ -59,6 +59,9 @@
display: none;
}
@media (max-width: 767px) {
.mq0:after {
content: "(max-width: 767px)" !important;
}
.visible-phone {
display: block;
}
@ -73,6 +76,9 @@
}
}
@media (min-width: 768px) and (max-width: 979px) {
.mq1:after {
content: "(min-width: 768px) and (max-width: 979px)" !important;
}
.visible-tablet {
display: block;
}
@ -87,6 +93,9 @@
}
}
@media (max-width: 480px) {
.mq2:after {
content: "(max-width: 480px)" !important;
}
.nav-collapse {
-webkit-transform: translate3d(0, 0, 0);
}
@ -134,6 +143,9 @@
}
}
@media (max-width: 767px) {
.mq3:after {
content: "(max-width: 767px)" !important;
}
body {
padding-left: 20px;
padding-right: 20px;
@ -183,6 +195,9 @@
}
}
@media (min-width: 768px) and (max-width: 979px) {
.mq4:after {
content: "(min-width: 768px) and (max-width: 979px)" !important;
}
.row {
margin-left: -20px;
*zoom: 1;
@ -374,6 +389,9 @@
}
}
@media (max-width: 979px) {
.mq5:after {
content: "max-width: 979px" !important;
}
body {
padding-top: 0;
}
@ -482,12 +500,18 @@
}
}
@media (min-width: 980px) {
.mq6:after {
content: "min-width: 980px" !important;
}
.nav-collapse.collapse {
height: auto !important;
overflow: visible !important;
}
}
@media (min-width: 1200px) {
.mq7:after {
content: "min-width: 1200px" !important;
}
.row {
margin-left: -30px;
*zoom: 1;

Binary file not shown.