diff --git a/phpgwapi/inc/class.egw_vfs.inc.php b/phpgwapi/inc/class.egw_vfs.inc.php
index fe1d8992d0..2fe6733ca2 100644
--- a/phpgwapi/inc/class.egw_vfs.inc.php
+++ b/phpgwapi/inc/class.egw_vfs.inc.php
@@ -72,6 +72,10 @@ class egw_vfs extends vfs_stream_wrapper
* Excecutable bit, here only use to check if user is allowed to search dirs
*/
const EXECUTABLE = 1;
+ /**
+ * Name of the lock table
+ */
+ const LOCK_TABLE = 'egw_locks';
/**
* Current user has root rights, no access checks performed!
*
@@ -96,6 +100,12 @@ class egw_vfs extends vfs_stream_wrapper
* @var int
*/
static $find_total;
+ /**
+ * Reference to the global db object
+ *
+ * @var egw_db
+ */
+ static $db;
/**
* fopen working on just the eGW VFS
@@ -947,6 +957,158 @@ class egw_vfs extends vfs_stream_wrapper
return '/filemanager/webdav.php'.$path;
}
+ /**
+ * We cache locks within a request, as HTTP_WebDAV_Server generates so many, that it can be a bottleneck
+ *
+ * @var array
+ */
+ static protected $lock_cache;
+
+ /**
+ * lock a ressource/path
+ *
+ * @param string $path path or url
+ * @param string $token
+ * @param int &$timeout
+ * @param string &$owner
+ * @param string &$scope
+ * @param string &$type
+ * @param boolean $update=false
+ * @param boolean $check_writable=true should we check if the ressource is writable, before granting locks, default yes
+ * @return boolean true on success
+ */
+ function lock($path,$token,&$timeout,&$owner,&$scope,&$type,$update=false,$check_writable=true)
+ {
+ // we require write rights to lock/unlock a resource
+ if (!$path || $update && !$token || $check_writable && !egw_vfs::is_writable($path))
+ {
+ return false;
+ }
+ // remove the lock info evtl. set in the cache
+ unset(self::$lock_cache[$path]);
+
+ if ($update) // Lock Update
+ {
+ if (($ret = (boolean)($row = self::$db->select(self::LOCK_TABLE,array('lock_owner','lock_exclusive','lock_write'),array(
+ 'lock_path' => $path,
+ 'lock_token' => $token,
+ )))))
+ {
+ $owner = $row['lock_owner'];
+ $scope = egw_db::from_bool($row['lock_exclusive']) ? 'exclusive' : 'shared';
+ $type = egw_db::from_bool($row['lock_write']) ? 'write' : 'read';
+
+ self::$db->update(self::LOCK_TABLE,array(
+ 'lock_expires' => $timeout,
+ 'lock_modified' => time(),
+ ),array(
+ 'path' => $path,
+ 'token' => $token,
+ ),__LINE__,__FILE__);
+ }
+ }
+ // HTTP_WebDAV_Server does this check before calling LOCK, but we want to be complete and usable outside WebDAV
+ elseif(($lock = self::checkLock($path)) && ($lock['scope'] == 'exclusive' || $scope == 'exclusive'))
+ {
+ $ret = false; // there's alread a lock
+ }
+ else
+ {
+ // HTTP_WebDAV_Server sets owner and token, but we want to be complete and usable outside WebDAV
+ if (!$owner || $owner == 'unknown')
+ {
+ $owner = 'mailto:'.$GLOBALS['egw_info']['user']['account_email'];
+ }
+ if (!$token)
+ {
+ $token = HTTP_WebDAV_Server::_new_locktoken();
+ }
+ try {
+ self::$db->insert(self::LOCK_TABLE,array(
+ 'lock_token' => $token,
+ 'lock_path' => $path,
+ 'lock_created' => time(),
+ 'lock_modified' => time(),
+ 'lock_owner' => $owner,
+ 'lock_expires' => $timeout,
+ 'lock_exclusive' => $scope == 'exclusive',
+ 'lock_write' => $type == 'write',
+ ),false,__LINE__,__FILE__);
+ $ret = true;
+ }
+ catch(egw_exception_db $e) {
+ $ret = false; // there's already a lock
+ }
+ }
+ error_log(__METHOD__."($path,$token,$timeout,$owner,$scope,$type,$update,$check_writable) returns ".($ret ? 'true' : 'false'));
+ return $ret;
+ }
+
+ /**
+ * unlock a ressource/path
+ *
+ * @param string $path path to unlock
+ * @param string $token locktoken
+ * @param boolean $check_writable=true should we check if the ressource is writable, before granting locks, default yes
+ * @return boolean true on success
+ */
+ function unlock($path,$token,$check_writable=true)
+ {
+ // we require write rights to lock/unlock a resource
+ if ($check_writable && !egw_vfs::is_writable($path))
+ {
+ return false;
+ }
+ if (($ret = self::$db->delete(self::LOCK_TABLE,array(
+ 'lock_path' => $path,
+ 'lock_token' => $token,
+ ),__LINE__,__FILE__) && self::$db->affected_rows()))
+ {
+ // remove the lock from the cache too
+ unset(self::$lock_cache[$path]);
+ }
+ error_log(__METHOD__."($path,$token,$check_writable) returns ".($ret ? 'true' : 'false'));
+ return $ret;
+ }
+
+ /**
+ * checkLock() helper
+ *
+ * @param string resource path to check for locks
+ * @return array|boolean false if there's no lock, else array with lock info
+ */
+ function checkLock($path)
+ {
+ if (isset(self::$lock_cache[$path]))
+ {
+ error_log(__METHOD__."($path) returns from CACHE ".str_replace(array("\n",' '),'',print_r(self::$lock_cache[$path],true)));
+ return self::$lock_cache[$path];
+ }
+ $where = 'lock_path='.self::$db->quote($path);
+ // ToDo: additional check parent dirs for locks and children of the requested directory
+ //$where .= ' OR '.self::$db->quote($path).' LIKE '.self::$db->concat('lock_path',"'%'").' OR lock_path LIKE '.self::$db->quote($path.'%');
+ // ToDo: shared locks can return multiple rows
+ if (($result = self::$db->select(self::LOCK_TABLE,'*',$where,__LINE__,__FILE__)->fetch()))
+ {
+ $result = egw_db::strip_array_keys($result,'lock_');
+ $result['type'] = egw_db::from_bool($result['write']) ? 'write' : 'read';
+ $result['scope'] = egw_db::from_bool($result['exclusive']) ? 'exclusive' : 'shared';
+ $result['depth'] = egw_db::from_bool($result['recursive']) ? 'infinite' : 0;
+ }
+ if ($result && $result['expires'] < time()) // lock is expired --> remove it
+ {
+ self::$db->delete(self::LOCK_TABLE,array(
+ 'lock_path' => $result['path'],
+ 'lock_token' => $result['token'],
+ ),__LINE__,__FILE__);
+
+ error_log(__METHOD__."($path) lock is expired at ".date('Y-m-d H:i:s',$result['expires'])." --> removed");
+ $result = false;
+ }
+ error_log(__METHOD__."($path) returns ".($result?str_replace(array("\n",' '),'',print_r($result,true)):'false'));
+ return self::$lock_cache[$path] = $result;
+ }
+
/**
* Initialise our static vars
*/
@@ -954,6 +1116,8 @@ class egw_vfs extends vfs_stream_wrapper
{
self::$user = (int) $GLOBALS['egw_info']['user']['account_id'];
self::$is_admin = isset($GLOBALS['egw_info']['user']['apps']['admin']);
+ self::$db = isset($GLOBALS['egw_setup']->db) ? $GLOBALS['egw_setup']->db : $GLOBALS['egw']->db;
+ self::$lock_cache = array();
}
}
diff --git a/phpgwapi/inc/class.vfs_webdav_server.inc.php b/phpgwapi/inc/class.vfs_webdav_server.inc.php
index bcea0fa34d..86e5b302ea 100644
--- a/phpgwapi/inc/class.vfs_webdav_server.inc.php
+++ b/phpgwapi/inc/class.vfs_webdav_server.inc.php
@@ -60,10 +60,10 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
{
foreach (apache_request_headers() as $key => $value)
{
- if (stristr($key, "litmus"))
+ if (stristr($key, 'litmus'))
{
error_log("Litmus test $value");
- header("X-Litmus-reply: ".$value);
+ header('X-Litmus-reply: '.$value);
}
}
}
@@ -71,114 +71,110 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
HTTP_WebDAV_Server::ServeRequest();
}
- /**
- * DELETE method handler
- *
- * @param array general parameter passing array
- * @return bool true on success
- */
- function DELETE($options)
- {
- $path = $this->base . "/" .$options["path"];
+ /**
+ * DELETE method handler
+ *
+ * @param array general parameter passing array
+ * @return bool true on success
+ */
+ function DELETE($options)
+ {
+ $path = $this->base . $options['path'];
- if (!file_exists($path))
- {
- return "404 Not found";
- }
+ if (!file_exists($path))
+ {
+ return '404 Not found';
+ }
- if (is_dir($path))
- {
- /*$query = "DELETE FROM {$this->db_prefix}properties
- WHERE path LIKE '".$this->_slashify($options["path"])."%'";
- mysql_query($query); */
- // recursive delete the directory
- if ($dir = egw_vfs::dir_opendir($options["path"]))
- {
- while(($file = readdir($dir)))
- {
- if ($file == '.' || $file == '..') continue;
+ if (is_dir($path))
+ {
- if (is_dir($path.'/'.$file))
- {
- // recursivly call ourself with the dir
- $opts = $options;
- $opts['path'] .= '/'.$file;
- $this->DELETE($opts);
- }
- else
- {
- unlink($path.'/'.$file);
- }
- }
- closedir($dir);
- }
- }
- else
- {
- unlink($path);
- }
- /*$query = "DELETE FROM {$this->db_prefix}properties
- WHERE path = '$options[path]'";
- mysql_query($query);*/
+ /*$query = "DELETE FROM {$this->db_prefix}properties
+ WHERE path LIKE '".$this->_slashify($options["path"])."%'";
+ mysql_query($query); */
- return "204 No Content";
- }
+ // recursive delete the directory
+ egw_vfs::remove($options['path']);
+ }
+ else
+ {
+ unlink($path);
+ }
+ /*$query = "DELETE FROM {$this->db_prefix}properties
+ WHERE path = '$options[path]'";
+ mysql_query($query);*/
- /**
- * Get properties for a single file/resource
- *
- * @param string resource path
- * @return array resource properties
- */
- function fileinfo($path)
- {
- error_log(__METHOD__."($path)");
- // map URI path to filesystem path
- $fspath = $this->base . $path;
+ return '204 No Content';
+ }
- // create result array
- $info = array();
- // TODO remove slash append code when base clase is able to do it itself
- $info["path"] = is_dir($fspath) ? $this->_slashify($path) : $path;
- $info["props"] = array();
+ /**
+ * Get properties for a single file/resource
+ *
+ * @param string resource path
+ * @return array resource properties
+ */
+ function fileinfo($path)
+ {
+ //error_log(__METHOD__."($path)");
+ // map URI path to filesystem path
+ $fspath = $this->base . $path;
- // no special beautified displayname here ...
- $info["props"][] = $this->mkprop("displayname", strtoupper($path));
+ // create result array
+ $info = array();
+ // TODO remove slash append code when base class is able to do it itself
+ $info['path'] = is_dir($fspath) ? $this->_slashify($path) : $path;
+ $info['props'] = array();
- // creation and modification time
- $info["props"][] = $this->mkprop("creationdate", filectime($fspath));
- $info["props"][] = $this->mkprop("getlastmodified", filemtime($fspath));
+ // no special beautified displayname here ...
+ $info['props'][] = HTTP_WebDAV_Server::mkprop ('displayname', strtoupper($path));
- // type and size (caller already made sure that path exists)
- if (is_dir($fspath)) {
- // directory (WebDAV collection)
- $info["props"][] = $this->mkprop("resourcetype", "collection");
- $info["props"][] = $this->mkprop("getcontenttype", "httpd/unix-directory");
- } else {
- // plain file (WebDAV resource)
- $info["props"][] = $this->mkprop("resourcetype", "");
- if (egw_vfs::is_readable($path)) {
- $info["props"][] = $this->mkprop("getcontenttype", egw_vfs::mime_content_type($path));
- } else {
+ // creation and modification time
+ $info['props'][] = HTTP_WebDAV_Server::mkprop ('creationdate', filectime($fspath));
+ $info['props'][] = HTTP_WebDAV_Server::mkprop ('getlastmodified', filemtime($fspath));
+
+ // type and size (caller already made sure that path exists)
+ if (is_dir($fspath)) {
+ // directory (WebDAV collection)
+ $info['props'][] = HTTP_WebDAV_Server::mkprop ('resourcetype', 'collection');
+ $info['props'][] = HTTP_WebDAV_Server::mkprop ('getcontenttype', 'httpd/unix-directory');
+ } else {
+ // plain file (WebDAV resource)
+ $info['props'][] = HTTP_WebDAV_Server::mkprop ('resourcetype', '');
+ if (egw_vfs::is_readable($path)) {
+ $info['props'][] = HTTP_WebDAV_Server::mkprop ('getcontenttype', egw_vfs::mime_content_type($path));
+ } else {
error_log(__METHOD__."($path) $fspath is not readable!");
- $info["props"][] = $this->mkprop("getcontenttype", "application/x-non-readable");
- }
- $info["props"][] = $this->mkprop("getcontentlength", filesize($fspath));
- }
+ $info['props'][] = HTTP_WebDAV_Server::mkprop ('getcontenttype', 'application/x-non-readable');
+ }
+ $info['props'][] = HTTP_WebDAV_Server::mkprop ('getcontentlength', filesize($fspath));
+ }
+ // supportedlock property
+ $info['props'][] = HTTP_WebDAV_Server::mkprop('supportedlock','
+
+
+
+
+
+
+
+ ');
+
+ // ToDo: etag from inode and modification time
+
/*
- // get additional properties from database
- $query = "SELECT ns, name, value
- FROM {$this->db_prefix}properties
- WHERE path = '$path'";
- $res = mysql_query($query);
- while ($row = mysql_fetch_assoc($res)) {
- $info["props"][] = $this->mkprop($row["ns"], $row["name"], $row["value"]);
- }
- mysql_free_result($res);
+ // get additional properties from database
+ $query = "SELECT ns, name, value
+ FROM {$this->db_prefix}properties
+ WHERE path = '$path'";
+ $res = mysql_query($query);
+ while ($row = mysql_fetch_assoc($res)) {
+ $info["props"][] = HTTP_WebDAV_Server::mkprop ($row["ns"], $row["name"], $row["value"]);
+ }
+ mysql_free_result($res);
*/
//error_log(__METHOD__."($path) info=".print_r($info,true));
- return $info;
- }
+ return $info;
+ }
/**
* Used eg. by get
@@ -207,7 +203,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
{
$path = $GLOBALS['egw']->translation->convert($options['path'],'utf-8');
- foreach ($options["props"] as $key => $prop) {
+ foreach ($options['props'] as $key => $prop) {
$attributes = array();
switch($prop['ns'])
{
@@ -235,12 +231,12 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
break;
// not sure why, the filesystem example of the WebDAV class does it ...
default:
- $options["props"][$key]['status'] = "403 Forbidden";
+ $options['props'][$key]['status'] = '403 Forbidden';
break;
}
break;
}
- if ($this->debug) $props[] = '('.$prop["ns"].')'.$prop['name'].'='.$prop['val'];
+ if ($this->debug) $props[] = '('.$prop['ns'].')'.$prop['name'].'='.$prop['val'];
}
if ($this->debug)
{
@@ -248,137 +244,55 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
if ($attributes) error_log(__METHOD__.": path=$options[path], set attributes=".str_replace("\n",' ',print_r($attributes,true)));
}
- return ""; // this is as the filesystem example handler does it, no true or false ...
+ return ''; // this is as the filesystem example handler does it, no true or false ...
}
- /**
- * LOCK method handler
- *
- * @param array general parameter passing array
- * @return bool true on success
- */
- function LOCK(&$options)
- {
- // behaving like LOCK is not implemented
- return "412 Precondition failed";
-/*
- // get absolute fs path to requested resource
- $fspath = $this->base . $options["path"];
-
+ /**
+ * LOCK method handler
+ *
+ * @param array general parameter passing array
+ * @return bool true on success
+ */
+ function LOCK(&$options)
+ {
+ error_log(__METHOD__.'('.str_replace(array("\n",' '),'',print_r($options,true)).')');
// TODO recursive locks on directories not supported yet
- if (is_dir($fspath) && !empty($options["depth"])) {
- return "409 Conflict";
+ if (is_dir($this->base . $options['path']) && !empty($options['depth']))
+ {
+ return '409 Conflict';
}
+ $options['timeout'] = time()+300; // 5min. hardcoded
- $options["timeout"] = time()+300; // 5min. hardcoded
+ // dont know why, but HTTP_WebDAV_Server passes the owner in D:href tags, which get's passed unchanged to checkLock/PROPFIND
+ // that's wrong according to the standard and cadaver does not show it on discover --> strip_tags removes eventual tags
+ if (($ret = egw_vfs::lock($options['path'],$options['locktoken'],$options['timeout'],strip_tags($options['owner']),
+ $options['scope'],$options['type'],isset($options['update']))) && !isset($options['update']))
+ {
+ return $ret ? '200 OK' : '409 Conflict';
+ }
+ return $ret;
+ }
- if (isset($options["update"])) { // Lock Update
- $where = "WHERE path = '$options[path]' AND token = '$options[update]'";
+ /**
+ * UNLOCK method handler
+ *
+ * @param array general parameter passing array
+ * @return bool true on success
+ */
+ function UNLOCK(&$options)
+ {
+ error_log(__METHOD__.'('.str_replace(array("\n",' '),'',print_r($options,true)).')');
+ return egw_vfs::unlock($options['path'],$options['token']) ? '204 No Content' : '409 Conflict';
+ }
- $query = "SELECT owner, exclusivelock FROM {$this->db_prefix}locks $where";
- $res = mysql_query($query);
- $row = mysql_fetch_assoc($res);
- mysql_free_result($res);
-
- if (is_array($row)) {
- $query = "UPDATE {$this->db_prefix}locks
- SET expires = '$options[timeout]'
- , modified = ".time()."
- $where";
- mysql_query($query);
-
- $options['owner'] = $row['owner'];
- $options['scope'] = $row["exclusivelock"] ? "exclusive" : "shared";
- $options['type'] = $row["exclusivelock"] ? "write" : "read";
-
- return true;
- } else {
- return false;
- }
- }
-
- $query = "INSERT INTO {$this->db_prefix}locks
- SET token = '$options[locktoken]'
- , path = '$options[path]'
- , created = ".time()."
- , modified = ".time()."
- , owner = '$options[owner]'
- , expires = '$options[timeout]'
- , exclusivelock = " .($options['scope'] === "exclusive" ? "1" : "0")
- ;
- mysql_query($query);
-
- return mysql_affected_rows() ? "200 OK" : "409 Conflict";*/
- }
-
- /**
- * UNLOCK method handler
- *
- * @param array general parameter passing array
- * @return bool true on success
- */
- function UNLOCK(&$options)
- {
- // behaving like LOCK is not implemented
- return "405 Method not allowed";
-/*
- $query = "DELETE FROM {$this->db_prefix}locks
- WHERE path = '$options[path]'
- AND token = '$options[token]'";
- mysql_query($query);
-
- return mysql_affected_rows() ? "204 No Content" : "409 Conflict";*/
- }
-
- /**
- * checkLock() helper
- *
- * @param string resource path to check for locks
- * @return bool true on success
- */
- function checkLock($path)
- {
- // behave like checkLock is not implemented
- return false;
-/*
- $result = false;
-
- $query = "SELECT owner, token, created, modified, expires, exclusivelock
- FROM {$this->db_prefix}locks
- WHERE path = '$path'
- ";
- $res = mysql_query($query);
-
- if ($res) {
- $row = mysql_fetch_array($res);
- mysql_free_result($res);
-
- if ($row) {
- $result = array( "type" => "write",
- "scope" => $row["exclusivelock"] ? "exclusive" : "shared",
- "depth" => 0,
- "owner" => $row['owner'],
- "token" => $row['token'],
- "created" => $row['created'],
- "modified" => $row['modified'],
- "expires" => $row['expires']
- );
- }
- }
-
- return $result;*/
- }
-
- /**
- * Remove not (yet) implemented LOCK methods, so we can use the mostly unchanged HTTP_WebDAV_Server_Filesystem class
- *
- * @return array
- */
- function _allow()
- {
- $allow = parent::_allow();
- unset($allow['LOCK']);
- unset($allow['UNLOCK']);
- return $allow;
- }
+ /**
+ * checkLock() helper
+ *
+ * @param string resource path to check for locks
+ * @return bool true on success
+ */
+ function checkLock($path)
+ {
+ return egw_vfs::checkLock($path);
+ }
}
\ No newline at end of file
diff --git a/phpgwapi/setup/setup.inc.php b/phpgwapi/setup/setup.inc.php
index b843527e26..ac6fbf8682 100755
--- a/phpgwapi/setup/setup.inc.php
+++ b/phpgwapi/setup/setup.inc.php
@@ -12,7 +12,7 @@
/* Basic information about this app */
$setup_info['phpgwapi']['name'] = 'phpgwapi';
$setup_info['phpgwapi']['title'] = 'eGroupWare API';
-$setup_info['phpgwapi']['version'] = '1.5.009';
+$setup_info['phpgwapi']['version'] = '1.5.010';
$setup_info['phpgwapi']['versions']['current_header'] = '1.28';
$setup_info['phpgwapi']['enable'] = 3;
$setup_info['phpgwapi']['app_order'] = 1;
@@ -47,6 +47,7 @@ $setup_info['phpgwapi']['tables'][] = 'egw_sqlfs';
$setup_info['phpgwapi']['tables'][] = 'egw_index_keywords';
$setup_info['phpgwapi']['tables'][] = 'egw_index';
$setup_info['phpgwapi']['tables'][] = 'egw_cat2entry';
+$setup_info['phpgwapi']['tables'][] = 'egw_locks';
// hooks used by vfs_home_hooks to manage user- and group-directories for the new stream based VFS
$setup_info['phpgwapi']['hooks']['addaccount'] = 'phpgwapi.vfs_home_hooks.addAccount';
@@ -65,3 +66,5 @@ $setup_info['notifywindow']['app_order'] = 1;
$setup_info['notifywindow']['tables'] = '';
$setup_info['notifywindow']['hooks'][] = 'home';
+
+
diff --git a/phpgwapi/setup/tables_current.inc.php b/phpgwapi/setup/tables_current.inc.php
index 76f20c8326..4e76da6d5c 100644
--- a/phpgwapi/setup/tables_current.inc.php
+++ b/phpgwapi/setup/tables_current.inc.php
@@ -486,5 +486,22 @@ $phpgw_baseline = array(
'fk' => array(),
'ix' => array('cat_id'),
'uc' => array()
+ ),
+ 'egw_locks' => array(
+ 'fd' => array(
+ 'lock_token' => array('type' => 'varchar','precision' => '255','nullable' => False),
+ 'lock_path' => array('type' => 'varchar','precision' => '255','nullable' => False),
+ 'lock_expires' => array('type' => 'int','precision' => '8','nullable' => False),
+ 'lock_owner' => array('type' => 'varchar','precision' => '255'),
+ 'lock_recursive' => array('type' => 'bool','nullable' => False,'default' => '0'),
+ 'lock_write' => array('type' => 'bool','nullable' => False,'default' => '0'),
+ 'lock_exclusive' => array('type' => 'bool','nullable' => False,'default' => '0'),
+ 'lock_created' => array('type' => 'int','precision' => '8','default' => '0'),
+ 'lock_modified' => array('type' => 'int','precision' => '8','default' => '0')
+ ),
+ 'pk' => array('lock_token'),
+ 'fk' => array(),
+ 'ix' => array('lock_path','lock_expires'),
+ 'uc' => array()
)
);
diff --git a/phpgwapi/setup/tables_update.inc.php b/phpgwapi/setup/tables_update.inc.php
index 03508156fc..2c7c9994d4 100644
--- a/phpgwapi/setup/tables_update.inc.php
+++ b/phpgwapi/setup/tables_update.inc.php
@@ -389,3 +389,27 @@ function phpgwapi_upgrade1_5_008()
return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '1.5.009';
}
+
+$test[] = '1.5.009';
+function phpgwapi_upgrade1_5_009()
+{
+ $GLOBALS['egw_setup']->oProc->CreateTable('egw_locks',array(
+ 'fd' => array(
+ 'lock_token' => array('type' => 'varchar','precision' => '255','nullable' => False),
+ 'lock_path' => array('type' => 'varchar','precision' => '255','nullable' => False),
+ 'lock_expires' => array('type' => 'int','precision' => '8','nullable' => False),
+ 'lock_owner' => array('type' => 'varchar','precision' => '255'),
+ 'lock_recursive' => array('type' => 'bool','nullable' => False,'default' => '0'),
+ 'lock_write' => array('type' => 'bool','nullable' => False,'default' => '0'),
+ 'lock_exclusive' => array('type' => 'bool','nullable' => False,'default' => '0'),
+ 'lock_created' => array('type' => 'int','precision' => '8','default' => '0'),
+ 'lock_modified' => array('type' => 'int','precision' => '8','default' => '0')
+ ),
+ 'pk' => array('lock_token'),
+ 'fk' => array(),
+ 'ix' => array('lock_path','lock_expires'),
+ 'uc' => array()
+ ));
+
+ return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '1.5.010';
+}