From 5e60bf4a9aee72ad114554a4a75d07460510d529 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Tue, 28 Aug 2007 08:18:04 +0000 Subject: [PATCH] - initial import --- AUTHORS | 5 + INSTALL | 211 ++++++++++++++++++++ Makefile | 18 ++ README | 71 +++++++ TODO | 6 + admin/.htaccess | 2 + admin/common.inc.php | 75 +++++++ admin/directories.php | 331 +++++++++++++++++++++++++++++++ admin/dynaform.css | 47 +++++ admin/format.css | 88 ++++++++ admin/getgroups.php | 69 +++++++ admin/images/assign.png | Bin 0 -> 876 bytes admin/images/delete.png | Bin 0 -> 841 bytes admin/images/directory.png | Bin 0 -> 575 bytes admin/images/edit.png | Bin 0 -> 710 bytes admin/images/groups.png | Bin 0 -> 847 bytes admin/images/newdirectory.png | Bin 0 -> 600 bytes admin/images/newuser.png | Bin 0 -> 751 bytes admin/images/throbber.gif | Bin 0 -> 1336 bytes admin/images/x.png | Bin 0 -> 752 bytes admin/index.php | 48 +++++ admin/scripts/autocomplete.js | 305 ++++++++++++++++++++++++++++ admin/scripts/directories.js | 148 ++++++++++++++ admin/scripts/helper.js | 29 +++ admin/scripts/jquery.js | 1 + admin/scripts/users.js | 189 ++++++++++++++++++ admin/templates/directories.html | 39 ++++ admin/templates/error.xml | 1 + admin/templates/footer.html | 1 + admin/templates/header.html | 8 + admin/templates/start.html | 10 + admin/templates/users.html | 37 ++++ admin/users.php | 317 +++++++++++++++++++++++++++++ config/config.inc.php | 35 ++++ config/dbsettings.inc.php | 36 ++++ setup/schema.sql | 25 +++ setup/webdavadmin.vhost | 52 +++++ 37 files changed, 2204 insertions(+) create mode 100644 AUTHORS create mode 100644 INSTALL create mode 100644 Makefile create mode 100644 README create mode 100644 TODO create mode 100644 admin/.htaccess create mode 100644 admin/common.inc.php create mode 100644 admin/directories.php create mode 100644 admin/dynaform.css create mode 100644 admin/format.css create mode 100644 admin/getgroups.php create mode 100644 admin/images/assign.png create mode 100644 admin/images/delete.png create mode 100644 admin/images/directory.png create mode 100644 admin/images/edit.png create mode 100644 admin/images/groups.png create mode 100644 admin/images/newdirectory.png create mode 100644 admin/images/newuser.png create mode 100644 admin/images/throbber.gif create mode 100644 admin/images/x.png create mode 100644 admin/index.php create mode 100644 admin/scripts/autocomplete.js create mode 100644 admin/scripts/directories.js create mode 100644 admin/scripts/helper.js create mode 100644 admin/scripts/jquery.js create mode 100644 admin/scripts/users.js create mode 100644 admin/templates/directories.html create mode 100644 admin/templates/error.xml create mode 100644 admin/templates/footer.html create mode 100644 admin/templates/header.html create mode 100644 admin/templates/start.html create mode 100644 admin/templates/users.html create mode 100644 admin/users.php create mode 100644 config/config.inc.php create mode 100644 config/dbsettings.inc.php create mode 100644 setup/schema.sql create mode 100644 setup/webdavadmin.vhost diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..3e9b91a --- /dev/null +++ b/AUTHORS @@ -0,0 +1,5 @@ +====================== +Authors of WebDAVAdmin +====================== + +Jan Dittberner diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..30a1da5 --- /dev/null +++ b/INSTALL @@ -0,0 +1,211 @@ +======================================= + WebDAVAdmin installation instructions +======================================= +:Author: Jan Dittberner +:Contact: jan@dittberner.info +:Version: 0.1 +:Revision: $Revision$ +:Date: $Date$ +:Copyright: Copyright (C) 2007 Jan Dittberner + +.. contents:: + +Unpack WebDAVAdmin +================== + +1. unpack the WebDAVAdmin distribution file somewhere:: + + cd ~/tmp/ + tar xjf webdavadmin-0.1.tar.bz2 + + ``~/tmp/`` is just an example to be able to reference it in these + installation instructions + +Setup PostgreSQL and your database +================================== + +1. Install PostgreSQL by the means of your operating system. For + Debian GNU/Linux 4.0 Etch execute:: + + sudo aptitude install postgresql-8.1 + +2. Switch to user postgres:: + + sudo su - postgres + + and + + 1) create a user for WebDAVAdmin:: + + createuser -SDRP myuser + + when prompted type the password for the database user twice + + 2) create a database:: + + createdb --owner=myuser --encoding=UTF-8 mydb + + 3) exit the postgres shell + + You may skip theese steps if you want to use an existing database + +3. Import the schema for WebDAVAdmin:: + + psql -h localhost -U myuser mydb < ~/tmp/webdavadmin-0.1/setup/schema.sql + + when prompted type the password for your database user. + +Setup Apache +============ + +1. Install, enable and configure apache and the apache modules + + - mod_dav + - mod_dav_fs + - mod_auth_pgsql + - libphp5 + + by the means of your operating system vendor. For Debian GNU/Linux 4.0 + Etch this means [1]_:: + + sudo aptitude install apache2-mpm-prefork libapache2-mod-php5 libapache2-mod-auth-pgsql + sudo a2enmod php5 + sudo a2enmod auth_pgsql + sudo a2enmod dav + sudo a2enmod dav_fs + +.. [1] if you don't want to use ``sudo`` you may also switch to root. + +2. Configure a VirtualHost to use WebDAV and PostgreSQL + authentication, this VirtualHost configuration could look like:: + + + ServerName davhost.yourdomain.net + + DavLockDb /var/run/apache2/davlock/davhost.yourdomain.net + DocumentRoot /var/www + + + Options Indexes + Order allow,deny + allow from all + + Dav on + + # Authentication/Authorization + AuthType Basic + AuthName "WebDAVAdmin example" + AuthBasicAuthoritative Off + AuthUserFile /etc/apache2/auth/davhost.yourdomain.net.passwd + + Auth_PG_host localhost + Auth_PG_port 5432 + Auth_PG_user myuser + Auth_PG_pwd secret + Auth_PG_database mydb + Auth_PG_pwd_table dav_password + Auth_PG_uid_field username + Auth_PG_pwd_field password + Auth_PG_grp_table dav_group + Auth_PG_grp_user_field username + Auth_PG_grp_group_field groupname + Auth_PG_hash_type MD5 + #Auth_PG_log_table dav_log + #Auth_PG_log_uname_field username + #Auth_PG_log_date_field reqdate + #Auth_PG_log_uri_field uri + #Auth_PG_log_addrs_field ipaddr + Auth_PG_authoritative on + + require group davroot + + + ErrorLog /var/log/apache2/davhost.yourdomain.net_error.log + CustomLog /var/log/apache2/davhost.yourdomain.net_access.log combined + + + + The directory specified for ``DavLockDb`` must be writable for the + user your apache processes run as. The ``AuthUserFile`` is + specified as a fallback if your PostgreSQL database is not + available. + +Install required php modules and classes +======================================== + +WebDAVAdmin needs Smarty and a PostgreSQL PDO driver for PHP5. To +install these requirements perform the following step:: + + sudo aptitude install smarty php5-pgsql + +on operating systems other then Debian GNU/Linux consult your system +documentation. + +Copy WebDAVAdmin files +====================== + +2. create a new document root directory or a subdirectory inside an + existing one + +3. create a subdirectory which you'll later use for WebDAVAdmin:: + + mkdir /var/www/dav + +4. copy the admin subdirectory of the unpacked webdavadmin distribution + file to the directory just created:: + + cp -R webdavadmin-0.1/admin /var/www/dav/ + +5. set the filesystem permissions of the dav directory to allow the + user apache is running as to write to the directory + +Configure WebDAVAdmin +===================== + +The WebDAVAdmin distribution contains a directory ``config`` with +configuration templates that you need to customize for your +environment. + +1. ``dbsettings.inc.php`` + + This file contains the settings for your database connection. The + file should be placed outside the document root for security + reasons. A customized version of this file may look like:: + + + +2. ``config.inc.php`` + + This file contains the absolute path to your WebDAVAdmin + installation and to your ``dbsettings.inc.php``. A customized + version of this file could be:: + + + + After adapting the contents to your environment put this file into + your WebDAVAdmin directory. For example:: + + cp config.inc.php /var/www/dav/admin/ + +Be sure to make the subdirectory templates_c of your WebDAVAdmin +directory writable for your apache user [2]_. + +.. [2] you could use chown, chmod and/or ACLs to perform this task + +Now you should be able to use your installation of WebDAVAdmin by +opening the URL http://davhost.yourdomain.net/dav/admin/ (if you just +followed this instructions). + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8d00250 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +.PHONY: apidoc + +VERSION=0.1 +PROJECT=webdavadmin +SRCFILES=admin/common.inc.php,admin/directories.php,admin/getgroups.php,admin/index.php,admin/users.php + +apidoc: + if [ -d apidoc ]; then rm -r apidoc; fi + phpdoc -f $(SRCFILES) -t apidoc -s + +clean: + find -name '*~' -type f -exec rm {} \; + +distclean: clean + if [ -d apidoc ]; then rm -r apidoc; fi + +dist: distclean + cd .. ; tar cjf $(PROJECT)-$(VERSION).tar.bz2 $(PROJECT)-$(VERSION) diff --git a/README b/README new file mode 100644 index 0000000..5458dac --- /dev/null +++ b/README @@ -0,0 +1,71 @@ +============= + WebDAVAdmin +============= +:Author: Jan Dittberner +:Contact: jan@dittberner.info +:Version: 0.1 +:Revision: $Revision$ +:Date: $Date$ +:Copyright: Copyright (C) 2007 Jan Dittberner + +.. contents:: + +Goals +===== + +The goal of this software is to provide an easy to use administration +interface for a WebDAV repository using mod-auth-pgsql as its +authentication and authorization source. + +Requirements +============ + +To use this software you need an Apache webserver configured with the +dav module and mod-auth-pgsql, PHP 5 with PostgreSQL PDO driver, the +Smarty_ template engine and a PostgreSQL database. The software has +been developed using the versions contained in Debian GNU/Linux 4.0 +Etch. + +- Apache 2.2.3 +- PostgreSQL 8.1.8 +- mod-auth-pgsql 2.0.3 +- PHP 5.2.0 +- Smarty 2.6.14 + +.. _Smarty: http://smarty.php.net/ + +Installation +============ + +Please see INSTALL_ for installation instructions. + +.. _INSTALL: INSTALL + +Used third party code +===================== + +WebDAVAdmin uses the JQuery Javascript library for DOM manipulations +and AJAX calls. + +.. _JQuery: http://www.jquery.com/ + +Version 1.1.3 of JQuery has been used for developing this software and +is bundled in the ``scripts`` directory as ``jquery.js``. + +License +======= + +This program 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. + +This program 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. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. diff --git a/TODO b/TODO new file mode 100644 index 0000000..b62ef0b --- /dev/null +++ b/TODO @@ -0,0 +1,6 @@ +TODO +==== + +- create an installer +- setup admin user during installation +- better integration into existing databases diff --git a/admin/.htaccess b/admin/.htaccess new file mode 100644 index 0000000..ef07044 --- /dev/null +++ b/admin/.htaccess @@ -0,0 +1,2 @@ +DirectoryIndex index.php +require group davadmin diff --git a/admin/common.inc.php b/admin/common.inc.php new file mode 100644 index 0000000..594f641 --- /dev/null +++ b/admin/common.inc.php @@ -0,0 +1,75 @@ + + * @version $Id$ + * @license GPL + * @package WebDAVAdmin + * + * Copyright (c) 2007 Jan Dittberner + * + * This file is part of WebDAVAdmin. + * + * This program 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. + * + * This program 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +/** Include configuration information. */ +require_once('config.inc.php'); + +/** DAV administrator group name. */ +define(ADMIN_GROUP, 'davadmin'); +/** DAV administration application subdirectory. */ +define(ADMIN_DIR, 'admin'); + +/** Include the Smarty template engine. */ +require_once("smarty/libs/Smarty.class.php"); + +/** Global Smarty template engine instance. */ +$smarty = new Smarty(); + +/** Handle invalid requests to the application. */ +function invalidCall() { + header("Content-Type: text/plain; charset=UTF-8"); + print("Ungültiger Aufruf!"); + die(); +} + +/** Handle errors with an XML message. */ +function errorAsXml($errormsg) { + header("Content-Type: text/xml; charset=UTF-8"); + $GLOBALS['smarty']->assign("errormsg", $errormsg); + $GLOBALS['smarty']->display("error.xml"); + die(); +} + +/** Handle errors with an HTML error page. */ +function errorAsHtml($errormsg) { + header("Content-Type: text/html; charset=UTF-8"); + $GLOBALS['smarty']->assign("errormsg", $errormsg); + $GLOBALS['smarty']->display("error.html"); + die(); +} + +/** + * Handle a PDO statement error. + * + * @param PDOStatement $sth statement handle + */ +function statementErrorAsXml(&$sth) { + errorAsXml(utf8_encode(implode("\n", $sth->errorInfo()))); +} +?> \ No newline at end of file diff --git a/admin/directories.php b/admin/directories.php new file mode 100644 index 0000000..dd2e5c4 --- /dev/null +++ b/admin/directories.php @@ -0,0 +1,331 @@ + + * @version $Id$ + * @license GPL + * @package WebDAVAdmin + * + * Copyright (c) 2007 Jan Dittberner + * + * This file is part of WebDAVAdmin. + * + * This program 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. + * + * This program 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +/** Include common code. */ +include_once('common.inc.php'); + +/** Format string for group lines in .htaccess files. */ +define('GROUPLINEFORMAT', "require group %s\n"); + +/** Regular expression for finding group lines in .htaccess files. */ +define('GROUPLINERE', '/^require\s+group\s+(.*)$/i'); + +/** Regular expression matching legal directory names. */ +define('DIRNAMERE', '/^[a-zA-Z0-9 -_.]+$/'); + +/** + * List of mandatory groups. These groups are assigned to every new or + * changed directory. + */ +$mandatorygroups = array(ADMIN_GROUP); + +/** + * Gets the group names allowed to access the given directory from its + * .htaccess file. Mandatory groups are excluded. + * + * @param string $dirname a fully qualified directory name + * @return an array of group names + */ +function getDirGroupsFromHtaccess($dirname) { + $htaccessname = $dirname . DIRECTORY_SEPARATOR . ".htaccess"; + $groups = array(); + if (false !== ($fh = fopen($htaccessname, "r"))) { + while (!feof($fh)) { + $buffer = fgets($fh); + if (preg_match_all(GROUPLINERE, trim($buffer), $matches)) { + $grouparr = explode(" ", $matches[1][0]); + foreach ($grouparr as $group) { + $group = trim($group); + if (!in_array($group, $GLOBALS['mandatorygroups'])) { + array_push($groups, $group); + } + } + } + } + } + fclose($fh); + return $groups; +} + +/** + * Gets the names of groups for a directory. + * + * @param string $dirname directory name relative to {@link DAV_ROOT} + * @return an array of group names + * @see #getDirGroupsFromHtaccess(string) + */ +function getDirGroups($dirname) { + return getDirGroupsFromHtaccess(DAV_ROOT . DIRECTORY_SEPARATOR . $dirname); +} + +/** + * Counts the visible files and their accumulated size in a directory + * tree. + * + * @param string $dirname a fully qualified directory name + * @param array $retval a two component array index 0 is the file + * count, index 2 is the accumulated size of files + * @return array updated with the data from subdirectories and files + * of $dirname + */ +function countFilesRecursive($dirname, $retval = array(0, 0)) { + $dh = opendir($dirname); + while (false !== ($entry = readdir($dh))) { + $fullname = $dirname . DIRECTORY_SEPARATOR . $entry; + if (strpos($entry, '.') !== 0) { + if (is_dir($fullname)) { + $retval = countFilesRecursive($fullname, $retval); + } else if (is_file($fullname)) { + $retval = array($retval[0] + 1, $retval[1] + filesize($fullname)); + } + } + } + closedir($dh); + return $retval; +} + +/** + * Gets the data of a directory. + * + * @param string $dirname a fully qualified directory name + * @return array of containing directory data + */ +function getDirectoryData($dirname) { + $dir = array(); + $dir['name'] = basename($dirname); + $dir['groups'] = getDirGroupsFromHtaccess($dirname); + list($dir['filecount'], $dir['filesize']) = countFilesRecursive($dirname); + $dir['maydelete'] = ($dir['filecount'] == 0) ? 1 : 0; + $dir['filesize'] = sprintf("%d kBytes", $dir['filesize'] / 1024); + return $dir; +} + +/** + * Gets XML encoded data of a directory. + * + * @param string $dirname dirname relative to {@link DAV_ROOT} + * @return XML string + */ +function getDirectoryDataAsXml($dirname) { + if (is_dir(DAV_ROOT . $dirname)) { + $dirdata = getDirectoryData(DAV_ROOT . $dirname); + header("Content-Type: text/xml; charset=UTF-8"); + return sprintf('%s%s%d%s%d', $dirdata['name'], implode(", ", $dirdata['groups']), $dirdata['filecount'], $dirdata['filesize'], $dirdata['maydelete']); + } else { + errorAsXml(sprintf(_("Invalid directory name %s!"), $dirname)); + } +} + +/** + * Gets XML encoded data of a deleted directory. + * + * @param string $dirname directory name relative to {@link DAV_ROOT} + * @return XML string + */ +function getDeletedDirectoryData($dirname) { + header("Content-Type: text/xml; charset=UTF-8"); + return sprintf('%s', $dirname); +} + +/** + * Gets the list of directory data for all valid directories below + * {@link DAV_ROOT}. + * + * @return array of directory data arrays + * @see #getDirectoryData(string) + */ +function getDirectories() { + $dirs = array(); + if (false !== ($entries = scandir(DAV_ROOT))) { + foreach ($entries as $entry) { + if (is_dir(DAV_ROOT . $entry)) { + if (strpos($entry, '.') !== 0) { + if ($entry != ADMIN_DIR) { + array_push($dirs, getDirectoryData(DAV_ROOT . $entry)); + } + } + } + } + } + return $dirs; +} + +/** + * Sets the groups of a directory in its .htaccess file. Mandatory + * groups are added automatically. + * + * @param string $dirname directory name relative to {@link DAV_ROOT} + * @param array &$groups reference to a list of group names + */ +function setGroups($dirname, &$groups) { + $fullname = DAV_ROOT . $dirname; + foreach ($groups as $key => $value) { + $groups[$key] = trim($value); + } + foreach ($GLOBALS['mandatorygroups'] as $mgroup) { + if (!in_array($mgroup, $groups)) { + array_push($groups, $mgroup); + } + } + $found = false; + $lines = array(); + $found = false; + if (is_dir($fullname)) { + $htaccess = $fullname . DIRECTORY_SEPARATOR . ".htaccess"; + if (file_exists($htaccess) && (false !== ($fh = fopen($htaccess, "r")))) { + while (!feof($fh)) { + $buffer = fgets($fh); + if (preg_match(GROUPLINERE, $buffer)) { + array_push($lines, sprintf(GROUPLINEFORMAT, join(" ", $groups))); + $found = true; + } else { + array_push($lines, $buffer); + } + } + fclose($fh); + } + if (!$found) { + array_push($lines, sprintf(GROUPLINEFORMAT, join(" ", $groups))); + } + if (false !== ($fh = fopen($htaccess, "w"))) { + foreach ($lines as $line) { + fwrite($fh, $line); + } + fclose($fh); + } + } +} + +/** + * Updates a directory to be accessible by the given list of + * groups. The directory is created if it doesn't exist. + * + * @param string $dirname directory name relative to {@link DAV_ROOT} + * @param array $groups a list of group names + */ +function updateDirectory($dirname, $groups) { + if (preg_match(DIRNAMERE, $dirname, $matches)) { + if ($dirname != ADMIN_DIR) { + $fullname = DAV_ROOT . $dirname; + if (file_exists($fullname)) { + if (!is_dir($fullname)) { + errorAsXml(sprintf(_("There already is a directory entry named %s, but it's not a directory!"), $dirname)); + } + } else { + mkdir($fullname); + } + setGroups($dirname, $groups); + } + return; + } + errorAsXml(sprintf(_("Invalid directory name %s!"), $dirname)); +} + +/** + * Recursively deletes the given directory. + * + * @param string $fullname fully qualified directory name + */ +function delrecursive($fullname) { + $dh = opendir($fullname); + while (false !== ($entry = readdir($dh))) { + if ($entry != "." && $entry != "..") { + $entryname = $fullname . DIRECTORY_SEPARATOR . $entry; + if (is_dir($entryname)) { + delrecursive($entryname); + } else { + unlink($entryname); + } + } + } + closedir($dh); + rmdir($fullname); + return true; +} + +/** + * Deletes the given directory if it has a valid name and is not the + * administration interface directory. + * + * @param string $dirname directory name relative to {@link DAV_ROOT} + */ +function deleteDirectory($dirname) { + if (preg_match(DIRNAMERE, $dirname, $matches)) { + if ($dirname != ADMIN_DIR) { + $fullname = DAV_ROOT . $dirname; + if (is_dir($fullname)) { + return delrecursive($fullname); + } + } + errorAsXml(_("Tried to delete the administration interface directory!")); + } + errorAsXml(sprintf(_("Invalid directory name %s!"), $dirname)); +} + +if ($_GET) { + // handle get requests with data + if ($_GET['method']) { + switch ($_GET['method']) { + case 'getdirectorydata': + print getDirectoryDataAsXml($_GET['dirname']); + break; + default: + errorAsXml(sprintf(_("Unexpected values %s!"), serialize($_GET))); + } + } else { + invalidCall(); + } +} elseif ($_POST) { + // handle post requests with data + if ($_POST['method']) { + switch ($_POST['method']) { + case 'submitdirectory': + updateDirectory($_POST['dirname'], explode(",", $_POST['groups'])); + print getDirectoryDataAsXml($_POST['dirname']); + break; + case 'deletedirectory': + if (deleteDirectory($_POST['dirname'])) { + print getDeletedDirectoryData($_POST['dirname']); + } else { + errorAsXml(_("Delete failed!")); + } + break; + default: + errorAsXml(sprintf(_("Unexpected values %s!"), serialize($_POST))); + } + } else { + invalidCall(); + } +} else { + // handle requests without data + header("Content-Type: text/html; charset=UTF-8"); + $smarty->assign("directories", getDirectories()); + $smarty->display("directories.html"); +} +?> \ No newline at end of file diff --git a/admin/dynaform.css b/admin/dynaform.css new file mode 100644 index 0000000..34f341d --- /dev/null +++ b/admin/dynaform.css @@ -0,0 +1,47 @@ +#usereditor { + display:none; + z-index:10; + position:absolute; + top:10px; + margin-left:auto; + margin-right:auto; +} + +.dynaform { + border:2px solid black; + background-color:#fffbed; + color:#2b2b26; + width:380px; + padding:2px; +} + +.dynaform .formelement { + padding:2px; +} + +.dynaform .formelement label { + display:block; + width:171px; + float:left; +} + +.dynaform .formactions { + margin-top:5px; + border-top:1px solid #ffd436; + padding:5px; + text-align:right; +} + +.dynaform input, .dynaform select +{ + padding:0; + width:200px; + background-color:#fffbed; + color:#2b2b26; + border:1px solid #ffd436; + clear:both; +} + +.dynaform input[type=submit] { + width:auto; +} \ No newline at end of file diff --git a/admin/format.css b/admin/format.css new file mode 100644 index 0000000..488f143 --- /dev/null +++ b/admin/format.css @@ -0,0 +1,88 @@ +html +{ + font-family: Verdana,Helvetica,Arial,sans-serif; + font-size: 10pt; + background-color:white; + color:black; +} + +#usertable, #dirtable { + border-collapse:collapse; +} + +#usertable #hcol-username { + width:120px; +} + +#usertable #hcol-name { + width:264px; +} + +#dirtable #hcol-directory { + width:120px; +} + +#dirtable #hcol-groups { + width:200px; +} + +#dirtable #hcol-files { + width:150px; +} + +#usertable th, #usertable td, #dirtable th, #dirtable td { + border:1px solid black; + text-align:left; + padding:2px; +} + +dt { + display:block; + float:left; + width:20px; + clear:both; +} + +dd { + display:block; + margin:0; +} + +/* +* Autocomplete styles +*/ +/* Suggestion list */ +#autocomplete { + position: absolute; + border: 1px solid; + overflow: hidden; + z-index: 100; +} +#autocomplete ul { + margin: 0; + padding: 0; + list-style: none; +} +#autocomplete li { + background: #fff; + color: #000; + white-space: pre; + cursor: default; +} +#autocomplete li.selected { + background: #0072b9; + color: #fff; +} +/* Animated throbber */ +input.form-autocomplete { + background-image: url(images/throbber.gif); + background-repeat: no-repeat; + background-position: 100% 2px; +} +input.throbbing { + background-position: 100% -18px; +} + +a img.actionicon { + border:0; +} diff --git a/admin/getgroups.php b/admin/getgroups.php new file mode 100644 index 0000000..6f64980 --- /dev/null +++ b/admin/getgroups.php @@ -0,0 +1,69 @@ + + * @version $Id$ + * @license GPL + * @package WebDAVAdmin + * + * Copyright (c) 2007 Jan Dittberner + * + * This file is part of WebDAVAdmin. + * + * This program 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. + * + * This program 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +/** Include configuration information. */ +require_once('config.inc.php'); + +// output is plain text (JSON or an error message) +header("Content-Type: text/plain; charset=UTF-8"); + +/** + * Gets group names for autocompletion. + * + * @param string $part Comma separated list of groups and beginning of + * a new group entry + * @return list of Comma separated lists of groups + */ +function getGroups($part) { + $regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x'; + preg_match_all($regexp, $part, $matches); + $array = $matches[1]; + $last_string = trim(array_pop($array)); + if ($last_string != '') { + try { + $dbh = new PDO($GLOBALS['dsn'], $GLOBALS['dbuser'], $GLOBALS['dbpass']); + $sth = $dbh->prepare("SELECT DISTINCT groupname FROM dav_group WHERE LOWER(groupname) LIKE LOWER(?) ORDER BY groupname"); + $sth->execute(array("%" . $last_string . "%")); + $prefix = count($array) ? implode(",", $array) . ", " : ''; + $retval = array(); + foreach ($sth->fetchAll(PDO::FETCH_ASSOC) as $row) { + $retval[$prefix . $row['groupname']] = $row['groupname']; + } + $dbh = null; + } catch (PDOException $e) { + return $e->getMessage(); + } + } + return json_encode($retval); +} + +// split group list part from requested URL. +$parts = substr($_SERVER['PHP_SELF'], strrpos($_SERVER['PHP_SELF'], "/") + 1); +print getGroups($parts); +?> \ No newline at end of file diff --git a/admin/images/assign.png b/admin/images/assign.png new file mode 100644 index 0000000000000000000000000000000000000000..86628028aea3ee702e824a43fff5ef9c5d18615c GIT binary patch literal 876 zcmV-y1C#uTP)WFU8GbZ8({Xk{QrNlj1+3MgYKATls8GayP~Yjt8ECu(VJ zZDC_4AX9W@X>Mh5Co}I@00087Nkl(uMO<0j$Kpza27{YGp(T<~X$^FYv6;!3 zdGA~oGpR9WbvT^oIXvf_$8aWEtyX(=b@e@KZSiz{b93{Tt*xyuf&6scoFv}a>7|R! zcdy@AS$qG(k6t-TeDB^5`R&g?`Ko((_~g;4mabfEMnFVqx7*}-&Tu$HL@>tCXf()5CCZZ(W~$lc?$dn(L_6Mt zohTKXVm;^2*HINh2;_N=wH8$+%QC$8l(I6V#9sm&aC|~gbZQpL6J_@I{{*1h?Q+oR zP@A7;d3hOCMMNm3C9=fVfeP@<0t!?TuT$y@G|OcIkUo4!zP5%>(-&Y>CC$oYiLU|E zz`l(4Fr5}%HHziv!2|Tp9S&M8e!733?d{)KUtec$Zx0ovIyH-THd8T8z}m5vniw;g zWEK3%3d5T>nVFpA4OPS#mR?@MS%-))S6{$eAuYOV&wIx|Tfn7BvFV-K+FYGpuZJ;) zUa!Z_&JL|b*{2V48#cMsuiqrs4DaI`U~wMLdbI# z8jB>Q36g?q|8>}R7D#KeGp#WAhr3^Y#&_S`MxDcmjDKK60Fhw`934J~!4X;N7wJ&4 zK;iF$gR(R;2z}z9&v8EzBM>7P5F?|nG@!;HR&iEmfT{($yN{pzc=uUHh9Nk>j9?gu zW5bz|E&j7$806u1pl8NsEC3#mvo1Z8pF{)(K>uH+tjd{1FAY`z0000WFU8GbZ8({Xk{QrNlj4iWF>9@00ON^L_t(I%XN}DZxdk@ zMb9jrNi3UjGLywn5O6FZhy@ckk0Xx)T3u#4wBt>2&(GWm&Y_ZHmPrwryVr=72S} z(MjOm`1t7jwX2ug3kx4asZrX)rHdzI5inh4ZHg zL&3*|42w(OC=^PlDx5lXf}P1YilQS+KF?pw^W^Evv%n(-AQFk(xO?Z;0mB$!M|=z< zomzE+Fbw$F{XyXO*qKZ+IXQ-|DNIf6;@R_g0X$X#JkKK>53bv#(`nLb)yd^@ zEG>N@pDzG70180A-J%=P)jGEHQfLJ0nAB6 zK~y-)rIX8QR6!7izmqwMgNPHH#Dy2Kl0a5ILGlE;%Tggi5OtY1$OHHkuCjOI6DSIj zMGz8XLD_B}Z1z_kjBs_D+E}{{?vVd~@f) z!_{?nCxGzaV1K~&_RB}7C*xQD1Z+HcytBNrur6~V0uLuVUkxvE*hRK;dSN(nQa0opT+M;*v@E+tQM9#J zmm8QP7cNI~k{K>FCeG(T$E2B*46i*f7jE7fC$D|6%H%A|8U=GXP`rEl@%7%`CrO^W zM;76P2W=b>1VGbJ@nd-W6BuW8JLkYGFmvvMW#{6H?H_;v%ZQZ)0n7A!DIdwEzGDG)Y83R5;7UlQBfmBXT#Wxp5=?YsVlYPI0Evl7{Q;te(o&!W45bBHHPeQ=UQt-1ZyoK7DR|d zqc}<-@&^+H5NkPUx0suoyW~2Kqh2m75Vq?Cf#%fn7)%Usc+}$F!^hms=X>aHiwKDH zN|Yk%07N4$cXHQwzxT6YOUjSe*cE`YU`#ichi zYhQ_l!l&hTq>>5lKbl~9Wrfu*tHk4RGMNmHQpEaVGTM-+74O=cwXmh zh0rR(;VIT-5OtwWCpyB(PY-J?xtlqrb2mWvdu9`APQ6nE=?QMnOygik4qnCUcx1C# zip3(M=`?%0yKHP!m>3_Yeo!ZwOtQ7T&E?b(B7$R#LF)i(4N55xAsUGQU`$951T-2A zy1Gld-A3yG1+Q>9RL6T07*qoM6N<$f@~==;{X5v literal 0 HcmV?d00001 diff --git a/admin/images/groups.png b/admin/images/groups.png new file mode 100644 index 0000000000000000000000000000000000000000..1def1ff7c0f360142b23f37ecaca4dfb38e7b52a GIT binary patch literal 847 zcmV-V1F-ywP)d8A03CEi zSad^gZEa<4bO1wgWnpw>WFU8GbZ8({Xk{QrNlj4iWF>9@00Of~L_t(I%Z-vtXjFF; z#()3&zqy&2iFmI`VXf>LU`D1n-diZ2i!tA^BtE?m3ms?t>{qHfiN zi&hb|DnV=<=}S#aM5BpGj4|`bBzNZC|9!dW1X7FA4?Z}H^L>YNz9abnOw(-Y+0p&e zj<36aXl`o!vaYW7?!?6F0{;_{W8e1PvCDIzXTJ%{#gXvj@uQ;~K51XRv`K$Qqyr~_ zJwgb%uC1l*)!@(gVGW@waB$bQRd?>*-wT{x6v%fBP6DTZMxf!wueY@?(xW6RzoRCz zcz!6HXlwLVfA;*MKwGMg8RF1a8Iw%9mMv={Y;KWUx;=q#D}X3z6 zuZ-sp#}YsnaPL1i=uGKbYY5i#Kefodut}MMb8dvoIfKDv2F2VwVWvDt@HHm!+qbF$+UPn^ngxpia;~)(FQ;>4{~E?|6t3$5+yu@5Wk3N+2cF;SzezH^+LKZWAvDb@6@(uUmTiUWRJ?Mj z7(V;+)>L(R0dN4ppcKrI^JIE)J%jK%FuwSdpS`_8SztN;2xHoBUGxlB1d3ia{?{^O8g%h4AkMqFdv9~_(YIJ$M zQLA6yyxaqr>2yYQ!~hQ;Jy^eatJyL$9B^}XzIWOem1j$>w+IlH^x zP}R&;=`^NVP~K@?WAk}CiUDf18TJo;U}obY6-7<6sEVi-ltuK=ftfKJ4p9|E3{yo+ zQOQ9n1XV6QeS!obguth*H-`WbAxZurxthudkmr&)FjayG5(E(eh+?2@rsXtEi-2(} zf~aNfiR3WB-2tX!pgK&`YLX=5-m~Hn%urKI4OJtEV5*oIz?m2*i)twZ!SWf1DJGVI zL{ZDlAa#6a@?UFYTYc2`v`HuK2caC64FgW~{Xb?!fV`@euelrK{ZOaY}M mA1peOA74HP1V~T%oB0phlYmc7AP>O+0000ri=gp026dY zSad^gZEa<4bO2OnX>%ZQZ)0n7A!DIdwEzGDT}ebiR5;7UlfP>daTtf6ORvrOqv|Pbr_b z+3WVcD=OsW@f%vRIH4 z$B#WRBaw)4cYK_nTEh1gHP53X*3O#aa_am=PNq_rXU-C}tRFUJ|8BZ~3-S1s2tZq_ z$B-lx$W3QKHTb%aC)U-= z-UEZo%+4|qGkJgdEGMR>02qtMdDq_Fv;hcp$TqdYgKK>lKyJCt%i;oI7?Kzmz=}q( z?bTl{pL_a%|*LZA(T?t^@i4vdx(^|qa? zZ+K{}NgYWslsXIoB7)YMFi_OI8iF(fhSNht2Q4?wxw{`P%dE_uhYh4g^5*`~UYq@csXH5cvQ7KL`K~ zSNzZI=Nc01>=@u`q-Vg)2on0q!pa7ub%2-wGjt;xM?96htN#UJ%?T-6rgZtrY<|Ka1OPl_mB6Jg*5LIzfMpw})>Vz{+posxMR zi-3r`qS(tB$*%Owi{%D!oL792{%`J$H8TJ!WCqrdP3uc@?o?A_*6SBIA!xCfoBN~lRKL}M9M}TG4QR6P1VqX}3Jj+37jp#`NeKl+ z>&)2Oz@T087*cX=&L8M`3NOk?4psB${7@Fdqq+!*ZTY z`Gde>Nly`jfCh%+>s)6fl&%oVSz>6|tzodh26yaXCJl3e3{HlJ4mJh>J;4c@TugrF zoRVf0bY%3*eemdGkJ{S6#H*_hFti2t8t>X5uS zHEh^K2rNkGIg*H?K@<&=1{Hy#p%jrt5lLuv39bg`2v@NU8#bH& zTWkMo^IMV#SXK&f8%P66gHju7tbk=j0ylvKpdu)BV8CB!^R3m7`ZmX@#AeDsjwC|2 zfvBApuUe9$)g6b9hOuBE7Xz1SVv+V|Npo`=KXy>XHWLQ6hrltrEE+qqwn&5h(%F*= zu15x5W8loyXrx4>QzxX^Sq&fFt3sRa1~LtJcUv@f=4_RQ2BpU}(uQy~c#(l-3|I#4 zReD^J^JUV^v<8auwa;Rtz@n`yFGCs`lIp6Yb?wQK|aAlw|9v7iB?@ zflbTY>ZBtA(D2ObijD6;AN66d&&$D(%72Mdk4*);l4F0>i&t!TkO(gB-Ky{q85DD=kToPW4& zz3`!Icxxb95*SD>vH9G$3VsxY47|^Sws)?`q|d3Tck*a3u#N(Yrh#}Dj&TZW9k|7S z&p>=Wd~9_^ru + * @version $Id$ + * @license GPL + * @package WebDAVAdmin + * + * Copyright (c) 2007 Jan Dittberner + * + * This file is part of WebDAVAdmin. + * + * This program 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. + * + * This program 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +/** Include common code. */ +include_once('common.inc.php'); + +header("Content-Type: text/html; charset=UTF-8"); +try { + $dbh = new PDO($dsn, $dbuser, $dbpass); + $query = $dbh->prepare("SELECT firstname, lastname FROM dav_password WHERE username=:username"); + $currentuser = $_SERVER['PHP_AUTH_USER']; + $query->execute(array(":username" => $currentuser)); + $row = $query->fetch(PDO::FETCH_ASSOC); + $smarty->assign("firstname", $row['firstname']); + $smarty->assign("lastname", $row['lastname']); + $smarty->display("start.html"); + $dbh = null; +} catch (PDOException $e) { + $smarty->setErrorMsg($e->getMessage()); + $smarty->display("error.html"); +} +?> \ No newline at end of file diff --git a/admin/scripts/autocomplete.js b/admin/scripts/autocomplete.js new file mode 100644 index 0000000..ad252e7 --- /dev/null +++ b/admin/scripts/autocomplete.js @@ -0,0 +1,305 @@ +// $Id$ + +/* + * Autocompletion for input fields, ideas from Drupal autocompletion + */ + +/** + * Attaches the autocomplete behaviour to all required fields + */ +DAV.autocompleteAutoAttach = function () { + var acdb = []; + $('input.autocomplete').each(function () { + var uri = this.value; + if (!acdb[uri]) { + acdb[uri] = new DAV.ACDB(uri); + } + var input = $('#' + this.id.substr(0, this.id.length - 13)) + .attr('autocomplete', 'OFF')[0]; + $(input.form).submit(DAV.autocompleteSubmit); + new DAV.jsAC(input, acdb[uri]); + }); +} + +/** + * Prevents the form from submitting if the suggestions popup is open + * and closes the suggestions popup when doing so. + */ +DAV.autocompleteSubmit = function () { + return $('#autocomplete').each(function () { + this.owner.hidePopup(); + }).size() == 0; +} + +/** + * An AutoComplete object + */ +DAV.jsAC = function (input, db) { + var ac = this; + this.input = input; + this.db = db; + + $(this.input) + .keydown(function (event) { return ac.onkeydown(this, event); }) + .keyup(function (event) { ac.onkeyup(this, event) }) + .blur(function () { ac.hidePopup(); ac.db.cancel(); }); + +}; + +/** + * Handler for the "keydown" event + */ +DAV.jsAC.prototype.onkeydown = function (input, e) { + if (!e) { + e = window.event; + } + switch (e.keyCode) { + case 40: // down arrow + this.selectDown(); + return false; + case 38: // up arrow + this.selectUp(); + return false; + default: // all other keys + return true; + } +} + +/** + * Handler for the "keyup" event + */ +DAV.jsAC.prototype.onkeyup = function (input, e) { + if (!e) { + e = window.event; + } + switch (e.keyCode) { + case 16: // shift + case 17: // ctrl + case 18: // alt + case 20: // caps lock + case 33: // page up + case 34: // page down + case 35: // end + case 36: // home + case 37: // left arrow + case 38: // up arrow + case 39: // right arrow + case 40: // down arrow + return true; + + case 9: // tab + case 13: // enter + case 27: // esc + this.hidePopup(e.keyCode); + return true; + + default: // all other keys + if (input.value.length > 0) + this.populatePopup(); + else + this.hidePopup(e.keyCode); + return true; + } +} + +/** + * Puts the currently highlighted suggestion into the autocomplete field + */ +DAV.jsAC.prototype.select = function (node) { + this.input.value = node.autocompleteValue; +} + +/** + * Highlights the next suggestion + */ +DAV.jsAC.prototype.selectDown = function () { + if (this.selected && this.selected.nextSibling) { + this.highlight(this.selected.nextSibling); + } + else { + var lis = $('li', this.popup); + if (lis.size() > 0) { + this.highlight(lis.get(0)); + } + } +} + +/** + * Highlights the previous suggestion + */ +DAV.jsAC.prototype.selectUp = function () { + if (this.selected && this.selected.previousSibling) { + this.highlight(this.selected.previousSibling); + } +} + +/** + * Highlights a suggestion + */ +DAV.jsAC.prototype.highlight = function (node) { + if (this.selected) { + $(this.selected).removeClass('selected'); + } + $(node).addClass('selected'); + this.selected = node; +} + +/** + * Unhighlights a suggestion + */ +DAV.jsAC.prototype.unhighlight = function (node) { + $(node).removeClass('selected'); + this.selected = false; +} + +/** + * Hides the autocomplete suggestions + */ +DAV.jsAC.prototype.hidePopup = function (keycode) { + // Select item if the right key or mousebutton was pressed + if (this.selected && ((keycode && keycode != 46 && keycode != 8 && keycode != 27) || !keycode)) { + this.input.value = this.selected.autocompleteValue; + } + // Hide popup + var popup = this.popup; + if (popup) { + this.popup = null; + $(popup).fadeOut('fast', function() { $(popup).remove(); }); + } + this.selected = false; +} + +/** + * Positions the suggestions popup and starts a search + */ +DAV.jsAC.prototype.populatePopup = function () { + // Show popup + if (this.popup) { + $(this.popup).remove(); + } + this.selected = false; + this.popup = document.createElement('div'); + this.popup.id = 'autocomplete'; + this.popup.owner = this; + $(this.popup).css({ + marginTop: this.input.offsetHeight +'px', + width: (this.input.offsetWidth - 4) +'px', + display: 'none' + }); + $(this.input).before(this.popup); + + // Do search + this.db.owner = this; + this.db.search(this.input.value); +} + +/** + * Fills the suggestion popup with any matches received + */ +DAV.jsAC.prototype.found = function (matches) { + // If no value in the textfield, do not show the popup. + if (!this.input.value.length) { + return false; + } + + // Prepare matches + var ul = document.createElement('ul'); + var ac = this; + for (key in matches) { + var li = document.createElement('li'); + $(li) + .html('
'+ matches[key] +'
') + .mousedown(function () { ac.select(this); }) + .mouseover(function () { ac.highlight(this); }) + .mouseout(function () { ac.unhighlight(this); }); + li.autocompleteValue = key; + $(ul).append(li); + } + + // Show popup with matches, if any + if (this.popup) { + if (ul.childNodes.length > 0) { + $(this.popup).empty().append(ul).show(); + } + else { + $(this.popup).css({visibility: 'hidden'}); + this.hidePopup(); + } + } +} + +DAV.jsAC.prototype.setStatus = function (status) { + switch (status) { + case 'begin': + $(this.input).addClass('throbbing'); + break; + case 'cancel': + case 'error': + case 'found': + $(this.input).removeClass('throbbing'); + break; + } +} + +/** + * An AutoComplete DataBase object + */ +DAV.ACDB = function (uri) { + this.uri = uri; + this.delay = 300; + this.cache = {}; +} + +/** + * Performs a cached and delayed search + */ +DAV.ACDB.prototype.search = function (searchString) { + var db = this; + this.searchString = searchString; + + // See if this key has been searched for before + if (this.cache[searchString]) { + return this.owner.found(this.cache[searchString]); + } + + // Initiate delayed search + if (this.timer) { + clearTimeout(this.timer); + } + this.timer = setTimeout(function() { + db.owner.setStatus('begin'); + + // Ajax GET request for autocompletion + $.ajax({ + type: "GET", + url: db.uri +'/'+ DAV.encodeURIComponent(searchString), + success: function (data) { + // Parse back result + var matches = DAV.parseJson(data); + if (typeof matches['status'] == 'undefined' || matches['status'] != 0) { + db.cache[searchString] = matches; + // Verify if these are still the matches the user wants to see + if (db.searchString == searchString) { + db.owner.found(matches); + } + db.owner.setStatus('found'); + } + }, + error: function (xmlhttp) { + alert('An HTTP error '+ xmlhttp.status +' occured.\n'+ db.uri); + } + }); + }, this.delay); +} + +/** + * Cancels the current autocomplete request + */ +DAV.ACDB.prototype.cancel = function() { + if (this.owner) this.owner.setStatus('cancel'); + if (this.timer) clearTimeout(this.timer); + this.searchString = ''; +} + +// Assign autocomplete +$(document).ready(DAV.autocompleteAutoAttach); diff --git a/admin/scripts/directories.js b/admin/scripts/directories.js new file mode 100644 index 0000000..d340fa5 --- /dev/null +++ b/admin/scripts/directories.js @@ -0,0 +1,148 @@ +function handleerror(xmldata) { + if ($('error', xmldata).size()) { + var msg = 'Es ist ein Fehler aufgetreten:\n'; + $('error', xmldata).find('errormsg').each(function(i) { + msg += $(this).text(); + }); + alert(msg); + return true; + } + return false; + $('#content').show(); +} + +function updateDirectories(xmldata) { + var dirname = $('dirname', xmldata).text(); + var groups = $('groups', xmldata).text(); + var filecount = $('filecount', xmldata).text(); + var filesize = $('filesize', xmldata).text(); + var maydelete = $('maydelete', xmldata).text(); + var htmltext = '' + dirname + '' + groups + '' + filecount + ', ' + filesize + 'Gruppen'; + if (maydelete == '1') { + htmltext = htmltext + 'löschen'; + } + htmltext = htmltext + ''; + $('#dirtable').find('tr#dir' + dirname).empty().append(htmltext); + if (!($('#dirtable').find('tr#dir' + dirname).size())) { + var rows = $('#dirtable').find('tr'); + var inserted = false; + for (var i = 0; !inserted && i < rows.length; i++) { + if ($(rows[i]).find('td:first').text() > dirname) { + $(rows[i]).before( + '' + htmltext + ''); + inserted = true; + } + } + if (!inserted) { + $(rows[rows.length-1]).before( + '' + htmltext + ''); + } + } + $('#dirtable').find('tr#dir' + dirname).find('a.editlink').click(function() { + editdirectory(this.id.substr(4)); + }); + $('#dirtable').find('tr#dir' + dirname).find('a.deletelink').click(function() { + removedirectory(this.id.substr(6)); + }); +} + +function getDirectoryForm(title, dirname, groups) { + return '
Schließen ' + title + '
'; +} + +function displaydirectoryeditor(title, dirname, groups) { + $('#content').hide(); + $('#direditor').hide().empty().append(getDirectoryForm(title, dirname, + groups)).show(); + DAV.autocompleteAutoAttach(); + $('#dirform').find('#input-dirname').focus(); + $('#closer').click(function() { + $('#direditor').hide().empty(); + $('#content').show(); + }); + $('#dirform').submit(function() { + if (!this.dirname.value.match(/^[a-zA-Z0-9 -_.]+$/)) { + alert("Ungültiger Verzeichnisname."); + this.dirname.focus(); + return false; + } + $.post( + "/dav/admin/directories.php", + {method : 'submitdirectory', + dirname : this.dirname.value, + groups : this.groups.value}, + function(retval) { + $('div#direditor').hide().empty(); + if (!handleerror(retval)) { + updateDirectories(retval); + $('#content').show(); + } + }); + return false; + }); +} + +function editdirectory(dirname) { + $.get( + "directories.php", + {dirname : dirname, + method : 'getdirectorydata'}, + function(retval) { + if (!handleerror(retval)) { + var dirname, groups; + dirname = $("dirname:first", retval).text(); + groups = $("groups:first", retval).text(); + displaydirectoryeditor("Verzeichnisdaten bearbeiten", + dirname, groups); + } + }); +} + +function newdirectory() { + displaydirectoryeditor('Neues Verzeichnis anlegen', '', ''); +} + +function deletedirectorydialog(dirname) { + $("#direditor").hide().empty(); + var msg = 'Soll das Verzeichnis ' + dirname + + ' wirklich gelöscht werden?'; + if (confirm(msg) == true) { + $.post( + "directories.php", + {method : 'deletedirectory', + dirname : dirname}, + function(retval) { + if (!handleerror(retval)) { + var dirname = $('dirname:first', retval).text(); + $('#dirtable').find('tr#dir' + dirname).remove(); + } + }); + } +} + +function removedirectory(dirname) { + $.get( + "directories.php", + {dirname : dirname, + method : 'getdirectorydata'}, + function(retval) { + if (!handleerror(retval)) { + var username, lastname, firstname; + var dirname, groups; + dirname = $("dirname:first", retval).text(); + deletedirectorydialog(dirname); + } + }); +} + +$(function() { + $("a.editlink").each(function(i) { + $(this).click(function() { editdirectory(this.id.substr(4)); }); + }); + $("a.newlink").each(function(i) { + $(this).click(function() { newdirectory(); }); + }); + $("a.deletelink").each(function(i) { + $(this).click(function() { removedirectory(this.id.substr(6)); }); + }); +}); diff --git a/admin/scripts/helper.js b/admin/scripts/helper.js new file mode 100644 index 0000000..612d210 --- /dev/null +++ b/admin/scripts/helper.js @@ -0,0 +1,29 @@ +// $Id$ + +/** + * Helper code. Ideas from Drupal. + */ + +var DAV = DAV || {}; + +/** + * Parse a JSON response. + * + * The result is either the JSON object, or an object with 'status' 0 and + * 'data' an error message. + */ +DAV.parseJson = function (data) { + if ((data.substring(0, 1) != '{') && (data.substring(0, 1) != '[')) { + return { status: 0, data: data.length ? data : 'Unspecified error' }; + } + return eval('(' + data + ');'); +}; + +/** + * Wrapper to address the mod_rewrite url encoding bug. + */ +DAV.encodeURIComponent = function (item, uri) { + uri = uri || location.href; + item = encodeURIComponent(item).replace('%2F', '/'); + return uri.indexOf('?q=') ? item : item.replace('%26', '%2526').replace('%23', '%2523'); +}; diff --git a/admin/scripts/jquery.js b/admin/scripts/jquery.js new file mode 100644 index 0000000..f954b6b --- /dev/null +++ b/admin/scripts/jquery.js @@ -0,0 +1 @@ +eval(function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('7(1C 1w.6=="T"){1w.T=1w.T;B 6=u(a,c){7(1w==q)v 1p 6(a,c);a=a||17;7(6.1t(a))v 1p 6(17)[6.E.27?"27":"2O"](a);7(1C a=="23"){B m=/^[^<]*(<(.|\\s)+>)[^>]*$/.2Q(a);7(m)a=6.3k([m[1]]);J v 1p 6(c).2o(a)}v q.6r(a.1l==2y&&a||(a.3Y||a.I&&a!=1w&&!a.24&&a[0]!=T&&a[0].24)&&6.3M(a)||[a])};7(1C $!="T")6.2S$=$;B $=6;6.E=6.8p={3Y:"1.1.2",8q:u(){v q.I},I:0,2b:u(1T){v 1T==T?6.3M(q):q[1T]},2r:u(a){B L=6(a);L.6p=q;v L},6r:u(a){q.I=0;[].1g.14(q,a);v q},K:u(E,1E){v 6.K(q,E,1E)},2h:u(1c){B 4c=-1;q.K(u(i){7(q==1c)4c=i});v 4c},1I:u(1Y,O,C){B 1c=1Y;7(1Y.1l==3t)7(O==T)v q.I&&6[C||"1I"](q[0],1Y)||T;J{1c={};1c[1Y]=O}v q.K(u(2h){P(B H 1x 1c)6.1I(C?q.1q:q,H,6.H(q,1c[H],C,2h,H))})},1m:u(1Y,O){v q.1I(1Y,O,"30")},2L:u(e){7(1C e=="23")v q.3u().3r(17.8t(e));B t="";6.K(e||q,u(){6.K(q.2I,u(){7(q.24!=8)t+=q.24!=1?q.60:6.E.2L([q])})});v t},2K:u(){B a=6.3k(1A);v q.K(u(){B b=a[0].3l(U);q.11.2X(b,q);22(b.1b)b=b.1b;b.4C(q)})},3r:u(){v q.3j(1A,U,1,u(a){q.4C(a)})},5i:u(){v q.3j(1A,U,-1,u(a){q.2X(a,q.1b)})},5j:u(){v q.3j(1A,12,1,u(a){q.11.2X(a,q)})},5t:u(){v q.3j(1A,12,-1,u(a){q.11.2X(a,q.2e)})},4g:u(){v q.6p||6([])},2o:u(t){v q.2r(6.31(q,u(a){v 6.2o(t,a)}),t)},4Y:u(4N){v q.2r(6.31(q,u(a){B a=a.3l(4N!=T?4N:U);a.$1H=16;v a}))},1D:u(t){v q.2r(6.1t(t)&&6.2q(q,u(2z,2h){v t.14(2z,[2h])})||6.3z(t,q))},2g:u(t){v q.2r(t.1l==3t&&6.3z(t,q,U)||6.2q(q,u(a){v(t.1l==2y||t.3Y)?6.3y(a,t)<0:a!=t}))},1M:u(t){v q.2r(6.2k(q.2b(),t.1l==3t?6(t).2b():t.I!=T&&(!t.1f||t.1f=="8v")?t:[t]))},4l:u(1s){v 1s?6.1D(1s,q).r.I>0:12},1a:u(1a){v 1a==T?(q.I?q[0].O:16):q.1I("O",1a)},4U:u(1a){v 1a==T?(q.I?q[0].2t:16):q.3u().3r(1a)},3j:u(1E,1P,3Z,E){B 4Y=q.I>1;B a=6.3k(1E);7(3Z<0)a.8w();v q.K(u(){B 1c=q;7(1P&&6.1f(q,"1P")&&6.1f(a[0],"3m"))1c=q.5J("20")[0]||q.4C(17.6n("20"));6.K(a,u(){E.14(1c,[4Y?q.3l(U):q])})})}};6.1z=6.E.1z=u(){B 1O=1A[0],a=1;7(1A.I==1){1O=q;a=0}B H;22(H=1A[a++])P(B i 1x H)1O[i]=H[i];v 1O};6.1z({8x:u(){7(6.2S$)$=6.2S$;v 6},1t:u(E){v!!E&&1C E!="23"&&!E.1f&&1C E[0]=="T"&&/u/i.1n(E+"")},4B:u(D){v D.66&&D.5I&&!D.5I.64},1f:u(D,Y){v D.1f&&D.1f.3K()==Y.3K()},K:u(1c,E,1E){7(1c.I==T)P(B i 1x 1c)E.14(1c[i],1E||[i,1c[i]]);J P(B i=0,6q=1c.I;i<6q;i++)7(E.14(1c[i],1E||[i,1c[i]])===12)3O;v 1c},H:u(D,O,C,2h,H){7(6.1t(O))O=O.3n(D,[2h]);B 6s=/z-?2h|7P-?8A|1d|58|8B-?28/i;v O&&O.1l==3Q&&C=="30"&&!6s.1n(H)?O+"4S":O},19:{1M:u(D,c){6.K(c.3o(/\\s+/),u(i,Q){7(!6.19.2V(D.19,Q))D.19+=(D.19?" ":"")+Q})},2f:u(D,c){D.19=c?6.2q(D.19.3o(/\\s+/),u(Q){v!6.19.2V(c,Q)}).6t(" "):""},2V:u(t,c){t=t.19||t;c=c.1R(/([\\.\\\\\\+\\*\\?\\[\\^\\]\\$\\(\\)\\{\\}\\=\\!\\<\\>\\|\\:])/g,"\\\\$1");v t&&1p 4v("(^|\\\\s)"+c+"(\\\\s|$)").1n(t)}},4d:u(e,o,f){P(B i 1x o){e.1q["1N"+i]=e.1q[i];e.1q[i]=o[i]}f.14(e,[]);P(B i 1x o)e.1q[i]=e.1q["1N"+i]},1m:u(e,p){7(p=="28"||p=="3V"){B 1N={},46,3P,d=["7d","8C","8D","8E"];6.K(d,u(){1N["8F"+q]=0;1N["8G"+q+"8H"]=0});6.4d(e,1N,u(){7(6.1m(e,"1h")!="1Z"){46=e.8I;3P=e.8J}J{e=6(e.3l(U)).2o(":4j").5l("2Z").4g().1m({4n:"1G",45:"8K",1h:"2D",7I:"0",8M:"0"}).5z(e.11)[0];B 3d=6.1m(e.11,"45");7(3d==""||3d=="4b")e.11.1q.45="6x";46=e.6y;3P=e.6z;7(3d==""||3d=="4b")e.11.1q.45="4b";e.11.33(e)}});v p=="28"?46:3P}v 6.30(e,p)},30:u(D,H,53){B L;7(H=="1d"&&6.W.1j)v 6.1I(D.1q,"1d");7(H=="4h"||H=="2v")H=6.W.1j?"3T":"2v";7(!53&&D.1q[H])L=D.1q[H];J 7(17.44&&17.44.4W){7(H=="2v"||H=="3T")H="4h";H=H.1R(/([A-Z])/g,"-$1").4m();B Q=17.44.4W(D,16);7(Q)L=Q.55(H);J 7(H=="1h")L="1Z";J 6.4d(D,{1h:"2D"},u(){B c=17.44.4W(q,"");L=c&&c.55(H)||""})}J 7(D.51){B 56=H.1R(/\\-(\\w)/g,u(m,c){v c.3K()});L=D.51[H]||D.51[56]}v L},3k:u(a){B r=[];6.K(a,u(i,1r){7(!1r)v;7(1r.1l==3Q)1r=1r.6C();7(1C 1r=="23"){B s=6.35(1r),1V=17.6n("1V"),2i=[];B 2K=!s.18("<1u")&&[1,"<42>",""]||(!s.18("<6D")||!s.18("<20")||!s.18("<6E"))&&[1,"<1P>",""]||!s.18("<3m")&&[2,"<1P><20>",""]||(!s.18("<6F")||!s.18("<6G"))&&[3,"<1P><20><3m>",""]||[0,"",""];1V.2t=2K[1]+s+2K[2];22(2K[0]--)1V=1V.1b;7(6.W.1j){7(!s.18("<1P")&&s.18("<20")<0)2i=1V.1b&&1V.1b.2I;J 7(2K[1]=="<1P>"&&s.18("<20")<0)2i=1V.2I;P(B n=2i.I-1;n>=0;--n)7(6.1f(2i[n],"20")&&!2i[n].2I.I)2i[n].11.33(2i[n])}1r=[];P(B i=0,l=1V.2I.I;im[3]-0",2a:"m[3]-0==i",5q:"m[3]-0==i",2u:"i==0",2T:"i==r.I-1",5R:"i%2==0",5S:"i%2","2a-3s":"6.2a(a.11.1b,m[3],\'2e\',a)==a","2u-3s":"6.2a(a.11.1b,1,\'2e\')==a","2T-3s":"6.2a(a.11.7n,1,\'5s\')==a","7p-3s":"6.2B(a.11.1b).I==1",5u:"a.1b",3u:"!a.1b",5v:"6.E.2L.14([a]).18(m[3])>=0",3i:\'a.C!="1G"&&6.1m(a,"1h")!="1Z"&&6.1m(a,"4n")!="1G"\',1G:\'a.C=="1G"||6.1m(a,"1h")=="1Z"||6.1m(a,"4n")=="1G"\',7v:"!a.2W",2W:"a.2W",2Z:"a.2Z",2Y:"a.2Y||6.1I(a,\'2Y\')",2L:"a.C==\'2L\'",4j:"a.C==\'4j\'",5x:"a.C==\'5x\'",4G:"a.C==\'4G\'",5y:"a.C==\'5y\'",4R:"a.C==\'4R\'",5A:"a.C==\'5A\'",5B:"a.C==\'5B\'",3x:\'a.C=="3x"||6.1f(a,"3x")\',5C:"/5C|42|7A|3x/i.1n(a.1f)"},".":"6.19.2V(a,m[2])","@":{"=":"z==m[4]","!=":"z!=m[4]","^=":"z&&!z.18(m[4])","$=":"z&&z.2U(z.I - m[4].I,m[4].I)==m[4]","*=":"z&&z.18(m[4])>=0","":"z",4u:u(m){v["",m[1],m[3],m[2],m[5]]},5P:"z=a[m[3]];7(!z||/5E|3e/.1n(m[3]))z=6.1I(a,m[3]);"},"[":"6.2o(m[2],a).I"},5M:[/^\\[ *(@)([a-2m-3C-]*) *([!*$^=]*) *(\'?"?)(.*?)\\4 *\\]/i,/^(\\[)\\s*(.*?(\\[.*?\\])?[^[]*?)\\s*\\]/,/^(:)([a-2m-3C-]*)\\("?\'?(.*?(\\(.*?\\))?[^(]*?)"?\'?\\)/i,/^([:.#]*)([a-2m-3C*-]*)/i],1Q:[/^(\\/?\\.\\.)/,"a.11",/^(>|\\/)/,"6.2B(a.1b)",/^(\\+)/,"6.2a(a,2,\'2e\')",/^(~)/,u(a){B s=6.2B(a.11.1b);v s.3N(6.3y(a,s)+1)}],3z:u(1s,1U,2g){B 1N,Q=[];22(1s&&1s!=1N){1N=1s;B f=6.1D(1s,1U,2g);1s=f.t.1R(/^\\s*,\\s*/,"");Q=2g?1U=f.r:6.2k(Q,f.r)}v Q},2o:u(t,1B){7(1C t!="23")v[t];7(1B&&!1B.24)1B=16;1B=1B||17;7(!t.18("//")){1B=1B.4H;t=t.2U(2,t.I)}J 7(!t.18("/")){1B=1B.4H;t=t.2U(1,t.I);7(t.18("/")>=1)t=t.2U(t.18("/"),t.I)}B L=[1B],2c=[],2T=16;22(t&&2T!=t){B r=[];2T=t;t=6.35(t).1R(/^\\/\\//i,"");B 3B=12;B 1J=/^[\\/>]\\s*([a-2m-9*-]+)/i;B m=1J.2Q(t);7(m){6.K(L,u(){P(B c=q.1b;c;c=c.2e)7(c.24==1&&(6.1f(c,m[1])||m[1]=="*"))r.1g(c)});L=r;t=t.1R(1J,"");7(t.18(" ")==0)5F;3B=U}J{P(B i=0;i<6.1Q.I;i+=2){B 1J=6.1Q[i];B m=1J.2Q(t);7(m){r=L=6.31(L,6.1t(6.1Q[i+1])?6.1Q[i+1]:u(a){v 40(6.1Q[i+1])});t=6.35(t.1R(1J,""));3B=U;3O}}}7(t&&!3B){7(!t.18(",")){7(L[0]==1B)L.4L();6.2k(2c,L);r=L=[1B];t=" "+t.2U(1,t.I)}J{B 34=/^([a-2m-3C-]+)(#)([a-2m-9\\\\*2S-]*)/i;B m=34.2Q(t);7(m){m=[0,m[2],m[3],m[1]]}J{34=/^([#.]?)([a-2m-9\\\\*2S-]*)/i;m=34.2Q(t)}7(m[1]=="#"&&L[L.I-1].4X){B 2l=L[L.I-1].4X(m[2]);7(6.W.1j&&2l&&2l.2J!=m[2])2l=6(\'[@2J="\'+m[2]+\'"]\',L[L.I-1])[0];L=r=2l&&(!m[3]||6.1f(2l,m[3]))?[2l]:[]}J{7(m[1]==".")B 4r=1p 4v("(^|\\\\s)"+m[2]+"(\\\\s|$)");6.K(L,u(){B 3E=m[1]!=""||m[0]==""?"*":m[2];7(6.1f(q,"7J")&&3E=="*")3E="3g";6.2k(r,m[1]!=""&&L.I!=1?6.4x(q,[],m[1],m[2],4r):q.5J(3E))});7(m[1]=="."&&L.I==1)r=6.2q(r,u(e){v 4r.1n(e.19)});7(m[1]=="#"&&L.I==1){B 5K=r;r=[];6.K(5K,u(){7(q.36("2J")==m[2]){r=[q];v 12}})}L=r}t=t.1R(34,"")}}7(t){B 1a=6.1D(t,r);L=r=1a.r;t=6.35(1a.t)}}7(L&&L[0]==1B)L.4L();6.2k(2c,L);v 2c},1D:u(t,r,2g){22(t&&/^[a-z[({<*:.#]/i.1n(t)){B p=6.5M,m;6.K(p,u(i,1J){m=1J.2Q(t);7(m){t=t.7M(m[0].I);7(6.1s[m[1]].4u)m=6.1s[m[1]].4u(m);v 12}});7(m[1]==":"&&m[2]=="2g")r=6.1D(m[3],r,U).r;J 7(m[1]=="."){B 1J=1p 4v("(^|\\\\s)"+m[2]+"(\\\\s|$)");r=6.2q(r,u(e){v 1J.1n(e.19||"")},2g)}J{B f=6.1s[m[1]];7(1C f!="23")f=6.1s[m[1]][m[2]];40("f = u(a,i){"+(6.1s[m[1]].5P||"")+"v "+f+"}");r=6.2q(r,f,2g)}}v{r:r,t:t}},4x:u(o,r,1Q,Y,1J){P(B s=o.1b;s;s=s.2e)7(s.24==1){B 1M=U;7(1Q==".")1M=s.19&&1J.1n(s.19);J 7(1Q=="#")1M=s.36("2J")==Y;7(1M)r.1g(s);7(1Q=="#"&&r.I)3O;7(s.1b)6.4x(s,r,1Q,Y,1J)}v r},4z:u(D){B 4A=[];B Q=D.11;22(Q&&Q!=17){4A.1g(Q);Q=Q.11}v 4A},2a:u(Q,1i,3Z,D){1i=1i||1;B 1T=0;P(;Q;Q=Q[3Z]){7(Q.24==1)1T++;7(1T==1i||1i=="5R"&&1T%2==0&&1T>1&&Q==D||1i=="5S"&&1T%2==1&&Q==D)v Q}},2B:u(n,D){B r=[];P(;n;n=n.2e){7(n.24==1&&(!D||n!=D))r.1g(n)}v r}});6.G={1M:u(S,C,1o,F){7(6.W.1j&&S.3L!=T)S=1w;7(F)1o.F=F;7(!1o.2A)1o.2A=q.2A++;7(!S.$1H)S.$1H={};B 38=S.$1H[C];7(!38){38=S.$1H[C]={};7(S["39"+C])38[0]=S["39"+C]}38[1o.2A]=1o;S["39"+C]=q.5Y;7(!q.1k[C])q.1k[C]=[];q.1k[C].1g(S)},2A:1,1k:{},2f:u(S,C,1o){7(S.$1H){B i,j,k;7(C&&C.C){1o=C.1o;C=C.C}7(C&&S.$1H[C])7(1o)5U S.$1H[C][1o.2A];J P(i 1x S.$1H[C])5U S.$1H[C][i];J P(j 1x S.$1H)q.2f(S,j);P(k 1x S.$1H[C])7(k){k=U;3O}7(!k)S["39"+C]=16}},1S:u(C,F,S){F=6.3M(F||[]);7(!S)6.K(q.1k[C]||[],u(){6.G.1S(C,F,q)});J{B 1o=S["39"+C],1a,E=6.1t(S[C]);7(1o){F.61(q.2j({C:C,1O:S}));7((1a=1o.14(S,F))!==12)q.4F=U}7(E&&1a!==12)S[C]();q.4F=12}},5Y:u(G){7(1C 6=="T"||6.G.4F)v;G=6.G.2j(G||1w.G||{});B 3R;B c=q.$1H[G.C];B 1E=[].3N.3n(1A,1);1E.61(G);P(B j 1x c){1E[0].1o=c[j];1E[0].F=c[j].F;7(c[j].14(q,1E)===12){G.2n();G.2H();3R=12}}7(6.W.1j)G.1O=G.2n=G.2H=G.1o=G.F=16;v 3R},2j:u(G){7(!G.1O&&G.63)G.1O=G.63;7(G.65==T&&G.67!=T){B e=17.4H,b=17.64;G.65=G.67+(e.68||b.68);G.7Y=G.7Z+(e.6c||b.6c)}7(6.W.2N&&G.1O.24==3){B 3a=G;G=6.1z({},3a);G.1O=3a.1O.11;G.2n=u(){v 3a.2n()};G.2H=u(){v 3a.2H()}}7(!G.2n)G.2n=u(){q.3R=12};7(!G.2H)G.2H=u(){q.82=U};v G}};6.E.1z({3U:u(C,F,E){v q.K(u(){6.G.1M(q,C,E||F,F)})},6u:u(C,F,E){v q.K(u(){6.G.1M(q,C,u(G){6(q).6f(G);v(E||F).14(q,1A)},F)})},6f:u(C,E){v q.K(u(){6.G.2f(q,C,E)})},1S:u(C,F){v q.K(u(){6.G.1S(C,F,q)})},3X:u(){B a=1A;v q.6j(u(e){q.4M=q.4M==0?1:0;e.2n();v a[q.4M].14(q,[e])||12})},83:u(f,g){u 4O(e){B p=(e.C=="41"?e.84:e.85)||e.86;22(p&&p!=q)2G{p=p.11}2w(e){p=q};7(p==q)v 12;v(e.C=="41"?f:g).14(q,[e])}v q.41(4O).6k(4O)},27:u(f){7(6.3W)f.14(17,[6]);J{6.3c.1g(u(){v f.14(q,[6])})}v q}});6.1z({3W:12,3c:[],27:u(){7(!6.3W){6.3W=U;7(6.3c){6.K(6.3c,u(){q.14(17)});6.3c=16}7(6.W.3h||6.W.3f)17.87("6o",6.27,12)}}});1p u(){6.K(("88,8a,2O,8b,8d,52,6j,8e,"+"8f,8g,8h,41,6k,8j,42,"+"4R,8k,8l,8m,2C").3o(","),u(i,o){6.E[o]=u(f){v f?q.3U(o,f):q.1S(o)}});7(6.W.3h||6.W.3f)17.8n("6o",6.27,12);J 7(6.W.1j){17.8o("<8r"+"8s 2J=62 8u=U "+"3e=//:><\\/2d>");B 2d=17.4X("62");7(2d)2d.37=u(){7(q.3D!="1X")v;q.11.33(q);6.27()};2d=16}J 7(6.W.2N)6.50=3L(u(){7(17.3D=="8y"||17.3D=="1X"){4p(6.50);6.50=16;6.27()}},10);6.G.1M(1w,"2O",6.27)};7(6.W.1j)6(1w).6u("52",u(){B 1k=6.G.1k;P(B C 1x 1k){B 4Z=1k[C],i=4Z.I;7(i&&C!=\'52\')6w 6.G.2f(4Z[i-1],C);22(--i)}});6.E.1z({6A:u(V,21,M){q.2O(V,21,M,1)},2O:u(V,21,M,1W){7(6.1t(V))v q.3U("2O",V);M=M||u(){};B C="5d";7(21)7(6.1t(21)){M=21;21=16}J{21=6.3g(21);C="5V"}B 4e=q;6.3v({V:V,C:C,F:21,1W:1W,1X:u(2P,15){7(15=="2M"||!1W&&15=="5L")4e.1I("2t",2P.3G).4V().K(M,[2P.3G,15,2P]);J M.14(4e,[2P.3G,15,2P])}});v q},6B:u(){v 6.3g(q)},4V:u(){v q.2o("2d").K(u(){7(q.3e)6.59(q.3e);J 6.4a(q.2L||q.6H||q.2t||"")}).4g()}});7(!1w.3p)3p=u(){v 1p 6I("6K.6M")};6.K("5m,5Q,5O,5W,5N,5H".3o(","),u(i,o){6.E[o]=u(f){v q.3U(o,f)}});6.1z({2b:u(V,F,M,C,1W){7(6.1t(F)){M=F;F=16}v 6.3v({V:V,F:F,2M:M,4t:C,1W:1W})},6Q:u(V,F,M,C){v 6.2b(V,F,M,C,1)},59:u(V,M){v 6.2b(V,16,M,"2d")},6S:u(V,F,M){v 6.2b(V,F,M,"6m")},6U:u(V,F,M,C){7(6.1t(F)){M=F;F={}}v 6.3v({C:"5V",V:V,F:F,2M:M,4t:C})},6X:u(29){6.3q.29=29},6Y:u(5c){6.1z(6.3q,5c)},3q:{1k:U,C:"5d",29:0,5r:"70/x-73-3w-77",5h:U,48:U,F:16},3S:{},3v:u(s){s=6.1z({},6.3q,s);7(s.F){7(s.5h&&1C s.F!="23")s.F=6.3g(s.F);7(s.C.4m()=="2b"){s.V+=((s.V.18("?")>-1)?"&":"?")+s.F;s.F=16}}7(s.1k&&!6.4E++)6.G.1S("5m");B 4y=12;B N=1p 3p();N.7j(s.C,s.V,s.48);7(s.F)N.3A("7l-7m",s.5r);7(s.1W)N.3A("7o-4K-7q",6.3S[s.V]||"7s, 7t 7w 7x 4o:4o:4o 7z");N.3A("X-7B-7C","3p");7(N.7E)N.3A("7F","7G");7(s.5G)s.5G(N);7(s.1k)6.G.1S("5H",[N,s]);B 37=u(4s){7(N&&(N.3D==4||4s=="29")){4y=U;7(3I){4p(3I);3I=16}B 15;2G{15=6.5Z(N)&&4s!="29"?s.1W&&6.69(N,s.V)?"5L":"2M":"2C";7(15!="2C"){B 3F;2G{3F=N.4P("6b-4K")}2w(e){}7(s.1W&&3F)6.3S[s.V]=3F;B F=6.6i(N,s.4t);7(s.2M)s.2M(F,15);7(s.1k)6.G.1S("5N",[N,s])}J 6.3J(s,N,15)}2w(e){15="2C";6.3J(s,N,15,e)}7(s.1k)6.G.1S("5O",[N,s]);7(s.1k&&!--6.4E)6.G.1S("5Q");7(s.1X)s.1X(N,15);7(s.48)N=16}};B 3I=3L(37,13);7(s.29>0)57(u(){7(N){N.7N();7(!4y)37("29")}},s.29);2G{N.7Q(s.F)}2w(e){6.3J(s,N,16,e)}7(!s.48)37();v N},3J:u(s,N,15,e){7(s.2C)s.2C(N,15,e);7(s.1k)6.G.1S("5W",[N,s,e])},4E:0,5Z:u(r){2G{v!r.15&&7V.7W=="4G:"||(r.15>=5X&&r.15<7X)||r.15==6d||6.W.2N&&r.15==T}2w(e){}v 12},69:u(N,V){2G{B 6e=N.4P("6b-4K");v N.15==6d||6e==6.3S[V]||6.W.2N&&N.15==T}2w(e){}v 12},6i:u(r,C){B 4Q=r.4P("8c-C");B F=!C&&4Q&&4Q.18("N")>=0;F=C=="N"||F?r.8i:r.3G;7(C=="2d")6.4a(F);7(C=="6m")40("F = "+F);7(C=="4U")6("<1V>").4U(F).4V();v F},3g:u(a){B s=[];7(a.1l==2y||a.3Y)6.K(a,u(){s.1g(2x(q.Y)+"="+2x(q.O))});J P(B j 1x a)7(a[j]&&a[j].1l==2y)6.K(a[j],u(){s.1g(2x(j)+"="+2x(q))});J s.1g(2x(j)+"="+2x(a[j]));v s.6t("&")},4a:u(F){7(1w.54)1w.54(F);J 7(6.W.2N)1w.57(F,0);J 40.3n(1w,F)}});6.E.1z({1L:u(R,M){B 1G=q.1D(":1G");R?1G.26({28:"1L",3V:"1L",1d:"1L"},R,M):1G.K(u(){q.1q.1h=q.2E?q.2E:"";7(6.1m(q,"1h")=="1Z")q.1q.1h="2D"});v q},1K:u(R,M){B 3i=q.1D(":3i");R?3i.26({28:"1K",3V:"1K",1d:"1K"},R,M):3i.K(u(){q.2E=q.2E||6.1m(q,"1h");7(q.2E=="1Z")q.2E="2D";q.1q.1h="1Z"});v q},5g:6.E.3X,3X:u(E,4I){B 1E=1A;v 6.1t(E)&&6.1t(4I)?q.5g(E,4I):q.K(u(){6(q)[6(q).4l(":1G")?"1L":"1K"].14(6(q),1E)})},7a:u(R,M){v q.26({28:"1L"},R,M)},7c:u(R,M){v q.26({28:"1K"},R,M)},7f:u(R,M){v q.K(u(){B 5k=6(q).4l(":1G")?"1L":"1K";6(q).26({28:5k},R,M)})},7r:u(R,M){v q.26({1d:"1L"},R,M)},7u:u(R,M){v q.26({1d:"1K"},R,M)},7y:u(R,43,M){v q.26({1d:43},R,M)},26:u(H,R,1v,M){v q.1F(u(){q.2F=6.1z({},H);B 1u=6.R(R,1v,M);P(B p 1x H){B e=1p 6.3b(q,1u,p);7(H[p].1l==3Q)e.2s(e.Q(),H[p]);J e[H[p]](H)}})},1F:u(C,E){7(!E){E=C;C="3b"}v q.K(u(){7(!q.1F)q.1F={};7(!q.1F[C])q.1F[C]=[];q.1F[C].1g(E);7(q.1F[C].I==1)E.14(q)})}});6.1z({R:u(R,1v,E){B 1u=R&&R.1l==7K?R:{1X:E||!E&&1v||6.1t(R)&&R,25:R,1v:E&&1v||1v&&1v.1l!=4w&&1v};1u.25=(1u.25&&1u.25.1l==3Q?1u.25:{7R:7S,7T:5X}[1u.25])||7U;1u.1N=1u.1X;1u.1X=u(){6.6a(q,"3b");7(6.1t(1u.1N))1u.1N.14(q)};v 1u},1v:{},1F:{},6a:u(D,C){C=C||"3b";7(D.1F&&D.1F[C]){D.1F[C].4L();B f=D.1F[C][0];7(f)f.14(D)}},3b:u(D,1e,H){B z=q;B y=D.1q;B 4D=6.1m(D,"1h");y.5T="1G";z.a=u(){7(1e.49)1e.49.14(D,[z.2p]);7(H=="1d")6.1I(y,"1d",z.2p);J 7(6l(z.2p))y[H]=6l(z.2p)+"4S";y.1h="2D"};z.6v=u(){v 4T(6.1m(D,H))};z.Q=u(){B r=4T(6.30(D,H));v r&&r>-8z?r:z.6v()};z.2s=u(4f,43){z.4J=(1p 5o()).5w();z.2p=4f;z.a();z.4q=3L(u(){z.49(4f,43)},13)};z.1L=u(){7(!D.1y)D.1y={};D.1y[H]=q.Q();1e.1L=U;z.2s(0,D.1y[H]);7(H!="1d")y[H]="5a"};z.1K=u(){7(!D.1y)D.1y={};D.1y[H]=q.Q();1e.1K=U;z.2s(D.1y[H],0)};z.3X=u(){7(!D.1y)D.1y={};D.1y[H]=q.Q();7(4D=="1Z"){1e.1L=U;7(H!="1d")y[H]="5a";z.2s(0,D.1y[H])}J{1e.1K=U;z.2s(D.1y[H],0)}};z.49=u(32,47){B t=(1p 5o()).5w();7(t>1e.25+z.4J){4p(z.4q);z.4q=16;z.2p=47;z.a();7(D.2F)D.2F[H]=U;B 2c=U;P(B i 1x D.2F)7(D.2F[i]!==U)2c=12;7(2c){y.5T="";y.1h=4D;7(6.1m(D,"1h")=="1Z")y.1h="2D";7(1e.1K)y.1h="1Z";7(1e.1K||1e.1L)P(B p 1x D.2F)7(p=="1d")6.1I(y,p,D.1y[p]);J y[p]=""}7(2c&&6.1t(1e.1X))1e.1X.14(D)}J{B n=t-q.4J;B p=n/1e.25;z.2p=1e.1v&&6.1v[1e.1v]?6.1v[1e.1v](p,n,32,(47-32),1e.25):((-6h.7O(p*6h.8L)/2)+0.5)*(47-32)+32;z.a()}}}})}',62,545,'||||||jQuery|if|||||||||||||||||||this||||function|return||||||var|type|elem|fn|data|event|prop|length|else|each|ret|callback|xml|value|for|cur|speed|element|undefined|true|url|browser||name|||parentNode|false||apply|status|null|document|indexOf|className|val|firstChild|obj|opacity|options|nodeName|push|display|result|msie|global|constructor|css|test|handler|new|style|arg|expr|isFunction|opt|easing|window|in|orig|extend|arguments|context|typeof|filter|args|queue|hidden|events|attr|re|hide|show|add|old|target|table|token|replace|trigger|num|elems|div|ifModified|complete|key|none|tbody|params|while|string|nodeType|duration|animate|ready|height|timeout|nth|get|done|script|nextSibling|remove|not|index|tb|fix|merge|oid|z0|preventDefault|find|now|grep|pushStack|custom|innerHTML|first|cssFloat|catch|encodeURIComponent|Array|el|guid|sibling|error|block|oldblock|curAnim|try|stopPropagation|childNodes|id|wrap|text|success|safari|load|res|exec|al|_|last|substr|has|disabled|insertBefore|selected|checked|curCSS|map|firstNum|removeChild|re2|trim|getAttribute|onreadystatechange|handlers|on|originalEvent|fx|readyList|parPos|src|opera|param|mozilla|visible|domManip|clean|cloneNode|tr|call|split|XMLHttpRequest|ajaxSettings|append|child|String|empty|ajax|form|button|inArray|multiFilter|setRequestHeader|foundToken|9_|readyState|tag|modRes|responseText|second|ival|handleError|toUpperCase|setInterval|makeArray|slice|break|oWidth|Number|returnValue|lastModified|styleFloat|bind|width|isReady|toggle|jquery|dir|eval|mouseover|select|to|defaultView|position|oHeight|lastNum|async|step|globalEval|static|pos|swap|self|from|end|float|alpha|radio|inv|is|toLowerCase|visibility|00|clearInterval|timer|rec|isTimeout|dataType|_resort|RegExp|Function|getAll|requestDone|parents|matched|isXMLDoc|appendChild|oldDisplay|active|triggered|file|documentElement|fn2|startTime|Modified|shift|lastToggle|deep|handleHover|getResponseHeader|ct|submit|px|parseFloat|html|evalScripts|getComputedStyle|getElementById|clone|els|safariTimer|currentStyle|unload|force|execScript|getPropertyValue|newProp|setTimeout|zoom|getScript|1px|sl|settings|GET|rl|check|_toggle|processData|prepend|before|state|removeAttr|ajaxStart|lt|Date|gt|eq|contentType|previousSibling|after|parent|contains|getTime|checkbox|password|appendTo|image|reset|input|webkit|href|continue|beforeSend|ajaxSend|ownerDocument|getElementsByTagName|tmp|notmodified|parse|ajaxSuccess|ajaxComplete|_prefix|ajaxStop|even|odd|overflow|delete|POST|ajaxError|200|handle|httpSuccess|nodeValue|unshift|__ie_init|srcElement|body|pageX|tagName|clientX|scrollLeft|httpNotModified|dequeue|Last|scrollTop|304|xmlRes|unbind|100|Math|httpData|click|mouseout|parseInt|json|createElement|DOMContentLoaded|prevObject|ol|setArray|exclude|join|one|max|do|relative|clientHeight|clientWidth|loadIfModified|serialize|toString|thead|tfoot|td|th|textContent|ActiveXObject|htmlFor|Microsoft|class|XMLHTTP|readOnly|gi|match|getIfModified|9999|getJSON|getAttributeNode|post|setAttribute|ig|ajaxTimeout|ajaxSetup|concat|application|userAgent|compatible|www|compatMode|CSS1Compat|next|urlencoded|siblings|children|slideDown|prependTo|slideUp|Top|insertAfter|slideToggle|removeAttribute|addClass|removeClass|open|toggleClass|Content|Type|lastChild|If|only|Since|fadeIn|Thu|01|fadeOut|enabled|Jan|1970|fadeTo|GMT|textarea|Requested|With|prev|overrideMimeType|Connection|close|boxModel|right|object|Object|navigator|substring|abort|cos|font|send|slow|600|fast|400|location|protocol|300|pageY|clientY|method|action|cancelBubble|hover|fromElement|toElement|relatedTarget|removeEventListener|blur|readonly|focus|resize|content|scroll|dblclick|mousedown|mouseup|mousemove|responseXML|change|keydown|keypress|keyup|addEventListener|write|prototype|size|scr|ipt|createTextNode|defer|FORM|reverse|noConflict|loaded|10000|weight|line|Bottom|Right|Left|padding|border|Width|offsetHeight|offsetWidth|absolute|PI|left'.split('|'),0,{})) diff --git a/admin/scripts/users.js b/admin/scripts/users.js new file mode 100644 index 0000000..58f8a65 --- /dev/null +++ b/admin/scripts/users.js @@ -0,0 +1,189 @@ +function handleerror(xmldata) { + if ($('error', xmldata).size()) { + var msg = 'Es ist ein Fehler aufgetreten:\n'; + $('error', xmldata).find('errormsg').each(function(i) { + msg += $(this).text(); + }); + alert(msg); + $('#content').show(); + return true; + } + return false; +} + +function updateusers(xmldata) { + var uid = $(xmldata).find('uid').text(); + var username = $(xmldata).find('username').text(); + var firstname = $(xmldata).find('firstname').text(); + var lastname = $(xmldata).find('lastname').text(); + var loggedin = $(xmldata).find('loggedin').text(); + var htmltext = '' + username + '' + lastname + ', ' + firstname + 'bearbeiten'; + if (loggedin == '0') { + htmltext = htmltext + 'löschen'; + } + htmltext = htmltext + ''; + $('#usertable').find('tr#uid' + uid).empty().append(htmltext); + if (!($('#usertable').find('tr#uid' + uid).size())) { + var rows = $('#usertable').find('tr'); + var inserted = false; + for (var i = 0; !inserted && i < rows.length; i++) { + if ($(rows[i]).find('td:first').text() > username) { + $(rows[i]).before( + '' + htmltext + ''); + inserted = true; + } + } + if (!inserted) { + $(rows[rows.length-1]).before( + '' + htmltext + ''); + } + } + $('#usertable').find('tr#uid' + uid).find('a.editlink').click(function() { + edituser(this.id.substr(4)); + }); + $('#usertable').find('tr#uid' + uid).find('a.deletelink').click(function() { + deleteuser(this.id.substr(6)); + }); + $('#content').show(); +} + +function getEditUserForm(title, username, firstname, lastname, groups, userid) { + var retval; + retval = '
Schließen ' + title + '
'; + return retval; +} + +function displayusereditor(title, userid, username, firstname, lastname, groups) { + $('#content').hide(); + $('#usereditor').hide().empty().append(getEditUserForm(title, username, firstname, lastname, groups, userid)).show(); + DAV.autocompleteAutoAttach(); + $('#closer').click(function() { + $('#usereditor').hide().empty(); + $('#content').show(); + }); + $('#userform').find('#input-username').focus(); + $('#userform').submit(function() { + var params; + if (userid == null) { + if (!this.username.value.match(/^[a-zA-Z0-9]{2,}$/)) { + alert('Der Nutzername muss aus mindestens 2 Buchstaben oder Ziffern bestehen und darf keine sonstigen Zeichen enthalten.'); + this.username.focus(); + return false; + } + } + if (userid == null || this.pwd1.value.length > 0) { + if (this.pwd1.value.length < 8) { + alert('Das Passwort muss mindestens 8 Zeichen lang sein!'); + this.pwd1.focus(); + return false; + } + if (this.pwd1.value != this.pwd2.value) { + alert('Passwort und Wiederholung müssen übereinstimmen!'); + this.pwd2.focus(); + return false; + } + } + if (!this.groups.value.match(/^([0-9a-zA-z]+[,\s]*)+$/)) { + alert('Die Gruppenangabe muss eine durch Kommata getrennte Liste von Gruppennamen, die aus Buchstaben und Ziffern zusammengesetzt sein können, sein.'); + this.groups.focus(); + return false; + } + if (userid == null) { + params = {method : 'submituser', + username : this.username.value, + firstname : this.firstname.value, + lastname : this.lastname.value, + password : this.pwd1.value, + groups : this.groups.value}; + } else { + params = {method : 'submituser', + uid : userid, + username : this.username.value, + firstname : this.firstname.value, + lastname : this.lastname.value, + password : this.pwd1.value, + groups : this.groups.value}; + } + $.post( + "users.php", params, + function(retval) { + $('div#usereditor').hide().empty(); + if (!handleerror(retval)) { + updateusers(retval); + } + }); + return false; + }); +} + +function deleteuserdialog(userid, username, firstname, lastname) { + $("#usereditor").hide().empty(); + var msg = 'Soll der Nutzer ' + firstname + ' ' + lastname + + ' mit dem Login ' + username + ' wirklich gelöscht werden?'; + if (confirm(msg) == true) { + $.post( + "users.php", + {method : 'deleteuser', + uid : userid}, + function(retval) { + if (!handleerror(retval)) { + var deluid = $('uid:first', retval).text(); + $('#usertable').find('tr#uid' + deluid).remove(); + } + }); + } +} + +function deleteuser(userid) { + $.get( + "users.php", + {uid : userid, + method : 'getuserdata'}, + function(retval) { + if (!handleerror(retval)) { + var username, lastname, firstname; + username = $("username:first", retval).text(); + lastname = $("lastname:first", retval).text(); + firstname = $("firstname:first", retval).text(); + deleteuserdialog(userid, username, firstname, lastname); + } + }); +} + +function edituser(userid) { + $.get( + "users.php", + {uid : userid, + method : 'getuserdata'}, + function(retval) { + if (!handleerror(retval)) { + var username, lastname, firstname, groups; + username = $("username:first", retval).text(); + lastname = $("lastname:first", retval).text(); + firstname = $("firstname:first", retval).text(); + groups = $("groups:first", retval).text(); + displayusereditor('Nutzerdaten bearbeiten', userid, username, + firstname, lastname, groups); + } + }); +} + +function newuser() { + displayusereditor('Neuen Nutzer anlegen', null, '', '', '', ''); +} + +$(function() { + $("a.editlink").each(function(i) { + $(this).click(function() { edituser(this.id.substr(4)); }); + }); + $("a.newlink").each(function(i) { + $(this).click(function() { newuser(); }); + }); + $("a.deletelink").each(function(i) { + $(this).click(function() { deleteuser(this.id.substr(6)); }); + }); +}); diff --git a/admin/templates/directories.html b/admin/templates/directories.html new file mode 100644 index 0000000..84d6fff --- /dev/null +++ b/admin/templates/directories.html @@ -0,0 +1,39 @@ +{include file="header.html" title="Verzeichnisverwaltung"} + + +
+

WebDAV-Verwaltung

+

Verzeichnisverwaltung

+ + + + + +{foreach item=dir from=$directories} + + + + + + +{foreachelse} + +{/foreach} + + + + +
VerzeichnisGruppenenthaltene Dateien (Anzahl und Größe)
{$dir.name}{foreach item=group from=$dir.groups name=dirloop}{$group}{if !$smarty.foreach.dirloop.last}, {/if}{/foreach}{$dir.filecount}, {$dir.filesize}Gruppen{if $dir.maydelete}löschen{/if}
keine Verzeichnisse vorhanden
neu
+

Symbolerklärung

+
+
Gruppen
+
Die Gruppenberechtigungen dieses Verzeichnisses bearbeiten
+
löschen
+
Diese Verzeichnis löschen
+
neu
+
Neues Verzeichnis anlegen
+
+ +
+
+{include file="footer.html"} \ No newline at end of file diff --git a/admin/templates/error.xml b/admin/templates/error.xml new file mode 100644 index 0000000..b3d8ff3 --- /dev/null +++ b/admin/templates/error.xml @@ -0,0 +1 @@ +{$errormsg} \ No newline at end of file diff --git a/admin/templates/footer.html b/admin/templates/footer.html new file mode 100644 index 0000000..8634a86 --- /dev/null +++ b/admin/templates/footer.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/templates/header.html b/admin/templates/header.html new file mode 100644 index 0000000..49a28cd --- /dev/null +++ b/admin/templates/header.html @@ -0,0 +1,8 @@ + + +{$smarty.server.SERVER_NAME} - WebDAV Verwaltung{if $title} - {$title}{/if} + + + + \ No newline at end of file diff --git a/admin/templates/start.html b/admin/templates/start.html new file mode 100644 index 0000000..e7f6a41 --- /dev/null +++ b/admin/templates/start.html @@ -0,0 +1,10 @@ +{include file="header.html"} +

WebDAV-Verwaltung

+

Hallo {$firstname} {$lastname},
+willkommen zur WebDAV-Verwaltung für {$smarty.server.SERVER_NAME}. Ihnen +stehen folgende Möglichkeiten zur Verfügung.

+ +{include file="footer.html"} \ No newline at end of file diff --git a/admin/templates/users.html b/admin/templates/users.html new file mode 100644 index 0000000..2b14287 --- /dev/null +++ b/admin/templates/users.html @@ -0,0 +1,37 @@ +{include file="header.html" title="Nutzerverwaltung"} + + + +
+

WebDAV-Verwaltung

+

Nutzerverwaltung

+ + + + + +{foreach item=user from=$users} + + + + + +{/foreach} + + + + +
NutzernameName, Vorname
{$user.username}{$user.lastname}, {$user.firstname}bearbeiten{if !$user.loggedin}löschen{/if}
neu
+

Symbolerklärung

+
+
bearbeiten
+
Die Daten dieses Nutzers bearbeiten
+
löschen
+
Die Daten dieses Nutzers löschen
+
neu
+
Neuen Nutzer anlegen
+
+ +
+
+{include file="footer.html"} diff --git a/admin/users.php b/admin/users.php new file mode 100644 index 0000000..6342cee --- /dev/null +++ b/admin/users.php @@ -0,0 +1,317 @@ + + * @version $Id$ + * @license GPL + * @package WebDAVAdmin + * + * Copyright (c) 2007 Jan Dittberner + * + * This file is part of WebDAVAdmin. + * + * This program 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. + * + * This program 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +/** Include common code. */ +include_once('common.inc.php'); + +/** + * Gets XML encoded data for a user. + * + * @param int $uid user id + * @return XML string + */ +function getUserData($uid) { + if (!is_numeric($uid)) { + errorAsXml(sprintf(_("Invalid user id %s"), $uid)); + } + try { + $currentuser = $_SERVER['PHP_AUTH_USER']; + + $dbh = new PDO($GLOBALS['dsn'], $GLOBALS['dbuser'], $GLOBALS['dbpass']); + $sth = $dbh->prepare("SELECT groupname FROM dav_group, dav_password WHERE dav_group.username=dav_password.username AND dav_password.uid=:uid"); + if (!$sth->execute(array(':uid' => $uid))) { + statementErrorAsXml($sth); + } + $groups = array(); + while ($grouprow = $sth->fetch(PDO::FETCH_ASSOC)) { + array_push($groups, $grouprow['groupname']); + } + $sth = $dbh->prepare("SELECT username, firstname, lastname FROM dav_password WHERE uid=:uid"); + if (!$sth->execute(array(':uid' => $uid))) { + statementErrorAsXml($sth); + } + $row = $sth->fetch(PDO::FETCH_ASSOC); + $retval = sprintf('%d%s%s%s%s%d', + $uid, $row['username'], $row['firstname'], + $row['lastname'], implode(", ", $groups), + ($currentuser == $row['username']) ? 1 : 0); + $dbh = null; + } catch (PDOException $e) { + errorAsXml($e->getMessage()); + } + header("Content-Type: text/xml; charset=UTF-8"); + return $retval; +} + +/** + * Gets XML encoded data for a deleted user. + * + * @param int $uid user id + * @return XML string + */ +function getDeletedUserData($uid) { + if (!is_numeric($uid)) { + errorAsXml(sprintf(_("Invalid user id %s"), $uid)); + } + return sprintf('%d', $uid); +} + +/** + * Validates the given user data array for correctness. + * + * @param array &$userdata reference to an user data array + * @param boolean $forinsert if this is true the check skips field + * that will be created during insert + * @return an array with validation error messages or an empty array + */ +function validateUserData(&$userdata, $forinsert) { + $errormsgs = array(); + // normalize the user data array + foreach ($userdata as $key => $value) { + $userdata[$key] = trim($value); + } + if (!$forinsert) { + if (!is_numeric($userdata['uid'])) { + array_push($errormsgs, _("Uid must be numeric.")); + } + if (!empty($userdata['password']) && strlen($userdata['password']) < 8) { + array_push($errormsgs, _("Password must be at least 8 characters long.")); + } + } else { + if (!preg_match('/^[a-zA-Z0-9]{2,}$/', $userdata['username'])) { + array_push($errormsgs, _("Username must be at least 2 characters long and must contain letters and digits only.")); + } + if (empty($userdata['password']) || strlen($userdata['password']) < 8) { + array_push($errormsgs, _("Password must be at least 8 characters long.")); + } + } + if (empty($userdata['firstname'])) { + $userdata['firstname'] = null; + } + if (empty($userdata['lastname'])) { + $userdata['lastname'] = null; + } + if (!preg_match('/^([0-9a-zA-z]+[,\s]*)+$/', $userdata['groups'])) { + array_push($errormsgs, _('Groups must be a list of group names separated by commas. Group names must consist of letters and digits.')); + } + return $errormsgs; +} + +/** + * Updates the data of a user in the database. + * + * @param array &$userdata reference to a user data array + */ +function updateUser(&$userdata) { + $validation = validateUserData($userdata, false); + if (!empty($validation)) { + errorAsXml(implode("\n", $validation)); + } + try { + $dbh = new PDO($GLOBALS['dsn'], $GLOBALS['dbuser'], $GLOBALS['dbpass']); + $dbh->beginTransaction(); + if ($userdata['password']) { + $sth = $dbh->prepare("UPDATE dav_password SET firstname=:firstname, lastname=:lastname, password=md5(:password) WHERE uid=:uid"); + if (!$sth->execute(array(':firstname' => $userdata['firstname'], + ':lastname' => $userdata['lastname'], + ':password' => $userdata['password'], + ':uid' => $userdata['uid']))) { + $dbh->rollback(); + statementErrorAsXml($sth); + } + } else { + $sth = $dbh->prepare("UPDATE dav_password SET firstname=:firstname, lastname=:lastname WHERE uid=:uid"); + if (!$sth->execute(array(':firstname' => $userdata['firstname'], + ':lastname' => $userdata['lastname'], + ':uid' => $userdata['uid']))) { + $dbh->rollback(); + statementErrorAsXml($sth); + } + } + if ($userdata['groups']) { + $groups = array(); + $sth = $dbh->prepare("DELETE FROM dav_group WHERE username=:username"); + if (!$sth->execute(array(':username' => $userdata['username']))) { + $dbh->rollback(); + statementErrorAsXml($sth); + } + $sth = $dbh->prepare("INSERT INTO dav_group (username, groupname) VALUES (:username, :groupname)"); + foreach (explode(",", $userdata['groups']) as $group) { + $group = trim($group); + if (!in_array($group, $groups)) { + array_push($groups, $group); + if (!$sth->execute(array(':username' => $userdata['username'], + ':groupname' => $group))) { + $dbh->rollback(); + statementErrorAsXml($sth); + } + $sth->closeCursor(); + } + } + } + $dbh->commit(); + $dbh = null; + } catch (PDOException $e) { + errorAsXml($e->getMessage()); + } +} + +/** + * Inserts a new user and its group assignments into the database. + * + * @param array &$userdata reference to an user data array + */ +function insertUser(&$userdata) { + $validation = validateUserData($userdata, true); + if (!empty($validation)) { + errorAsXml(implode("\n", $validation)); + } + try { + $dbh = new PDO($GLOBALS['dsn'], $GLOBALS['dbuser'], $GLOBALS['dbpass']); + $dbh->beginTransaction(); + $sth = $dbh->prepare("INSERT INTO dav_password (username, firstname, lastname, password) VALUES (:username, :firstname, :lastname, md5(:password))"); + if (!$sth->execute(array(':username' => $userdata['username'], + ':firstname' => $userdata['firstname'], + ':lastname' => $userdata['lastname'], + ':password' => $userdata['password']))) { + $dbh->rollback(); + statementErrorAsXml($sth); + } + $uid = $dbh->lastInsertId('dav_password_uid_seq'); + $groups = array(); + $sth = $dbh->prepare("INSERT INTO dav_group (username, groupname) VALUES (:username, :groupname)"); + foreach (split(",", $userdata['groups']) as $group) { + $group = trim($group); + if (!in_array($group, $groups)) { + array_push($groups, $group); + if (!$sth->execute(array(':username' => $userdata['username'], + ':groupname' => $group))) { + $dbh->rollback(); + statementErrorAsXml($sth); + } + $sth->closeCursor(); + } + } + $dbh->commit(); + $dbh = null; + } catch (PDOException $e) { + errorAsXml($e->getMessage); + } + return $uid; +} + +/** + * Delete the user with the given user id and its group assignments + * from the database. + * + * @param int $uid user id + */ +function deleteUser($uid) { + if (!is_numeric($uid)) { + errorAsXml(sprintf(_("Invalid user id %s"), $uid)); + } + try { + $dbh = new PDO($GLOBALS['dsn'], $GLOBALS['dbuser'], $GLOBALS['dbpass']); + $dbh->beginTransaction(); + $query = $dbh->prepare("DELETE FROM dav_group WHERE username IN (SELECT username FROM dav_password WHERE uid=:uid)"); + if (!$query->execute(array(':uid' => $uid))) { + $dbh->rollback(); + statementErrorAsXml($query); + } + $query = $dbh->prepare("DELETE FROM dav_password WHERE uid=:uid"); + if (!$query->execute(array(':uid' => $uid))) { + $dbh->rollback(); + statementErrorAsXml($query); + } + $dbh->commit(); + $dbh = null; + } catch (PDOException $e) { + errorAsXml($e->getMessage()); + } +} + +if ($_GET) { + if ($_GET['method']) { + switch ($_GET['method']) { + case 'getuserdata': + if ($_GET['uid']) { + print getUserData($_GET['uid']); + } + break; + default: + errorAsXml(sprintf(_("Unexpected values %s!"), serialize($_GET))); + } + } else { + invalidCall(); + } +} elseif ($_POST) { + if ($_POST['method']) { + switch ($_POST['method']) { + case 'submituser': + if ($_POST['uid']) { + updateUser($_POST); + print getUserData($_POST['uid']); + } else { + print getUserData(insertUser($_POST)); + } + break; + case 'deleteuser': + if ($_POST['uid']) { + deleteUser($_POST['uid']); + print getDeletedUserData($_POST['uid']); + } else { + errorAsXml(sprintf(_("Unexpected values %s!"), serialize($_POST))); + } + break; + default: + errorAsXml(sprintf(_("Unexpected values %s!"), serialize($_POST))); + } + } else { + invalidCall(); + } +} else { + $currentuser = $_SERVER['PHP_AUTH_USER']; + + header("Content-Type: text/html; charset=UTF-8"); + try { + $dbh = new PDO($dsn, $dbuser, $dbpass); + $query = $dbh->prepare("SELECT uid, username, firstname, lastname FROM dav_password ORDER BY username"); + $query->execute(); + $rows = $query->fetchall(PDO::FETCH_ASSOC); + foreach ($rows as $key => $value) { + $value['loggedin'] = ($value['username'] == $currentuser); + $rows[$key] = $value; + } + $smarty->assign("users", $rows); + $smarty->display("users.html"); + } catch (PDOException $e) { + errorAsHtml($e->getMessage()); + } +} +?> \ No newline at end of file diff --git a/config/config.inc.php b/config/config.inc.php new file mode 100644 index 0000000..5f22cba --- /dev/null +++ b/config/config.inc.php @@ -0,0 +1,35 @@ + + * @version $Id$ + * @license GPL + * @package WebDAVAdmin + * + * Copyright (c) 2007 Jan Dittberner + * + * This file is part of WebDAVAdmin. + * + * This program 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. + * + * This program 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +/** Absolute path to DAV area root directory with a trailing slash. */ +define(DAV_ROOT, '@davrootdirectory@'); + +/** Include the database settings. */ +require_once('@path.to.dbsettings@/dbsettings.inc.php'); +?> \ No newline at end of file diff --git a/config/dbsettings.inc.php b/config/dbsettings.inc.php new file mode 100644 index 0000000..f47f67e --- /dev/null +++ b/config/dbsettings.inc.php @@ -0,0 +1,36 @@ + + * @version $Id$ + * @license GPL + * @package WebDAVAdmin + * + * Copyright (c) 2007 Jan Dittberner + * + * This file is part of WebDAV administration. + * + * This program 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. + * + * This program 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +/** Data source name. */ +$dsn = "pgsql:host=localhost port=5432 dbname=@dbname@"; +/** Database user. */ +$dbuser = "@dbuser@"; +/** Database password. */ +$dbpass = "@dbpass@"; +?> \ No newline at end of file diff --git a/setup/schema.sql b/setup/schema.sql new file mode 100644 index 0000000..ae45b04 --- /dev/null +++ b/setup/schema.sql @@ -0,0 +1,25 @@ +CREATE TABLE dav_password ( + uid SERIAL PRIMARY KEY, + username VARCHAR(16) NOT NULL UNIQUE, + password VARCHAR(34) NOT NULL, + firstname VARCHAR(64), + lastname VARCHAR(64) +); + +CREATE TABLE dav_group ( + gid SERIAL PRIMARY KEY, + username VARCHAR(16) NOT NULL REFERENCES dav_password(username), + groupname VARCHAR(32) NOT NULL, + UNIQUE(username, groupname) +); + +CREATE TABLE dav_log ( + logid SERIAL PRIMARY KEY, + username VARCHAR(16), + reqdate VARCHAR(20), + uri TEXT, + ipaddr VARCHAR(16) +); + +INSERT INTO dav_password (username, password) VALUES ('admin', md5('secret')); +INSERT INTO dav_group (username, groupname) VALUES ('admin', 'davadmin'); diff --git a/setup/webdavadmin.vhost b/setup/webdavadmin.vhost new file mode 100644 index 0000000..7ea1134 --- /dev/null +++ b/setup/webdavadmin.vhost @@ -0,0 +1,52 @@ + + ServerAdmin webmaster@davhost.yourdomain.net + ServerName davhost.yourdomain.net + + DavLockDb /var/run/apache2/davlock/davhost.yourdomain.net + DocumentRoot /home/www/usr29/html + + php_admin_value allow_call_time_pass_reference 1 + + Dav on + AllowOverride AuthConfig Indexes + Order Allow,Deny + allow from all + + AuthType Basic + AuthName "WebDAV on davhost" + AuthBasicAuthoritative Off + AuthUserFile /etc/apache2/auth/davhost.yourdomain.net + + Auth_PG_host localhost + Auth_PG_port 5432 + Auth_PG_user @dbuser@ + Auth_PG_pwd @dbpassword@ + Auth_PG_database @dbname@ + + Auth_PG_pwd_table dav_password + Auth_PG_uid_field username + Auth_PG_pwd_field password + + Auth_PG_grp_table dav_group + Auth_PG_grp_user_field username + Auth_PG_grp_group_field groupname + Auth_PG_hash_type MD5 + + #Auth_PG_log_table dav_log + #Auth_PG_log_uname_field username + #Auth_PG_log_date_field reqdate + #Auth_PG_log_uri_field uri + #Auth_PG_log_addrs_field ipaddr + Auth_PG_authoritative on + + require group davroot + + + ErrorLog /var/log/apache2/davhost.yourdomain.net_error.log + + # Possible values include: debug, info, notice, warn, error, crit, + # alert, emerg. + LogLevel warn + + CustomLog /var/log/apache2/davhost.yourdomain.net_access.log combined +