added listing archiving-and-restore function (vandalism protection), and ...

- added log-picture removal on log deletion (bugfix)
- block deletion of user accounts if archived logs or cache reports exist
- show number of archived logs and cache reports in adminuser interface
- improved cache description deletion confirmation message
This commit is contained in:
following
2013-03-06 00:34:35 +01:00
parent 085c13983f
commit 5fe55ee0c2
18 changed files with 1509 additions and 73 deletions

View File

@@ -201,7 +201,7 @@
else if ((substr($sql, $sqlpos - $arglength - 1, 1) == '`') && (substr($sql, $sqlpos + 1, 1) == '`'))
$filtered_sql .= sql_escape_backtick($args[$arg]);
else
sql_error();
sql_error($sql);
}
}
else
@@ -258,7 +258,7 @@
$filtered_sql .= '`';
}
else
sql_error();
sql_error($sql);
$sqlpos = $nextarg + $arglength + 1;
}
@@ -302,7 +302,7 @@
$result = $sqldebugger->execute($filtered_sql, $dblink, ($dblink===$db['dblink_slave']), $db['slave_server']);
if ($result === false)
{
sql_error();
sql_error($filtered_sql);
}
}
else
@@ -318,7 +318,7 @@
$result = @mysql_query($filtered_sql, $dblink);
if ($result === false)
{
sql_error();
sql_error($filtered_sql);
}
if ($opt['db']['warn']['time'] > 0)
@@ -876,7 +876,7 @@
$db['dblink_slave'] = false;
}
function sql_error()
function sql_error($sqlstatement="")
{
global $tpl, $opt, $db;
global $bSmartyNoTranslate;
@@ -888,6 +888,8 @@
$errno = mysql_errno();
$error = mysql_error();
if ($sqlstatement != "")
$error .= "\n\nSQL statement: " . $sqlstatement;
if ($db['connected'] == false)
$bSmartyNoTranslate = true;
@@ -902,7 +904,7 @@
$mail->name = 'sql_error';
$mail->assign('errno', $errno);
$mail->assign('error', $error);
$mail->assign('error', str_replace("\n","\r\n",$error));
$mail->assign('trace', print_r(debug_backtrace(), true));
$mail->send();
@@ -914,14 +916,14 @@
if (isset($tpl))
{
if ($opt['db']['error']['display'] == true)
$tpl->error('MySQL error' . ' (' . $errno . '): ' . $error);
$tpl->error('MySQL error (' . $errno . '): ' . $error);
else
$tpl->error('A database command could not be performed.');
}
else
{
if ($opt['db']['error']['display'] == true)
die('<html><body>' . htmlspecialchars('MySQL error (' .$errno . '): ' . $error) . '</body></html>');
die('<html><body>' . htmlspecialchars('MySQL error (' .$errno . '): ' . str_replace("\n,","<br />", $error)) . '</body></html>');
else
die('<html><body>A database command could not be performed</body></html>');
}

View File

@@ -146,7 +146,6 @@ class cachelog
{
return $this->reCacheLog->setValue('text_htmledit', $value);
}
function getUUID()
{
return $this->reCacheLog->getValue('uuid');
@@ -168,6 +167,15 @@ class cachelog
return $this->reCacheLog->setValue('node', $value);
}
function getOwnerNotified()
{
return $this->reCacheLog->getValue('owner_notified') != 0;
}
function setOwnerNotified($value)
{
return $this->reCacheLog->setValue('owner_notified', $value ? 1 : 0);
}
function getAnyChanged()
{
return $this->reCacheLog->getAnyChanged();
@@ -177,7 +185,18 @@ class cachelog
function save()
{
sql_slave_exclude();
return $this->reCacheLog->save();
$saved = $this->reCacheLog->save();
if ($saved && $this->nLogId == ID_NEW)
$this->nLogId = $this->reCacheLog->getValue('id');
return $saved;
}
function updatePictureStat()
{
sql("UPDATE `cache_logs` SET `picture` =
(SELECT COUNT(*) FROM `pictures` WHERE `object_type`=1 AND `object_id`='&1')
WHERE `id`= '&1'",
$this->getLogId());
}
function allowView()

View File

@@ -33,6 +33,7 @@
define('ADMIN_MAINTAINANCE', 2); // check table etc.
define('ADMIN_USER', 4); // drop users, caches etc.
define('ADMIN_NEWS', 8); // approve news entries
define('ADMIN_RESTORE', 16); // restore vandalized listings
define('ADMIN_ROOT', 128 | 127); // root + all previous rights
define('ATTRIB_SELECTED', 1);

View File

@@ -93,7 +93,7 @@ class picture
return false;
$sExtension = mb_strtolower(substr($sFilename, strrpos($sFilename, '.') + 1));
if (strpos(';' . $opt['logic']['pictures']['extensions'] . ';', ';' . $sExtension . ';') !== false)
return true;
else
@@ -108,13 +108,15 @@ class picture
return;
if (strpos($sFilename, '.') === false)
return;
$sExtension = mb_strtolower(substr($sFilename, strrpos($sFilename, '.') + 1));
$this->sFileExtension = $sExtension;
$sUUID = $this->getUUID();
$this->sFileExtension = $sExtension;
$this->setUrl($opt['logic']['pictures']['url'] . $sUUID . '.' . $sExtension);
//$this->setThumbUrl($opt['logic']['pictures']['thumb_url'] . substr($sUUID, 0, 1) . '/' . substr($sUUID, 1, 1) . '/' . $sUUID . '.' . $sExtension);
$this->bFilenamesSet = true;
}
@@ -123,17 +125,51 @@ class picture
return $this->nPictureId;
}
function delete()
private function setArchiveFlag($bRestoring, $original_id=0)
{
global $opt;
global $login;
// delete record, image and thumb
@unlink($this->getFilename());
@unlink($this->getThumbFilename());
// This function determines if an insert, update oder deletion at pictures table
// ist to be recorded for vandalism recovery, depending on WHO OR WHY the
// operation is done. Other conditions, depending on the data, are handled
// by triggers.
//
// Data is passed by ugly global DB variables, so try call this function as
// close before the targetet DB operation as possible.
sql("DELETE FROM `pictures` WHERE `id`='&1'", $this->nPictureId);
if ($this->getObjectType() == 1)
{
/*
$owner_id = sql_value("SELECT `user_id` FROM `caches` WHERE `cache_id`=
IFNULL((SELECT `cache_id` FROM `cache_logs` WHERE `id`='&1'),
(SELECT `cache_id` FROM `cache_logs_archived` WHERE `id`='&1'))",
0, $this->getObjectId());
*/
$logger_id = sql_value("SELECT
IFNULL((SELECT `user_id` FROM `cache_logs` WHERE `id`='&1'),
(SELECT `user_id` FROM `cache_logs_archived` WHERE `id`='&1'))",
0, $this->getObjectId());
return true;
$archive = ($bRestoring || $login->userid != $logger_id);
}
else
$archive = true;
sql("SET @archive_picop=" . ($archive ? "TRUE" : "FALSE"));
sql_slave("SET @archive_picop=" . ($archive ? "TRUE" : "FALSE"));
sql("SET @original_picid='&1'", $original_id);
sql_slave("SET @original_picid='&1'", $original_id);
// @archive_picop and @original_picid are evaluated by trigger functions
}
private function resetArchiveFlag()
{
sql("SET @archive_picop=FALSE");
sql("SET @original_picid=0");
sql_slave("SET @archive_picop=FALSE");
sql_slave("SET @original_picid=0");
}
function getUrl()
@@ -179,6 +215,14 @@ class picture
{
return $this->rePicture->setValue('local', $value ? 1 : 0);
}
function getUnknownFormat()
{
return $this->rePicture->getValue('unknown_format')!=0;
}
function setUnknownFormat($value)
{
return $this->rePicture->setValue('unknown_format', $value ? 1 : 0);
}
function getDisplay()
{
return $this->rePicture->getValue('display')!=0;
@@ -197,18 +241,18 @@ class picture
}
function getFilename()
{
// works intendently before bFilenameSet == true !
global $opt;
if (mb_substr($opt['logic']['pictures']['dir'], -1, 1) != '/')
$opt['logic']['pictures']['dir'] .= '/';
$uuid = $this->getUUID();
$url = $this->getUrl();
$fna = mb_split('\\.', $url);
$extension = mb_strtolower($fna[count($fna) - 1]);
return $opt['logic']['pictures']['dir'] . $uuid . '.' . $extension;
$fna = mb_split('\\/', $url);
return $opt['logic']['pictures']['dir'] . end($fna);
}
function getThumbFilename()
{
global $opt;
@@ -216,16 +260,16 @@ class picture
if (mb_substr($opt['logic']['pictures']['thumb_dir'], -1, 1) != '/')
$opt['logic']['pictures']['thumb_dir'] .= '/';
$uuid = $this->getUUID();
$url = $this->getUrl();
$fna = mb_split('\\.', $url);
$extension = mb_strtolower($fna[count($fna) - 1]);
$fna = mb_split('\\/', $url);
$filename = end($fna);
$dir1 = mb_strtoupper(mb_substr($uuid, 0, 1));
$dir2 = mb_strtoupper(mb_substr($uuid, 1, 1));
$dir1 = mb_strtoupper(mb_substr($filename, 0, 1));
$dir2 = mb_strtoupper(mb_substr($filename, 1, 1));
return $opt['logic']['pictures']['thumb_dir'] . $dir1 . '/' . $dir2 . '/' . $uuid . '.' . $extension;
return $opt['logic']['pictures']['thumb_dir'] . $dir1 . '/' . $dir2 . '/' . $filename;
}
function getLogId()
{
if ($this->getObjectType() == OBJECT_CACHELOG)
@@ -311,13 +355,29 @@ class picture
return $this->rePicture->getAnyChanged();
}
// return if successfull (with insert)
function save()
// return true if successful (with insert)
function save($restore=false, $original_id=0, $original_url="")
{
$undelete = ($original_id != 0);
if ($undelete)
if ($this->bFilenamesSet == true)
return false;
else
{
// restore picture file
$this->setUrl($original_url); // set the url, so that we can
$filename = $this->getFilename(); // .. retreive the file path+name
$this->setFilenames($filename); // now set url(s) from the new uuid
@rename($this->deleted_filename($filename), $this->getFilename());
}
if ($this->bFilenamesSet == false)
return false;
$this->setArchiveFlag($restore, $original_id);
$bRetVal = $this->rePicture->save();
$this->resetArchiveFlag();
if ($bRetVal)
{
@@ -331,6 +391,43 @@ class picture
return $bRetVal;
}
function delete($restore=false)
{
// see also removelog.php, 'remove log pictures'
global $opt;
// delete record, image and thumb
$this->setArchiveFlag($restore);
sql("DELETE FROM `pictures` WHERE `id`='&1'", $this->nPictureId);
$this->resetArchiveFlag();
// archive picture if picture record has been archived
if (sql_value("SELECT `id` FROM `pictures_modified` WHERE `id`='&1'",
0, $this->getPictureId()) != 0)
{
$filename = $this->getFilename();
@rename($filename, $this->deleted_filename($filename));
}
else
@unlink($filename);
@unlink($this->getThumbFilename());
return true;
}
private function deleted_filename($filename)
{
$fna = mb_split('\\/',$filename);
$fna[] = end($fna);
$fna[count($fna)-2] = 'deleted';
$dp = "";
foreach ($fna as $fp)
$dp .= "/" . $fp;
return substr($dp,1);
}
function allowEdit()
{
global $login;

View File

@@ -880,6 +880,9 @@ class user
sql_free_result($rsp);
}
sql_free_result($rs);
// discard achived logs' texts
sql("UPDATE `cache_logs_archived` SET `text`='' WHERE `user_id`='&1'", $this->getUserId());
// success
return true;
@@ -1130,13 +1133,12 @@ class user
if ($login->userid != $this->nUserId && ($login->admin & ADMIN_USER) != ADMIN_USER)
return false;
if (sql_value("SELECT COUNT(*) FROM `caches` WHERE `user_id`='&1'", 0, $this->nUserId) > 0)
return false;
if (sql_value("SELECT COUNT(*) FROM `cache_logs` WHERE `user_id`='&1'", 0, $this->nUserId) > 0)
return false;
return true;
return
sql_value("SELECT COUNT(*) FROM `caches` WHERE `user_id`='&1'", 0, $this->nUserId)
+ sql_value("SELECT COUNT(*) FROM `cache_logs` WHERE `user_id`='&1'", 0, $this->nUserId)
+ sql_value("SELECT COUNT(*) FROM `cache_logs_archived` WHERE `user_id`='&1'", 0, $this->nUserId)
+ sql_value("SELECT COUNT(*) FROM `cache_reports` WHERE `userid`='&1'", 0, $this->nUserId)
== 0;
}
function delete()
@@ -1160,13 +1162,7 @@ class user
serialize($backup));
sql("DELETE FROM `user` WHERE `user_id`='&1'", $this->nUserId);
sql("DELETE FROM `cache_adoption` WHERE `user_id`='&1'", $this->nUserId);
sql("DELETE FROM `cache_ignore` WHERE `user_id`='&1'", $this->nUserId);
sql("DELETE FROM `cache_rating` WHERE `user_id`='&1'", $this->nUserId);
sql("DELETE FROM `cache_watches` WHERE `user_id`='&1'", $this->nUserId);
sql("DELETE FROM `stat_user` WHERE `user_id`='&1'", $this->nUserId);
sql("DELETE FROM `user_options` WHERE `user_id`='&1'", $this->nUserId);
sql("DELETE FROM `watches_waiting` WHERE `user_id`='&1'", $this->nUserId);
// all data in depending tables is cleared via trigger
$this->reload();