* Filemanager/Admin: ability to check virtual filesystem (Admin >> Check virtual filesystem) and some code to prefent double creation of directories

This commit is contained in:
Ralf Becker 2012-02-27 13:24:01 +00:00
commit 8848953385
6 changed files with 564 additions and 87 deletions

View File

@ -0,0 +1,172 @@
<?php
/**
* EGroupware Admin: Hooks
*
* @link http://www.egroupware.org
* @author Stefan Becker <StefanBecker-AT-outdoor-training.de>
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @package admin
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
/**
* Static hooks for admin application
*/
class admin_prefs_sidebox_hooks
{
/**
* Functions callable via menuaction
*
* @var unknown_type
*/
var $public_functions = array(
'register_all_hooks' => True,
'fsck' => true,
);
/**
* hooks to build projectmanager's sidebox-menu plus the admin and preferences sections
*
* @param string/array $args hook args
*/
static function all_hooks($args)
{
if (!isset($_GET['menuaction']) && substr($_SERVER['PHP_SELF'],-16) == '/admin/index.php')
{
admin_statistics::check();
}
$appname = 'admin';
$location = is_array($args) ? $args['location'] : $args;
if ($GLOBALS['egw_info']['user']['apps']['admin'] && $location != 'admins')
{
if (! $GLOBALS['egw']->acl->check('site_config_access',1,'admin'))
{
$file['Site Configuration'] = egw::link('/index.php','menuaction=admin.uiconfig.index&appname=admin');
}
/* disabled it, til it does something useful
if (! $GLOBALS['egw']->acl->check('peer_server_access',1,'admin'))
{
$file['Peer Servers'] = egw::link('/index.php','menuaction=admin.uiserver.list_servers');
}
*/
if (! $GLOBALS['egw']->acl->check('account_access',1,'admin'))
{
$file['User Accounts'] = egw::link('/index.php','menuaction=admin.uiaccounts.list_users');
}
if (! $GLOBALS['egw']->acl->check('group_access',1,'admin'))
{
$file['User Groups'] = egw::link('/index.php','menuaction=admin.uiaccounts.list_groups');
}
if (! $GLOBALS['egw']->acl->check('applications_access',1,'admin'))
{
$file['Applications'] = egw::link('/index.php','menuaction=admin.admin_applications.index');
}
if (! $GLOBALS['egw']->acl->check('global_categories_access',1,'admin'))
{
$file['Global Categories'] = egw::link('/index.php','menuaction=admin.admin_categories.index&appname=phpgw');
}
if (!$GLOBALS['egw']->acl->check('mainscreen_message_access',1,'admin') || !$GLOBALS['egw']->acl->check('mainscreen_message_access',2,'admin'))
{
$file['Change Main Screen Message'] = egw::link('/index.php','menuaction=admin.uimainscreen.index');
}
if (! $GLOBALS['egw']->acl->check('current_sessions_access',1,'admin'))
{
$file['View Sessions'] = egw::link('/index.php','menuaction=admin.uicurrentsessions.list_sessions');
}
if (! $GLOBALS['egw']->acl->check('access_log_access',1,'admin'))
{
$file['View Access Log'] = egw::link('/index.php','menuaction=admin.admin_accesslog.index');
}
if (! $GLOBALS['egw']->acl->check('error_log_access',1,'admin'))
{
$file['View Error Log'] = egw::link('/index.php','menuaction=admin.uilog.list_log');
}
if (! $GLOBALS['egw']->acl->check('applications_access',16,'admin'))
{
$file['Find and Register all Application Hooks'] = egw::link('/index.php','menuaction=admin.admin_prefs_sidebox_hooks.register_all_hooks');
}
//if (! $GLOBALS['egw']->acl->check('applications_access',16,'admin'))
{
$file['Check virtual filesystem'] = egw::link('/index.php','menuaction=admin.admin_prefs_sidebox_hooks.fsck');
}
if (! $GLOBALS['egw']->acl->check('asyncservice_access',1,'admin'))
{
$file['Asynchronous timed services'] = egw::link('/index.php','menuaction=admin.uiasyncservice.index');
}
if (! $GLOBALS['egw']->acl->check('db_backup_access',1,'admin'))
{
$file['DB backup and restore'] = egw::link('/index.php','menuaction=admin.admin_db_backup.index');
}
if (! $GLOBALS['egw']->acl->check('info_access',1,'admin'))
{
$file['phpInfo'] = "javascript:openwindow('" . egw::link('/admin/phpinfo.php') . "')"; //egw::link('/admin/phpinfo.php');
}
$file['Admin queue and history'] = egw::link('/index.php','menuaction=admin.admin_cmds.index');
$file['Remote administration instances'] = egw::link('/index.php','menuaction=admin.admin_cmds.remotes');
$file['Submit statistic information'] = egw::link('/index.php','menuaction=admin.admin_statistics.submit');
if ($location == 'admin')
{
display_section($appname,$file);
}
else
{
display_sidebox($appname,lang('Admin'),$file);
}
}
}
/**
* Register all hooks
*/
function register_all_hooks()
{
if ($GLOBALS['egw']->acl->check('applications_access',16,'admin'))
{
$GLOBALS['egw']->redirect_link('/index.php');
}
$GLOBALS['egw']->hooks->register_all_hooks();
if (method_exists($GLOBALS['egw'],'invalidate_session_cache')) // egw object in setup is limited
{
$GLOBALS['egw']->invalidate_session_cache(); // in case with cache the egw_info array in the session
}
$GLOBALS['egw']->redirect_link('/admin/index.php');
}
/**
* Run fsck on sqlfs
*/
function fsck()
{
$check_only = !isset($_POST['fix']);
if (!($msgs = sqlfs_utils::fsck($check_only)))
{
$msgs = lang('Filesystem check reported no problems.');
}
$content = '<p>'.implode("</p>\n<p>", (array)$msgs)."</p>\n";
$content .= html::form('<p>'.($check_only&&is_array($msgs)?html::submit_button('fix', lang('Fix reported problems')):'').
html::submit_button('cancel', lang('Cancel'), "window.location.href='".egw::link('/admin/index.php')."'; return false;").'</p>',
'',egw::link('/index.php',array('menuaction'=>'admin.admin_prefs_sidebox_hooks.fsck')));
$GLOBALS['egw']->framework->render($content, lang('Admin').' - '.lang('Check virtual filesystem'), true);
}
}

View File

@ -111,6 +111,8 @@ change password for %1 admin de Ändern des Passworts für %1
check acl for entries of not (longer) existing accounts admin de Prüfe ACL Einträge auf Bezüge zu nicht (mehr) existierenden Benutzerkonten check acl for entries of not (longer) existing accounts admin de Prüfe ACL Einträge auf Bezüge zu nicht (mehr) existierenden Benutzerkonten
check ip address of all sessions admin de IP-Adresse für alle Sessions überprüfen check ip address of all sessions admin de IP-Adresse für alle Sessions überprüfen
check items to <b>%1</b> to %2 for %3 admin de Durch Abhaken %3 in %2 <b>%1</b> check items to <b>%1</b> to %2 for %3 admin de Durch Abhaken %3 in %2 <b>%1</b>
check virtual filesystem admin de Virtuelles Dateisystem überprüfen
children admin de Kinder
click to select a color admin de Anclicken um eine Farbe auszuwählen click to select a color admin de Anclicken um eine Farbe auszuwählen
color admin de Farbe color admin de Farbe
command scheduled to run at %1 admin de Ausführung des Befehls eingeplant am/um %1 command scheduled to run at %1 admin de Ausführung des Befehls eingeplant am/um %1
@ -251,6 +253,7 @@ false admin de Falsch
field '%1' already exists !!! admin de Feld '%1' existiert bereits !!! field '%1' already exists !!! admin de Feld '%1' existiert bereits !!!
file space admin de Speicherplatz file space admin de Speicherplatz
file space must be an integer admin de Speicherplatz muss eine Zahl sein file space must be an integer admin de Speicherplatz muss eine Zahl sein
filesystem check reported no problems. admin de Überprüfung des Dateisystem ergab keine Probleme.
find and register all application hooks admin de Suchen und registrieren der "Hooks" aller Anwendungen find and register all application hooks admin de Suchen und registrieren der "Hooks" aller Anwendungen
for the times above admin de für die oben angegebenen Zeiten for the times above admin de für die oben angegebenen Zeiten
for the times below (empty values count as '*', all empty = every minute) admin de für die darunter angegebenen Zeiten (leere Felder zählen als "*", alles leer = jede Minute) for the times below (empty values count as '*', all empty = every minute) admin de für die darunter angegebenen Zeiten (leere Felder zählen als "*", alles leer = jede Minute)

View File

@ -113,6 +113,8 @@ change password for %1 admin en change password for %1
check acl for entries of not (longer) existing accounts admin en Check ACL for entries of not (longer) existing accounts check acl for entries of not (longer) existing accounts admin en Check ACL for entries of not (longer) existing accounts
check ip address of all sessions admin en Check IP address of all sessions check ip address of all sessions admin en Check IP address of all sessions
check items to <b>%1</b> to %2 for %3 admin en Check items to <b>%1</b> to %2 for %3 check items to <b>%1</b> to %2 for %3 admin en Check items to <b>%1</b> to %2 for %3
check virtual filesystem admin en Check virtual filesystem
children admin en Children
click to select a color admin en Click to select a color click to select a color admin en Click to select a color
color admin en Color color admin en Color
command scheduled to run at %1 admin en Command scheduled to run at %1 command scheduled to run at %1 admin en Command scheduled to run at %1
@ -253,14 +255,18 @@ false admin en false
field '%1' already exists !!! admin en Field '%1' already exists !!! field '%1' already exists !!! admin en Field '%1' already exists !!!
file space admin en File space file space admin en File space
file space must be an integer admin en File space must be an integer file space must be an integer admin en File space must be an integer
find and register all application hooks admin en Find and Register all Application Hooks filesystem check reported no problems. admin en Filesystem check reported no problems.
for the times above admin en for the times above find and register all application hooks admin en Find and register all application hooks
for the times below (empty values count as '*', all empty = every minute) admin en for the times below (empty values count as '*', all empty = every minute) for the times above admin en For the times above
force selectbox admin en Force Selectbox for the times below (empty values count as '*', all empty = every minute) admin en For the times below: empty values count as '*', all empty = every minute.
forward also to admin en forward also to force password strength (1-5, default empty: no check against rules for a strong password)? admin en Set required password strength. 1 = weak, up to 5 = very strong. Default = empty, no password strength checked
forward emails to admin en forward emails to force selectbox admin en Force select box
forward only admin en forward only force users to change their password regularily?(empty for no,number for after that number of days admin en Set recurrent forced password change. Set a number of days. Empty = No
global categories common en Global Categories forward also to admin en Forward also to
forward emails to admin en Forward emails to
forward only admin en Forward only
full name admin en Full name
global categories common en Global categories
go directly to admin menu, returning here the next time you click on administration. admin en Go directly to admin menu, returning here the next time you click on administration. go directly to admin menu, returning here the next time you click on administration. admin en Go directly to admin menu, returning here the next time you click on administration.
governmental: incl. state or municipal authorities or services admin en Governmental: incl. state or municipal authorities or services governmental: incl. state or municipal authorities or services admin en Governmental: incl. state or municipal authorities or services
grant admin en Grant grant admin en Grant

View File

@ -267,7 +267,7 @@ switch($cmd)
} }
die("\n/ NOT mounted with 'storage=db' --> no need to convert!\n\n"); die("\n/ NOT mounted with 'storage=db' --> no need to convert!\n\n");
} }
$num_files = sqlfs_stream_wrapper::migrate_db2fs(); // throws exception on error $num_files = sqlfs_utils::migrate_db2fs(); // throws exception on error
echo "\n$num_files files migrated from DB to filesystem.\n"; echo "\n$num_files files migrated from DB to filesystem.\n";
$new_url = preg_replace('/storage=db&?/','',$fstab['/']); $new_url = preg_replace('/storage=db&?/','',$fstab['/']);
if (substr($new_url,-1) == '?') $new_url = substr($new_url,0,-1); if (substr($new_url,-1) == '?') $new_url = substr($new_url,0,-1);

View File

@ -1,13 +1,13 @@
<?php <?php
/** /**
* eGroupWare API: VFS - new DB based VFS stream wrapper * EGroupware API: VFS - new DB based VFS stream wrapper
* *
* @link http://www.egroupware.org * @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api * @package api
* @subpackage vfs * @subpackage vfs
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de> * @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2008-9 by Ralf Becker <RalfBecker-AT-outdoor-training.de> * @copyright (c) 2008-12 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$ * @version $Id$
*/ */
@ -700,7 +700,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
unset(self::$stat_cache[$path]); unset(self::$stat_cache[$path]);
$stmt = self::$pdo->prepare('INSERT INTO '.self::TABLE.' (fs_name,fs_dir,fs_mode,fs_uid,fs_gid,fs_size,fs_mime,fs_created,fs_modified,fs_creator'. $stmt = self::$pdo->prepare('INSERT INTO '.self::TABLE.' (fs_name,fs_dir,fs_mode,fs_uid,fs_gid,fs_size,fs_mime,fs_created,fs_modified,fs_creator'.
') VALUES (:fs_name,:fs_dir,:fs_mode,:fs_uid,:fs_gid,:fs_size,:fs_mime,:fs_created,:fs_modified,:fs_creator)'); ') VALUES (:fs_name,:fs_dir,:fs_mode,:fs_uid,:fs_gid,:fs_size,:fs_mime,:fs_created,:fs_modified,:fs_creator)');
return $stmt->execute(array( if (($ok = $stmt->execute(array(
'fs_name' => egw_vfs::basename($path), 'fs_name' => egw_vfs::basename($path),
'fs_dir' => $parent['ino'], 'fs_dir' => $parent['ino'],
'fs_mode' => $parent['mode'], 'fs_mode' => $parent['mode'],
@ -711,7 +711,25 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
'fs_created' => self::_pdo_timestamp(time()), 'fs_created' => self::_pdo_timestamp(time()),
'fs_modified' => self::_pdo_timestamp(time()), 'fs_modified' => self::_pdo_timestamp(time()),
'fs_creator' => egw_vfs::$user, 'fs_creator' => egw_vfs::$user,
)); ))))
{
// check if some other process created the directory parallel to us (sqlfs would gives SQL errors later!)
$new_fs_id = self::$pdo->lastInsertId('egw_sqlfs_fs_id_seq');
unset($stmt); // free statement object, on some installs a new prepare fails otherwise!
$stmt = self::$pdo->prepare($q='SELECT COUNT(*) FROM '.self::TABLE.
' WHERE fs_dir=:fs_dir AND fs_active=:fs_active AND fs_name'.self::$case_sensitive_equal.':fs_name');
if ($stmt->execute(array(
'fs_dir' => $parent['ino'],
'fs_active' => self::_pdo_boolean(true),
'fs_name' => egw_vfs::basename($path),
)) && $stmt->fetchColumn() > 1) // if there's more then one --> remove our new dir
{
self::$pdo->query('DELETE FROM '.self::TABLE.' WHERE fs_id='.$new_fs_id);
}
}
return $ok;
} }
/** /**
@ -1794,80 +1812,6 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
if (self::LOG_LEVEL > 1) foreach((array)$props as $k => $v) error_log(__METHOD__."($path_ids,$ns) $k => ".array2string($v)); if (self::LOG_LEVEL > 1) foreach((array)$props as $k => $v) error_log(__METHOD__."($path_ids,$ns) $k => ".array2string($v));
return $props; return $props;
} }
/**
* Migrate SQLFS content from DB to filesystem
*
* @param boolean $debug true to echo a message for each copied file
*/
static function migrate_db2fs($debug=false)
{
if (!is_object(self::$pdo))
{
self::_pdo();
}
$query = 'SELECT fs_id,fs_name,fs_size,fs_content'.
' FROM '.self::TABLE.' WHERE fs_content IS NOT NULL';
$stmt = self::$pdo->prepare($query);
$stmt->bindColumn(1,$fs_id);
$stmt->bindColumn(2,$fs_name);
$stmt->bindColumn(3,$fs_size);
$stmt->bindColumn(4,$fs_content,PDO::PARAM_LOB);
if ($stmt->execute())
{
foreach($stmt as $row)
{
// hack to work around a current php bug (http://bugs.php.net/bug.php?id=40913)
// PDOStatement::bindColumn(,,PDO::PARAM_LOB) is not working for MySQL, content is returned as string :-(
if (is_string($fs_content))
{
$name = md5($fs_name.$fs_id);
$GLOBALS[$name] =& $fs_content;
require_once(EGW_API_INC.'/class.global_stream_wrapper.inc.php');
$content = fopen('global://'.$name,'r');
if (!$content) echo "fopen('global://$name','w' failed, strlen(\$GLOBALS['$name'])=".strlen($GLOBALS[$name]).", \$GLOBALS['$name']=".substr($GLOBALS['$name'],0,100)."...\n";
unset($GLOBALS[$name]); // unset it, so it does not use up memory, once the stream is closed
}
else
{
$content = $fs_content;
}
if (!is_resource($content))
{
throw new egw_exception_assertion_failed(__METHOD__."(): fs_id=$fs_id ($fs_name, $fs_size bytes) content is NO resource! ".array2string($content));
}
$filename = self::_fs_path($fs_id);
if (!file_exists($fs_dir=egw_vfs::dirname($filename)))
{
self::mkdir_recursive($fs_dir,0700,true);
}
if (!($dest = fopen($filename,'w')))
{
throw new egw_exception_assertion_failed(__METHOD__."(): fopen($filename,'w') failed!");
}
if (($bytes = stream_copy_to_stream($content,$dest)) != $fs_size)
{
throw new egw_exception_assertion_failed(__METHOD__."(): fs_id=$fs_id ($fs_name) $bytes bytes copied != size of $fs_size bytes!");
}
if ($debug) echo "$fs_id: $fs_name: $bytes bytes copied to fs\n";
fclose($dest);
fclose($content); unset($content);
++$n;
}
unset($stmt);
if ($n) // delete all content in DB, if there was some AND no error (exception thrown!)
{
$query = 'UPDATE '.self::TABLE.' SET fs_content=NULL WHERE fs_content IS NOT NULL';
$stmt = self::$pdo->prepare($query);
$stmt->execute();
}
}
return $n;
}
} }
stream_register_wrapper(sqlfs_stream_wrapper::SCHEME ,'sqlfs_stream_wrapper'); stream_register_wrapper(sqlfs_stream_wrapper::SCHEME ,'sqlfs_stream_wrapper');

View File

@ -0,0 +1,352 @@
<?php
/**
* EGroupware API: sqlfs stream wrapper utilities: migration db-fs, fsck
*
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage vfs
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2008-12 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
require_once 'class.iface_stream_wrapper.inc.php';
require_once 'class.sqlfs_stream_wrapper.inc.php';
/**
* sqlfs stream wrapper utilities: migration db-fs, fsck
*/
class sqlfs_utils extends sqlfs_stream_wrapper
{
/**
* Migrate SQLFS content from DB to filesystem
*
* @param boolean $debug true to echo a message for each copied file
*/
static function migrate_db2fs($debug=false)
{
if (!is_object(self::$pdo))
{
self::_pdo();
}
$query = 'SELECT fs_id,fs_name,fs_size,fs_content'.
' FROM '.self::TABLE.' WHERE fs_content IS NOT NULL';
$stmt = self::$pdo->prepare($query);
$stmt->bindColumn(1,$fs_id);
$stmt->bindColumn(2,$fs_name);
$stmt->bindColumn(3,$fs_size);
$stmt->bindColumn(4,$fs_content,PDO::PARAM_LOB);
if ($stmt->execute())
{
foreach($stmt as $row)
{
// hack to work around a current php bug (http://bugs.php.net/bug.php?id=40913)
// PDOStatement::bindColumn(,,PDO::PARAM_LOB) is not working for MySQL, content is returned as string :-(
if (is_string($fs_content))
{
$name = md5($fs_name.$fs_id);
$GLOBALS[$name] =& $fs_content;
require_once(EGW_API_INC.'/class.global_stream_wrapper.inc.php');
$content = fopen('global://'.$name,'r');
if (!$content) echo "fopen('global://$name','w' failed, strlen(\$GLOBALS['$name'])=".strlen($GLOBALS[$name]).", \$GLOBALS['$name']=".substr($GLOBALS['$name'],0,100)."...\n";
unset($GLOBALS[$name]); // unset it, so it does not use up memory, once the stream is closed
}
else
{
$content = $fs_content;
}
if (!is_resource($content))
{
throw new egw_exception_assertion_failed(__METHOD__."(): fs_id=$fs_id ($fs_name, $fs_size bytes) content is NO resource! ".array2string($content));
}
$filename = self::_fs_path($fs_id);
if (!file_exists($fs_dir=egw_vfs::dirname($filename)))
{
self::mkdir_recursive($fs_dir,0700,true);
}
if (!($dest = fopen($filename,'w')))
{
throw new egw_exception_assertion_failed(__METHOD__."(): fopen($filename,'w') failed!");
}
if (($bytes = stream_copy_to_stream($content,$dest)) != $fs_size)
{
throw new egw_exception_assertion_failed(__METHOD__."(): fs_id=$fs_id ($fs_name) $bytes bytes copied != size of $fs_size bytes!");
}
if ($debug) echo "$fs_id: $fs_name: $bytes bytes copied to fs\n";
fclose($dest);
fclose($content); unset($content);
++$n;
}
unset($stmt);
if ($n) // delete all content in DB, if there was some AND no error (exception thrown!)
{
$query = 'UPDATE '.self::TABLE.' SET fs_content=NULL WHERE fs_content IS NOT NULL';
$stmt = self::$pdo->prepare($query);
$stmt->execute();
}
}
return $n;
}
/**
* Check and optionaly fix corruption in sqlfs
*
* @param boolean $check_only=true
* @return array with messages / found problems
*/
public static function fsck($check_only=true)
{
if (!is_object(self::$pdo))
{
self::_pdo();
}
$msgs = self::fsck_fix_multiple_active($check_only);
$msgs = array_merge($msgs, self::fsck_fix_unconnected($check_only));
$msgs = array_merge($msgs, self::fsck_fix_no_content($check_only));
return $msgs;
}
/**
* Check and optionally remove files without content part in physical filesystem
*
* @param boolean $check_only=true
* @return array with messages / found problems
*/
private static function fsck_fix_no_content($check_only=true)
{
$msgs = array();
foreach(self::$pdo->query('SELECT fs_id FROM '.self::TABLE.
" WHERE fs_mime!='httpd/unix-directory' AND fs_content IS NULL AND fs_link IS NULL") as $row)
{
if (!file_exists($phy_path=self::_fs_path($row['fs_id'])))
{
egw_vfs::$is_root = true;
$path = self::id2path($row['fs_id']);
if ($check_only)
{
$msgs[] = lang('File %1 has no content in physical filesystem %2!',
$path.' (#'.$row['fs_id'].')',$phy_path);
}
elseif (self::unlink($path.'?storage=db')) // storage=db to not try to delete not existing phy. file
{
$msgs[] = lang('File %1 has no content in physical filesystem %2 --> file removed!',$path,$phy_path);
}
else
{
$msgs[] = lang('File %1 has no content in physical filesystem %2 --> failed to remove file!',
$path.' (#'.$row['fs_id'].')',$phy_path);
}
egw_vfs::$is_root = false;
}
}
if ($check_only && $msgs)
{
$msgs[] = lang('Files without content in physical filesystem will be removed.');
}
return $msgs;
}
/**
* Name of lost+found directory for unconnected nodes
*/
const LOST_N_FOUND = '/lost+found';
const LOST_N_FOUND_MOD = 070;
const LOST_N_FOUND_GRP = 'Admins';
/**
* Check and optionally fix unconnected nodes - parent directory does not (longer) exists:
*
* SELECT fs.*
* FROM egw_sqlfs fs
* LEFT JOIN egw_sqlfs dir ON dir.fs_id=fs.fs_dir
* WHERE fs.fs_id > 1 && dir.fs_id IS NULL
*
* @param boolean $check_only=true
* @return array with messages / found problems
*/
private static function fsck_fix_unconnected($check_only=true)
{
$msgs = array();
foreach(self::$pdo->query('SELECT fs.* FROM '.self::TABLE.' fs'.
' LEFT JOIN '.self::TABLE.' dir ON dir.fs_id=fs.fs_dir'.
' WHERE fs.fs_id > 1 && dir.fs_id IS NULL') as $row)
{
if ($check_only)
{
$msgs[] = lang('Found unconnected %1 %2!',
mime_magic::mime2label($row['fs_mime']),
egw_vfs::decodePath($row['fs_name']).' (#'.$row['fs_id'].')');
continue;
}
if (!isset($lostnfound))
{
// check if we already have /lost+found, create it if not
if (!($lostnfound = self::url_stat(self::LOST_N_FOUND, STREAM_URL_STAT_QUIET)))
{
egw_vfs::$is_root = true;
if (!self::mkdir(self::LOST_N_FOUND, self::LOST_N_FOUND_MOD, 0) ||
!(!($admins = $GLOBALS['egw']->accounts->name2id(self::LOST_N_FOUND_GRP)) ||
self::chgrp(self::LOST_N_FOUND, $admins) && self::chmod(self::LOST_N_FOUND,self::LOST_N_FOUND_MOD)) ||
!($lostnfound = self::url_stat(self::LOST_N_FOUND, STREAM_URL_STAT_QUIET)))
{
$msgs[] = lang("Can't create directory %1 to connect found unconnected nodes to it!",self::LOST_N_FOUND);
}
else
{
$msgs[] = lang('Successful created new directory %1 for unconnected nods.',self::LOST_N_FOUND);
}
egw_vfs::$is_root = false;
if (!$lostnfound) break;
}
$stmt = self::$pdo->prepare('UPDATE '.self::TABLE.' SET fs_dir=:fs_dir WHERE fs_id=:fs_id');
}
if ($stmt->execute(array(
'fs_dir' => $lostnfound['ino'],
'fs_id' => $row['fs_id'],
)))
{
$msgs[] = lang('Moved unconnected %1 %2 to %3.',
mime_magic::mime2label($row['fs_mime']),
egw_vfs::decodePath($row['fs_name']).' (#'.$row['fs_id'].')',
self::LOST_N_FOUND);
}
else
{
$msgs[] = lang('Faild to move unconnected %1 %2 to %3!',
mime_magic::mime2label($row['fs_mime']), egw_vfs::decodePath($row['fs_name']), self::LOST_N_FOUND);
}
}
if ($check_only && $msgs)
{
$msgs[] = lang('Unconnected nodes will be moved to %1.',self::LOST_N_FOUND);
continue;
}
return $msgs;
}
/**
* Check and optionally fix multiple active files and directories with identical path
*
* @param boolean $check_only=true
* @return array with messages / found problems
*/
private static function fsck_fix_multiple_active($check_only=true)
{
$msgs = array();
foreach(self::$pdo->query('SELECT fs_dir,fs_name,COUNT(*) FROM '.self::TABLE.
' WHERE fs_active='.self::_pdo_boolean(true).
' GROUP BY fs_dir,fs_name'.
' HAVING COUNT(*) > 1') as $row)
{
if (!isset($stmt))
{
$stmt = self::$pdo->prepare('SELECT *,(SELECT COUNT(*) FROM '.self::TABLE.' sub WHERE sub.fs_dir=fs.fs_id) AS children'.
' FROM '.self::TABLE.' fs'.
' WHERE fs.fs_dir=:fs_dir AND fs.fs_active='.self::_pdo_boolean(true).' AND fs.fs_name'.self::$case_sensitive_equal.':fs_name'.
" ORDER BY fs.fs_mime='httpd/unix-directory' DESC,children DESC,fs.fs_modified DESC");
$inactivate_stmt = self::$pdo->prepare('UPDATE '.self::TABLE.
' SET fs_active='.self::_pdo_boolean(false).
' WHERE fs_dir=:fs_dir AND fs_active='.self::_pdo_boolean(true).
' AND fs_name'.self::$case_sensitive_equal.':fs_name AND fs_id!=:fs_id');
}
//$msgs[] = array2string($row);
$cnt = 0;
$stmt->execute(array(
'fs_dir' => $row['fs_dir'],
'fs_name' => $row['fs_name'],
));
foreach($stmt as $entry)
{
if ($entry['fs_mime'] == 'httpd/unix-directory')
{
if (!$n)
{
$dir = $entry; // directory to keep
$msgs[] = lang('%1 directories %2 found!', $row[2], self::id2path($entry['fs_id']));
if ($check_only) break;
}
else
{
if ($entry['children'])
{
$msgs[] = lang('Moved %1 children from directory fs_id=%2 to %3',
$children = self::$pdo->exec('UPDATE '.self::TABLE.' SET fs_dir='.(int)$dir['fs_id'].
' WHERE fs_dir='.(int)$entry['fs_id']),
$entry['fs_id'], $dir['fs_id']);
$dir['children'] += $children;
}
self::$pdo->query('DELETE FROM '.self::TABLE.' WHERE fs_id='.(int)$entry['fs_id']);
$msgs[] = lang('Removed (now) empty directory fs_id=%1',$entry['fs_id']);
}
}
elseif (isset($dir)) // file and directory with same name exist!
{
if (!$check_only)
{
$inactivate_stmt->execute(array(
'fs_dir' => $row['fs_dir'],
'fs_name' => $row['fs_name'],
'fs_id' => $dir['fs_id'],
));
$cnt = $inactivate_stmt->rowCount();
}
else
{
$cnt = ucfirst(lang('none of %1', $row[2]-1));
}
$msgs[] = lang('%1 active file(s) with same name as directory inactivated!',$cnt);
break;
}
else // newest file --> set for all other fs_active=false
{
if (!$check_only)
{
$inactivate_stmt->execute(array(
'fs_dir' => $row['fs_dir'],
'fs_name' => $row['fs_name'],
'fs_id' => $entry['fs_id'],
));
$cnt = $inactivate_stmt->rowCount();
}
else
{
$cnt = lang('none of %1', $row[2]-1);
}
$msgs[] = lang('More then one active file %1 found, inactivating %2 older revisions!',
self::id2path($entry['fs_id']), $cnt);
break;
}
}
unset($dir);
if ($cnt && !isset($inactivate_msg_added))
{
$msgs[] = lang('To examine or reinstate inactived files, you might need to turn versioning on.');
$inactivate_msg_added = true;
}
}
return $msgs;
}
}
// fsck testcode, if this file is called via it's URL (you need to uncomment it!)
/*if (isset($_SERVER['SCRIPT_FILENAME']) && $_SERVER['SCRIPT_FILENAME'] == __FILE__)
{
$GLOBALS['egw_info'] = array(
'flags' => array(
'currentapp' => 'admin',
'nonavbar' => true,
),
);
include_once '../../header.inc.php';
$msgs = sqlfs_utils::fsck(!isset($_GET['check_only']) || $_GET['check_only']);
echo '<p>'.implode("</p>\n<p>", (array)$msgs)."</p>\n";
}*/