Files
Phraseanet/lib/classes/task/archive.class.php
2011-02-16 16:09:48 +01:00

2024 lines
57 KiB
PHP

<?php
class task_archive extends phraseatask
{
// ====================================================================
// getName : must return the name for this kind of task
// MANDATORY
// ====================================================================
public function getName()
{
return(_('task::archive:Archivage'));
}
// ====================================================================
// graphic2xml : must return the xml (text) version of the form
// ====================================================================
public function graphic2xml($oldxml)
{
// global $parm;
$request = httpRequest::getInstance();
$parm2 = $request->get_parms(
"base_id"
, "hotfolder"
, "period"
, "move_archived"
, "move_error"
, "copy_spe"
, "delfolder"
, 'cold'
);
$dom = new DOMDocument();
$dom->formatOutput = true;
$dom->preserveWhiteSpace = false;
if( $dom->loadXML($oldxml) )
{
$xmlchanged = false;
// foreach($parm2 as $pname=>$pvalue)
foreach(array("str:base_id", "str:hotfolder", "str:period", "boo:move_archived", "boo:move_error", "boo:delfolder", 'boo:copy_spe', 'str:cold') as $pname)
{
$ptype = substr($pname, 0, 3);
$pname = substr($pname, 4);
$pvalue = $parm2[$pname];
if( $ns = $dom->getElementsByTagName($pname)->item(0) )
{
// le champ existait dans le xml, on supprime son ancienne valeur (tout le contenu)
while( ($n = $ns->firstChild) )
$ns->removeChild($n);
}
else
{
// le champ n'existait pas dans le xml, on le cree
$ns = $dom->documentElement->appendChild($dom->createElement($pname));
}
// on fixe sa valeur
switch($ptype)
{
case "str":
$ns->appendChild($dom->createTextNode($pvalue));
break;
case "boo":
$ns->appendChild($dom->createTextNode($pvalue ? '1':'0'));
break;
}
$xmlchanged = true;
}
}
return($dom->saveXML());
}
// ====================================================================
// xml2graphic : must fill the graphic form (using js) from xml
// ====================================================================
public function xml2graphic($xml, $form)
{
if( ($sxml = simplexml_load_string($xml)) ) // in fact XML IS always valid here...
{
// ... but we could check for safe values (ex. 0 < period < 3600)
if( (int)($sxml->period) < 10 )
$sxml->period = 10;
elseif( (int)($sxml->period) > 300 )
$sxml->period = 300;
if( (int)($sxml->cold) < 5 )
$sxml->cold = 5;
elseif( (int)($sxml->cold) > 3600 )
$sxml->cold = 3600;
?>
<script type="text/javascript">
var i;
var opts = <?php echo $form?>.base_id.options;
var basefound = 0;
for(i=1; basefound==0 && i<opts.length; i++)
{
if(opts[i].value == "<?php echo p4string::MakeString($sxml->base_id, "form")?>")
basefound = i;
}
opts[basefound].selected = true;
<?php echo $form?>.hotfolder.value = "<?php echo p4string::MakeString($sxml->hotfolder, "js", '"')?>";
<?php echo $form?>.period.value = "<?php echo p4string::MakeString($sxml->period, "js", '"')?>";
<?php echo $form?>.cold.value = "<?php echo p4string::MakeString($sxml->cold, "js", '"')?>";
<?php echo $form?>.move_archived.checked = <?php echo p4field::isyes($sxml->move_archived) ? "true" : "false"?>;
<?php echo $form?>.move_error.checked = <?php echo p4field::isyes($sxml->move_error) ? "true" : "false"?>;
<?php echo $form?>.delfolder.checked = <?php echo p4field::isyes($sxml->delfolder) ? "true" : "false"?>;
<?php echo $form?>.copy_spe.checked = <?php echo p4field::isyes($sxml->copy_spe) ? "true" : "false"?>;
</script>
<?php
return("");
}
else // ... so we NEVER come here
{
// bad xml
return("BAD XML");
}
}
// ====================================================================
// printInterfaceJS() : generer le code js de l'interface 'graphic view'
// ====================================================================
public function printInterfaceJS()
{
global $parm;
?>
<script type="text/javascript">
function chgxmltxt(textinput, fieldname)
{
setDirty();
}
function chgxmlck(checkinput, fieldname)
{
setDirty();
}
function chgxmlpopup(popupinput, fieldname)
{
setDirty();
}
</script>
<?php
}
// ====================================================================
// callback : must return the name graphic form to submit
// if not implemented, assume 'graphicForm'
// ====================================================================
function getGraphicForm()
{
return('graphicForm');
}
// ====================================================================
// printInterfaceHTML(..) : generer l'interface 'graphic view' !! EN UTF-8 !!
// ====================================================================
public function printInterfaceHTML()
{
global $usr_id;
$colls = databox::getColls();
?>
<form name="graphicForm" onsubmit="return(false);">
<?php echo _('task::archive:archivage sur base/collection/')?> :
<select onchange="chgxmlpopup(this, 'base_id');" name="base_id">
<option value="">...</option>
<?php
$base_id = isset($base_id) ? $base_id : null;
foreach($colls as $sbas_id=>$c)
{
foreach($c['colls'] as $bid=>$asciiname)
{
$selected = ($base_id!==null && $base_id==$bid) ? "selected" : "";
print("\t\t\t\t<option value=\"".$bid."\" $selected>" . p4string::MakeString(($c["viewname"]." / ".$asciiname), "form") . "</option>\n");
}
}
?>
</select>
<br/>
<br/>
<?php echo _('task::_common_:hotfolder')?>
<input type="text" name="hotfolder" style="width:400px;" onchange="chgxmltxt(this, 'hotfolder');" value=""><br/>
<br/>
<?php echo _('task::_common_:periodicite de la tache')?>&nbsp;:&nbsp;
<input type="text" name="period" style="width:40px;" onchange="chgxmltxt(this, 'period');" value="">&nbsp;<?php echo _('task::_common_:secondes (unite temporelle)')?><br/>
<br/>
<?php echo _('task::archive:delai de \'repos\' avant traitement')?>&nbsp;:&nbsp;
<input type="text" name="cold" style="width:40px;" onchange="chgxmltxt(this, 'cold');" value="">&nbsp;<?php echo _('task::_common_:secondes (unite temporelle)')?><br/>
<br/>
<input type="checkbox" name="move_archived" onchange="chgxmlck(this, 'move_archived');">&nbsp;<?php echo _('task::archive:deplacer les fichiers archives dans _archived')?>
&nbsp;&nbsp;&nbsp;&nbsp;
<input type="checkbox" name="move_error" onchange="chgxmlck(this, 'move_error');">&nbsp;<?php echo _('task::archive:deplacer les fichiers non-archives dans _error')?><br/>
<br/>
<input type="checkbox" name="copy_spe" onchange="chgxmlck(this, 'copy_spe');">&nbsp;<?php echo _('task::archive:copier les fichiers \'.phrasea.xml\' et \'.grouping.xml\' dans _archived')?><br/>
<br/>
<input type="checkbox" name="delfolder" onchange="chgxmlck(this, 'delfolder');">&nbsp;<?php echo _('task::archive:supprimer les repertoires apres archivage')?><br/>
</form>
<?php
}
// ====================================================================
// $argt : command line args specifics to this task (optional)
// ====================================================================
public $argt = array(
// "--truc" => array("set"=>false, "values"=>array(), "usage"=>" : usage du truc")
);
// ======================================================================================================
// ===== help() : text displayed if --help (optional)
// ======================================================================================================
function help()
{
return(_("task::archive:Archiving files found into a 'hotfolder'"));
}
// ======================================================================================================
// ===== run() : le code d'execution de la teche proprement dite
// ======================================================================================================
private $mimeTypes = array( "jpg"=>"image/jpeg", "jpeg"=>"image/jpeg", "pdf"=>"application/pdf" );
private $hotfiles = array(); // la liste de TOUS les fichiers 'chauds' (en cours d'arrivee ftp ?)
private $sxTaskSettings = null; // les settings de la tache en simplexml
private $connbas = null; // cnx e la base
private $running = false;
private $msg = "";
// copy settings to task, so it's easier to get later
private $move_archived = true; // default value
private $move_error = true; // default value
function run()
{
$this->debug = false;
if( ($this->sxTaskSettings = simplexml_load_string($this->taskSettings)) )
{
}
else
{
// Settings illisibles
return(false);
}
return($this->run2());
}
private function run2()
{
$ret = '';
$server_coll_id = null;
$conn = connection::getInstance();
$base_id = (int)($this->sxTaskSettings->base_id);
$sql = 'SELECT s.sbas_id, b.server_coll_id FROM bas b INNER JOIN sbas s ON s.sbas_id=b.sbas_id WHERE b.base_id="'.$base_id.'"' ;
if($rs = $conn->query($sql))
{
if($row = $conn->fetch_assoc($rs))
{
$this->connbas = connection::getInstance($row['sbas_id']);
$server_coll_id = $row["server_coll_id"];
try
{
$this->connbas->isok() || generalException::get('no db');
// on liste les collections disponibles
$this->TColls = array();
$sql = 'SELECT coll_id FROM coll';
$rsbas = $this->connbas->query($sql);
while($rowbas = $this->connbas->fetch_assoc($rsbas))
$this->TColls['c'.$rowbas['coll_id']] = $rowbas;
$this->connbas->free_result($rsbas);
// on lit les prefs de cette collection
$sql = "SELECT uid AS maxcoll, value AS baseprefs, prefs AS collprefs FROM uids, pref, coll WHERE uids.name='COLL' AND pref.prop='structure' AND coll_id='" . $server_coll_id ."'";
$rsbas = $this->connbas->query($sql);
$rowbas = $this->connbas->fetch_assoc($rsbas);
$this->connbas->free_result($rsbas);
$rowbas || generalException::get('no prefs for collection '.$server_coll_id);
}
catch(Exception $e)
{
print($e->getMessage());
return('tostop');
}
}
$conn->free_result($rs);
}
// ici :
// conn est une connexion e application server du site
// rowtask : record de la teche en cours
// connbas est une connexion e la base distante
// rowbas : record contenant les prefs de base et collection
// on a les prefs de coll directement dans la session
$this->running = true;
$this->tmask = array(); // les masques de fichiers acceptes par cette tache
$this->tmaskgrp = array();
$this->period = 60;
$this->cold = 60;
if( ($this->sxBasePrefs = simplexml_load_string($rowbas["baseprefs"])) )
{
$this->sxBasePrefs["id"] = $base_id; // info utile
$do_it = true;
$this->period = (int)($this->sxTaskSettings->period);
if($this->period <= 0 || $this->period >= 60*60)
$this->period = 60;
$this->cold = (int)($this->sxTaskSettings->cold);
if($this->cold <= 0 || $this->cold >= 60*60)
$this->cold = 60;
// si on doit copier le doc dans la base, on verifie que le dossier existe
$copyhd = true;
$pathhd = (string)($this->sxBasePrefs->path);
if($copyhd && $pathhd)
{
if(!is_dir($pathhd))
p4::fullmkdir($pathhd);
if(!is_dir($pathhd))
{
$this->log(sprintf(_('task::archive:Can\'t create or go to folder \'%s\''), $pathhd));
$this->running = false;
}
}
// on charge les masques de fichiers
if($this->sxTaskSettings->files && $this->sxTaskSettings->files->file)
{
foreach($this->sxTaskSettings->files->file as $ft)
$this->tmask[] = array("mask"=>(string)($ft["mask"]), "caption"=>(string)($ft["caption"]), "accept"=>(string)($ft["accept"]));
}
if($this->sxTaskSettings->files && $this->sxTaskSettings->files->grouping)
{
foreach($this->sxTaskSettings->files->grouping as $ft)
$this->tmaskgrp[] = array("mask"=>(string)($ft["mask"]), "caption"=>(string)($ft["caption"]),"representation"=>(string)($ft["representation"]), "accept"=>(string)($ft["accept"]));
}
if(count($this->tmask)==0)
{
// pas de masque defini : on accepte tout
$this->tmask[] = array("mask"=>".*", "caption"=>"", "accept"=>"");
}
// ici la teche tourne tant qu'elle est active
$loop = 0;
while($this->running)
{
if(!$conn || !$conn->ping())
{
$this->log(("Warning : abox connection lost, restarting in 10 min."));
sleep(60*10);
$this->running = false;
return('');
}
if(!$this->connbas->ping())
{
$this->log(("dbox connection lost, restarting in 10 min."));
sleep(60*10);
$this->running = false;
return('');
}
$path_in = (string)($this->sxTaskSettings->hotfolder);
if(!@is_dir( $path_in ))
{
$this->log(sprintf(('missing hotfolder \'%s\', restarting in 10 min.'), $path_in));
sleep(60*10);
$this->running = false;
return('');
}
// printf("====================== LOOP ====================== \n");
$sql = "UPDATE task2 SET last_exec_time=NOW() WHERE task_id=" . $this->taskid ;
$conn->query($sql);
$sql = "SELECT * FROM task2 WHERE task_id=" . $this->taskid ;
if($rs = $conn->query($sql))
{
$row = $conn->fetch_assoc($rs);
$conn->free_result($rs);
if($row)
{
if($row['status'] == 'tostop')
{
$ret = 'stopped';
$this->running = false;
}
else
{
if( $this->sxTaskSettings = simplexml_load_string($row['settings']) )
{
// copy settings to task, so it's easier to get later
$this->move_archived = p4field::isyes($this->sxTaskSettings->move_archived);
$this->move_error = p4field::isyes($this->sxTaskSettings->move_error);
$period = (int)($this->sxTaskSettings->period);
if($period <= 0 || $period >= 60*60)
$period = 60;
$cold = (int)($this->sxTaskSettings->cold);
if($cold <= 0 || $cold >= 60*60)
$cold = 60;
}
else
{
$period = 60;
$cold = 60;
}
$duration = time();
$r = $this->archiveHotFolder($server_coll_id);
if($loop > 10)
$r = 'MAXLOOP';
switch($r)
{
case 'WAIT':
$ret = 'stopped';
$this->running = false;
break;
case 'BAD':
$ret = 'stopped';
$this->running = false;
break;
case 'NORECSTODO':
$duration = time() - $duration;
if($duration < ($period+$cold))
{
$conn->close();
sleep(($period+$cold) - $duration);
unset($conn);
$conn = connection::getInstance();
}
break;
case 'MAXRECSDONE':
case 'MAXMEMORY':
case 'LOOP':
case 'MAXLOOP':
if($row['status'] == 'started')
{
$ret = 'torestart';
$this->running = false;
}
break;
default:
if($row['status'] == 'started')
{
$ret = 'stopped';
$this->running = false;
}
break;
}
}
}
else
{
$this->running = false;
}
}
else
{
$this->running = false;
}
$loop++;
}
}
return($ret);
}
function archiveHotFolder($server_coll_id)
{
// $this->filesToIgnore = array('.', '..', 'Thumbs.db', '');
clearstatcache();
$conn = connection::getInstance();
$path_in = p4string::delEndSlash(trim((string)($this->sxTaskSettings->hotfolder)));
if(!@is_file($path_in . "/.phrasea.xml"))
{
$this->log(sprintf(('NO .phrasea.xml AT ROOT v2 \'%s\' !'), $path_in));
return('WAIT');
}
$path_archived = $path_error = null;
if($this->move_archived)
{
$path_archived = $path_in . '_archived';
@mkdir($path_archived, 0755, true);
if(!file_exists($path_archived))
{
$this->log(sprintf(('Can\'t create folder \'%s\' !'), $path_archived));
return('BAD');
}
}
if($this->move_error)
{
$path_error = $path_in . '_error';
@mkdir($path_error, 0755, true);
if(!file_exists($path_error))
{
$this->log(sprintf(('archive:Can\'t create folder \'%s\' !'), $path_error));
return('BAD');
}
}
$dom = new DOMDocument();
$dom->formatOutput = true;
$root = $dom->appendChild($dom->createElement('root'));
$this->movedFiles = 0;
$this->archivedFiles = 0;
$this->path_in = $path_in;
$nnew = $this->listFilesPhase1($dom, $root, $path_in, $server_coll_id);
if($this->debug)
$this->log("=========== listFilesPhase1 ========== (returned ".$nnew.")\n" . $dom->saveXML());
$cold = (int)($this->sxTaskSettings->cold);
if($cold <= 0 || $cold >= 60*60)
$cold = 60;
$sql = "SELECT status FROM task2 WHERE task_id=" . $this->taskid ;
while($cold > 0)
{
if($rs = $conn->query($sql))
{
$row = $conn->fetch_assoc($rs);
$conn->free_result($rs);
if($row && $row['status'] == 'tostop')
return('NORECSTODO');
}
sleep(2);
$cold -= 2;
}
$this->listFilesPhase2($dom, $root, $path_in);
if($this->debug)
$this->log("=========== listFilesPhase2 ========== : \n" . $dom->saveXML());
$this->makePairs($dom, $root, $path_in, $path_archived, $path_error);
if($this->debug)
$this->log("=========== makePairs ========== : \n" . $dom->saveXML());
$r = $this->removeBadGroups($dom, $root, $path_in, $path_archived, $path_error);
if($this->debug)
$this->log("=========== removeBadGroups ========== (returned ".($r?'true':'false').") : \n" . $dom->saveXML());
$this->archive($dom, $root, $path_in, $path_archived, $path_error);
if($this->debug)
$this->log("=========== archive ========== : \n" . $dom->saveXML());
$this->bubbleResults($dom, $root, $path_in);
if($this->debug)
$this->log("=========== bubbleResults ========== : \n" . $dom->saveXML());
$r = $this->moveFiles($dom, $root, $path_in, $path_archived, $path_error);
if($this->debug)
$this->log("=========== moveFiles ========== (returned ".($r?'true':'false').") : \n" . $dom->saveXML());
if($this->movedFiles)
{
// something happened : a least one file has moved
return('MAXRECSDONE');
}
elseif(memory_get_usage()>>20 > 15)
{
return('MAXMEMORY');
}
else
{
return('NORECSTODO');
}
//print($dom->saveXML());
// unset($dom);
// die();
}
function isIgnoredFile($f)
{
return($f=='.' || $f=='..' || $f=='Thumbs.db' || $f=='par-SYSTEM' || substr($f, 0, 10)=='.com.apple');
// return(substr($f,0,1)=='.' || $f=='Thumbs.db' || $f=='par-SYSTEM');
}
/**
* check if the file matches any mask, and flag the 'caption' file if found
*
* @param unknown_type $dom
* @param unknown_type $node
* @param unknown_type $path
* @param unknown_type $grp_rid
*/
function checkMatch($dom, $node)
{
$file = $node->getAttribute('name');
foreach($this->tmask as $mask)
{
$preg_mask = '/' . $mask['mask'] . '/';
if(preg_match($preg_mask, $file))
{
if($mask['caption'])
{
// caption in a linked file ?
$captionFileName = @preg_replace($preg_mask, $mask['caption'], $file);
$xpath = new DOMXPath($dom);
$dnl = $xpath->query('./file[@name="'.$captionFileName.'"]', $node->parentNode);
if($dnl->length == 1)
{
// the caption file exists
$node->setAttribute('match', $captionFileName);
$dnl->item(0)->setAttribute('match', '*');
}
else
{
// the caption file is missing
$node->setAttribute('match', '?');
}
}
else
{
// self-described
$node->setAttribute('match', '.');
}
// first match is ok
break;
}
}
}
/**
* Phase 1 :
* list every file, all 'hot'
* read .phrasea.xml files
*
* @param unknown_type $dom
* @param unknown_type $node
* @param unknown_type $path
* @param unknown_type $server_coll_id
*/
function listFilesPhase1($dom, $node, $path, $server_coll_id)
{
// $this->traceRam();
$nnew = 0;
try
{
$listFolder = new CListFolder($path, GV_ArchiveSorted);
if( ($sxDotPhrasea = @simplexml_load_file($path.'/.phrasea.xml')) )
{
// on gere le magicfile
if( ($magicfile = trim((string)($sxDotPhrasea->magicfile))) != '')
{
$magicmethod = strtoupper($sxDotPhrasea->magicfile['method']);
if($magicmethod=='LOCK' && file_exists($path.'/'.$magicfile))
return;
elseif($magicmethod=='UNLOCK' && !file_exists($path.'/'.$magicfile))
return;
}
// on gere le changement de collection
if( ($new_cid = $sxDotPhrasea['collection']) != '' )
{
//printf("new_cid=%s\n", $new_cid);
//var_dump($this->TColls);
if(isset($this->TColls['c'.$new_cid]))
{
$server_coll_id = $new_cid;
}
else
{
$this->log(sprintf(('Unknown coll_id (%1$d) in "%2$s"'), (int)$new_cid, $path.'/.phrasea.xml'));
$server_coll_id = -1;
}
}
$node->setAttribute('pxml', '1');
}
$iloop = 0;
while( ($file = $listFolder->read()) !== NULL )
{
if($this->isIgnoredFile($file))
continue;
if( ($iloop++ % 100) == 0)
usleep(1000);
if(is_dir($path.'/'.$file))
{
$n = $node->appendChild($dom->createElement('file'));
$n->setAttribute('isdir', '1');
$n->setAttribute('name', $file);
$nnew += $this->listFilesPhase1($dom, $n, $path.'/'.$file, $server_coll_id);
}
else
{
$n = $node->appendChild($dom->createElement('file'));
$n->setAttribute('name', $file);
$stat = stat($path.'/'.$file);
foreach(array("size", "ctime", "mtime") as $k)
$n->setAttribute($k, $stat[$k]);
$nnew++;
}
$n->setAttribute('cid', $server_coll_id);
$n->setAttribute('temperature', 'hot');
}
}
catch(Exception $e)
{
}
return($nnew);
}
/**
* Phase 2 :
* list again and flag dead files as 'cold'
*
* @param unknown_type $dom
* @param unknown_type $node
* @param unknown_type $path
*/
function listFilesPhase2($dom, $node, $path, $depth=0)
{
static $iloop = 0;
if($depth == 0)
$iloop = 0;
$nnew = 0;
try
{
$listFolder = new CListFolder($path, GV_ArchiveSorted);
$xp = new DOMXPath($dom);
while( ($file = $listFolder->read()) !== NULL )
{
if($this->isIgnoredFile($file))
continue;
if( ($iloop++ % 100) == 0)
usleep(500);
$dnl = @$xp->query('./file[@name="'.$file.'"]', $node);
// if(!$dnl)
// printf("%s \n", './file[@name="'.$file.'"]');
if($dnl && $dnl->length == 0)
{
if(is_dir($path.'/'.$file))
{
$n = $node->appendChild($dom->createElement('file'));
$n->setAttribute('isdir', '1');
$n->setAttribute('name', $file);
$nnew += $this->listFilesPhase2($dom, $n, $path.'/'.$file, $depth+1);
}
else
{
$n = $node->appendChild($dom->createElement('file'));
$n->setAttribute('name', $file);
// $stat = stat($path.'/'.$file);
// foreach(array("size", "ctime", "mtime") as $k)
// $n->setAttribute($k, $stat[$k]);
$nnew++;
}
// $n->setAttribute('cid', $server_coll_id);
// $n->setAttribute('temperature', 'hot');
$this->setBranchHot($dom, $n);
}
elseif($dnl && $dnl->length == 1)
{
$dnl->item(0)->setAttribute('temperature', 'cold');
// $dnl->item(0)->removeAttribute('hot');
if(is_dir($path.'/'.$file))
{
$this->listFilesPhase2($dom, $dnl->item(0), $path.'/'.$file, $depth+1);
}
else
{
$stat = stat($path.'/'.$file);
foreach(array("size", "ctime", "mtime") as $k)
{
if($dnl->item(0)->getAttribute($k) != $stat[$k])
{
$this->setBranchHot($dom, $dnl->item(0));
break;
}
}
}
}
}
}
catch(Exception $e)
{
}
}
/**
* makePairs :
* flag files to be archived (make pairs with linked caption when needed)
* flag grp folders and their linked files (caption, representation)
* declare uncomplete grp as error
*
* @param unknown_type $dom
* @param unknown_type $node
* @param unknown_type $path
*/
function makePairs($dom, $node, $path, $path_archived, $path_error, $inGrp=false, $depth=0)
{
static $iloop = 0;
if($depth == 0)
$iloop = 0;
if($depth==0 && ($node->getAttribute('temperature')=='hot' || $node->getAttribute('cid')=='-1'))
return;
$xpath = new DOMXPath($dom); // useful
for($n=$node->firstChild; $n; $n=$n->nextSibling)
{
if( ($iloop++ % 100) == 0)
usleep(1000);
// make xml lighter (free ram)
foreach(array("size", "ctime", "mtime") as $k)
$n->removeAttribute($k);
if($n->getAttribute('temperature')=='hot' || $n->getAttribute('cid')=='-1')
continue;
$name = $n->getAttribute('name');
if($n->getAttribute('isdir')=='1')
{
if( ($grpSettings = $this->getGrpSettings($name)) !== FALSE) // get 'caption', 'representation'
{
// this is a grp folder, we check it
$dnl = $xpath->query('./file[@name=".grouping.xml"]', $n);
if($dnl->length == 1)
{
// this group is old (don't care about any linked files), just flag it
$n->setAttribute('grp', 'tocomplete');
$dnl->item(0)->setAttribute('match', '*');
// recurse only if group is ok
$this->makePairs($dom, $n, $path.'/'.$name, true, $depth+1);
}
else
{
// this group in new (to be created)
// do we need one (or both) linked file ? (caption or representation)
$err = false;
$flink = array('caption'=>null, 'representation'=>null);
foreach($flink as $linkName=>$v)
{
if(isset($grpSettings[$linkName]) && $grpSettings[$linkName]!='')
{
// we need this linked file, calc his real name
$f = preg_replace('/'.$grpSettings['mask'].'/i', $grpSettings[$linkName], $name);
$dnl = $xpath->query('./file[@name="'.$f.'"]', $node);
if($dnl->length == 1)
{
$flink[$linkName] = $dnl->item(0); // it's here
}
else
{
$this->log(sprintf(('missing linked file \'%1$s\' to group \'%2$s\''), $f, $name));
$err = true; // missing -> error
}
}
}
if(!$err)
{
// the group is ok, flag it ...
$n->setAttribute('grp', 'tocreate');
// ... as the existing linked file(s) ...
foreach($flink as $linkName=>$v)
{
if($v) // this linked file exists
{
// $v->setAttribute('grp', '1');
$v->setAttribute('match', '*');
$n->setAttribute('grp_'.$linkName, $v->getAttribute('name'));
}
}
// recurse only if group is ok
$this->makePairs($dom, $n, $path.'/'.$name
, $path_archived.'/'.$name
, $path_error.'/'.$name
, true, $depth+1);
}
else
{
// something is missing, the whole group goes error, ...
$n->setAttribute('grp', 'todelete');
$this->setAllChildren($dom, $n, array('error'=>'1'));
// bubble to the top
for($nn=$n; $nn && $nn->nodeType==XML_ELEMENT_NODE; $nn=$nn->parentNode)
$nn->setAttribute('error', '1');
// ... as the existing linked file(s) ...
foreach($flink as $linkName=>$v)
{
if($v) // this linked file exists, it goes error also
$v->setAttribute('error', '1');
}
}
}
}
else
{
// not a grp folder, recurse
$this->makePairs($dom, $n, $path.'/'.$name
, $path_archived.'/'.$name
, $path_error.'/'.$name
, $inGrp, $depth+1);
}
}
else
{
// this is a file
if(!$n->getAttribute('match')) // because match can be set before
{
if($name == '.phrasea.xml')
$n->setAttribute('match', '*'); // special file(s) always ok
else
$this->checkMatch($dom, $n);
}
}
}
// scan again for unmatched files
for($n=$node->firstChild; $n; $n=$n->nextSibling)
{
if(!$n->getAttribute('isdir')=='1' && !$n->getAttribute('match'))
{
// still no match, now it's an error (bubble to the top)
for($nn=$n; $nn && $nn->nodeType==XML_ELEMENT_NODE; $nn=$nn->parentNode)
$nn->setAttribute('error', '1');
}
}
}
/**
* removeBadGroups :
* move files to archived or error dir
*
* @param unknown_type $dom
* @param unknown_type $node
* @param unknown_type $path
* @param unknown_type $path_archived
* @param unknown_type $path_error
*/
function removeBadGroups($dom, $node, $path, $path_archived, $path_error, $depth=0)
{
static $iloop=0;
if($depth == 0)
$iloop = 0;
$ret = false;
if($depth==0 && $node->getAttribute('temperature')=='hot') // if root of hotfolder if hot, die...
return($ret);
//printf("%s : \n", __LINE__);
$nodesToDel = array();
for($n=$node->firstChild; $n; $n=$n->nextSibling)
{
if( ($iloop++ % 20) == 0)
usleep(1000);
if($n->getAttribute('temperature')=='hot') // do not move hotfiles
continue;
//printf("%s : \n", __LINE__);
$name = $n->getAttribute('name');
//printf("----------------\n file %s : \n ", $path.'/'.$name);
//$fp = fopen('php://stdin', 'r');
//fgets($fp);
//fclose($fp);
if($n->getAttribute('isdir'))
{
// a dir
// if($n->getAttribute('error') && $this->move_error)
// {
// @mkdir($path_error . '/' . $name);
// }
$ret |= $this->removeBadGroups($dom, $n, $path.'/'.$name
, $path_archived.'/'.$name
, $path_error.'/'.$name
, $depth+1);
if($n->getAttribute('grp')=='todelete')
{
$nodesToDel[] = $n;
@unlink($path.'/'.$name);
}
}
else
{
// a file
if($n->getAttribute('error'))
{
// printf("%s : \n", __LINE__);
if($this->move_error)
{
$rootpath = p4string::delEndSlash(trim((string)($this->sxTaskSettings->hotfolder)));
$subpath = substr($path, strlen($rootpath));
$this->log(sprintf(('copy \'%s\' to \'error\''), $subpath.'/'.$name));
@mkdir($path_error, 0755, true);
@copy($path.'/'.$name, $path_error.'/'.$name);
}
$nodesToDel[] = $n;
@unlink($path.'/'.$name);
$this->movedFiles++;
}
}
}
foreach($nodesToDel as $n)
$n->parentNode->removeChild($n);
}
/**
* archive :
* archive files
* do special work on grp folders
*
* @param unknown_type $dom
* @param unknown_type $node
* @param unknown_type $path
*/
function archive($dom, $node, $path, $path_archived, $path_error, $depth=0)
{
static $iloop = 0;
if($depth == 0)
$iloop = 0;
if($node->getAttribute('temperature')=='hot')
return;
$nodesToDel = array();
for($n=$node->firstChild; $n; $n=$n->nextSibling)
{
if( ($iloop++ % 20) == 0)
usleep(1000);
if($n->getAttribute('temperature')=='hot')
continue;
if($n->getAttribute('cid')=='-1')
{
$n->setAttribute('error', '1');
continue;
}
if($n->getAttribute('isdir')=='1')
{
if($n->getAttribute('grp'))
{
// a grp folder : special work
$this->ArchiveGrp($dom, $n, $path, $path_archived, $path_error, $nodesToDel);
}
else
{
// ...normal subfolder : recurse
$name = $n->getAttribute('name');
$this->archive($dom, $n, $path.'/'.$name
, $path_archived.'/'.$name
, $path_error.'/'.$name
, $depth+1);
}
}
else
{
// a file
$this->archiveFile($dom, $n, $path, $path_archived, $path_error, $nodesToDel, 0); // 0 = no grp
}
}
// printf("========== %s === %s ====== \n", __LINE__, var_export($nodesToDel, true));
foreach($nodesToDel as $n)
$n->parentNode->removeChild($n);
// at the end of recursion, restore the magic file (create or delete it)
if( ($magicfile = $node->getAttribute('magicfile')) != '')
{
$magicmethod = $node->getAttribute('magicmethod');
if($magicmethod=='LOCK')
file_put_contents($path.'/'.$magicfile, '');
elseif($magicmethod=='UNLOCK')
unlink($path.'/'.$magicfile);
}
}
/**
* bubbleResults :
* bubble result attributes ('keep', 'archived', 'error') up to top,
* to help creation of result folders and cleaning of the hotfolder
*
* @param unknown_type $dom
* @param unknown_type $node
* @param unknown_type $path
* @return unknown
*/
function bubbleResults($dom, $node, $path, $depth=0)
{
static $iloop=0;
if($depth == 0)
$iloop = 0;
if($node->getAttribute('temperature')=='hot')
return;
$ret = 0;
for($n=$node->firstChild; $n; $n=$n->nextSibling)
{
if( ($iloop++ % 20) == 0)
usleep(1000);
if($n->getAttribute('name') == '.phrasea.xml' || $n->getAttribute('name') == '.grouping.xml')
{
// special files stay in place AND are copied into 'archived'
$n->setAttribute('keep', '1');
if(p4field::isyes($this->sxTaskSettings->copy_spe))
$n->setAttribute('archived', '1');
}
// else
// {
// if(!$n->getAttribute('isdir') && ($n->getAttribute('temperature') == 'cold' && !$n->getAttribute('archived')))
// {
// $n->setAttribute('error', '1');
// }
// }
if($n->getAttribute('keep') == '1')
$ret |= 1;
if($n->getAttribute('archived') == '1')
$ret |= 2;
if($n->getAttribute('error') == '1')
$ret |= 4;
if($n->getAttribute('isdir') == '1')
$ret |= $this->bubbleResults($dom, $n, $path.'/'.$n->getAttribute('name'), $depth+1);
}
if($ret & 1)
$node->setAttribute('keep', '1');
if($ret & 2)
$node->setAttribute('archived', '1');
if($ret & 4)
$node->setAttribute('error', '1');
return($ret);
}
/**
* Phase 5 :
* move files to archived or error dir
*
* @param unknown_type $dom
* @param unknown_type $node
* @param unknown_type $path
* @param unknown_type $path_archived
* @param unknown_type $path_error
*/
function moveFiles($dom, $node, $path, $path_archived, $path_error, $depth=0)
{
static $iloop=0;
if($depth == 0)
$iloop = 0;
$ret = false;
if($depth==0 && $node->getAttribute('temperature')=='hot') // if root of hotfolder if hot, die...
return($ret);
//printf("%s : \n", __LINE__);
$nodesToDel = array();
for($n=$node->firstChild; $n; $n=$n->nextSibling)
{
if( ($iloop++ % 20) == 0)
usleep(1000);
if($n->getAttribute('temperature')=='hot') // do not move hotfiles
continue;
//printf("%s : \n", __LINE__);
$name = $n->getAttribute('name');
//printf("----------------\n file %s : \n ", $path.'/'.$name);
//$fp = fopen('php://stdin', 'r');
//fgets($fp);
//fclose($fp);
if($n->getAttribute('isdir'))
{
// printf("%s : ('%s', '%s')\n", __LINE__, $path_archived, $path_error);
// $new_path_archived = $new_path_error = null;
// if($n->getAttribute('archived') && $this->move_archived)
// {
// @mkdir($new_path_archived = ($path_archived . '/' . $name));
// if(!is_dir($new_path_archived))
// {
// $rootpath = p4string::delEndSlash(trim((string)($this->sxTaskSettings->hotfolder)));
// $subpath = substr($new_path_archived, strlen($rootpath));
//
// $this->log("error subfolder '".$subpath."' does not exits");
// }
// }
//
// if($n->getAttribute('error') && $this->move_error)
// {
// @mkdir($new_path_error = ($path_error . '/' . $name));
// if(!is_dir($new_path_error))
// {
// $rootpath = p4string::delEndSlash(trim((string)($this->sxTaskSettings->hotfolder)));
// $subpath = substr($new_path_error, strlen($rootpath));
//
// $this->log("error subfolder '".$subpath."' does not exists");
// }
// }
$ret |= $this->moveFiles($dom, $n, $path.'/'.$name
, $path_archived.'/'.$name
, $path_error.'/'.$name
, $depth+1);
if(!$n->firstChild)
$nodesToDel[] = $n;
// ----- JY 20100318 : DO NOT DELETE EMPTY FOLDERS ANYMORE, AS THEY MAY DISAPEAR TOO SOON -----
// if(!$n->getAttribute('keep'))
// @rmdir($path.'/'.$name);
// --------------------------------------------------------------------------------------------
}
else
{
//printf("%s : \n", __LINE__);
$rootpath = p4string::delEndSlash(trim((string)($this->sxTaskSettings->hotfolder)));
$subpath = substr($path, strlen($rootpath));
if($n->getAttribute('archived') && $this->move_archived)
{
$this->log(sprintf(('copy \'%s\' to \'archived\''), $subpath.'/'.$name));
@mkdir($path_archived, 0755, true);
@copy($path.'/'.$name, $path_archived.'/'.$name);
if(!$n->getAttribute('keep')) // do not count copy of special files as a real event
{
$nodesToDel[] = $n;
$ret = true;
}
}
if($n->getAttribute('error') && $this->move_error)
{
// printf("%s : \n", __LINE__);
$this->log(sprintf(('copy \'%s\' to \'error\''), $subpath.'/'.$name));
@mkdir($path_error, 0755, true);
@copy($path.'/'.$name, $path_error.'/'.$name);
if(!$n->getAttribute('keep')) // do not count copy of special files as a real event
{
$nodesToDel[] = $n;
$ret = true;
}
//printf("-> copy to %s \n", $path_error.'/'.$name);
}
if(!$n->getAttribute('keep'))
{
//printf("-> unlink %s \n", $path.'/'.$name);
$this->log(sprintf(('delete \'%s\''), $subpath.'/'.$name));
if(@unlink($path.'/'.$name))
{
// $n->parentNode->removeChild($n);
$this->movedFiles++;
}
else
{
$this->log(sprintf(("Warning : can't delete '%s' from hotfolder, task is stopped"), $subpath.'/'.$name));
}
}
}
}
foreach($nodesToDel as $n)
$n->parentNode->removeChild($n);
return($ret);
}
function setBranchHot($dom, $node)
{
for($n=$node; $n; $n=$n->parentNode)
{
if($n->nodeType == XML_ELEMENT_NODE)
{
$n->setAttribute('temperature', 'hot');
if($n->hasAttribute('pxml'))
break;
}
}
}
/**
* special work for a grp folder :
* create the grp if needed
* archive files
*
* @param unknown_type $dom
* @param unknown_type $node
* @param unknown_type $parentPath
*/
function archiveGrp($dom, $node, $path, $path_archived, $path_error, &$nodesToDel)
{
$xpath = new DOMXPath($dom); // useful
$node->setAttribute('keep', '1'); // grp folders stay in place
$grpFolder = $node->getAttribute('name');
$groupingFile = $path.'/'.$grpFolder.'/.grouping.xml';
if($node->getAttribute('grp') == 'tocreate')
{
$representationFileName = null;
$representationFileNode = null;
$captionFileName = null;
$captionFileNode = null;
$cid = $node->getAttribute('cid');
$genericdoc = null;
$rootpath = p4string::delEndSlash(trim((string)($this->sxTaskSettings->hotfolder)));
$subpath = substr($path, strlen($rootpath));
$this->log(sprintf(('created story \'%s\''), $subpath.'/'.$grpFolder));
// if the .grp does not have a representative doc, let's use a generic file
if(!($rep = $node->getAttribute('grp_representation')))
{
copy(p4string::addEndSlash(GV_RootPath).'www/skins/icons/substitution/regroup_doc.png', $genericdoc=($path.'/group.jpg'));
$representationFileName = 'group.jpg';
$this->log((' (no representation file)'));
}
else
{
$dnl = $xpath->query('./file[@name="'.$rep.'"]', $node->parentNode);
$representationFileNode = $dnl->item(0);
$representationFileName = $rep;
$node->removeAttribute('grp_representation');
$this->log(sprintf(('representation from \'%s\''), $representationFileName));
}
if( ($cap = $node->getAttribute('grp_caption')) )
{
$dnl = $xpath->query('./file[@name="'.$cap.'"]', $node->parentNode);
$captionFileNode = $dnl->item(0);
$captionFileName = $cap;
$node->removeAttribute('grp_caption');
$this->log(sprintf(('caption from \'%s\''), $captionFileName));
}
$rid = $this->connbas->getId("RECORD", 1);
$mimeExt = giveMimeExt($path.'/'.$representationFileName);
$propfile = array(
'recordid' => $rid,
'extension' => $mimeExt['ext'],
'mime' => $mimeExt['mime'],
'size' => sprintf('%u', filesize($path.'/'.$representationFileName)),
'subpath' => $subpath,
'parentdirectory' => '',
'hotfolderfile' => $path.'/'.$representationFileName,
'hotfoldercaptionfile' => $path.'/'.$captionFileName,
'pathhd' => '',
'file' => '',
'originalname' => $representationFileName,
'inbase' => 0,
'type' => giveMeDocType($mimeExt['mime'])
);
$pi = pathinfo($subpath);
$propfile['parentdirectory'] = $pi["basename"];
$meta = get_xml($this->sxBasePrefs, $propfile);
$stat0 = $stat1= "0";
if($this->sxBasePrefs->status)
$stat0 = (string)($this->sxBasePrefs->status);
if($this->sxTaskSettings->status)
$stat1 = (string)($this->sxTaskSettings->status);
if(!$stat0)
$stat0 = '0';
if(!$stat1)
$stat1 = '0';
// ... flag the record as 'to reindex' (status-bits 0 and 1 to 0) AND UNLOCK IT (status-bit 2 to 1)
// ... and flag it for subdef creation (jeton bit 0 to 1)
$fl = 'coll_id, record_id, parent_record_id, status, jeton, moddate, credate, xml, type, sha256';
$vl = $cid . ', ' .$rid . ', ' .$rid . ', (((' . $stat0 . ' | ' . $stat1 . ') & ~0x0F) | 0x0C), '.JETON_MAKE_SUBDEF.', NOW(), NOW(), \'' . $this->connbas->escape_string($meta['xml']->saveXML()) . '\', \'' . $this->connbas->escape_string($propfile['type']) . '\', \''.hash_file('sha256',$propfile["hotfolderfile"]).'\'';
$sql = 'INSERT INTO record ('.$fl.') VALUES ('.$vl.')';
$this->connbas->query($sql);
$copyhd = true;
$documentisok = false;
$this->log(sprintf((' (recordId %s)'), $rid));
if($copyhd)
{
// archiving with copy into base
$pathhd = trim((string)($this->sxBasePrefs->path));
$pathhd = p4string::addEndSlash(p4::dispatch($pathhd));
if(!is_dir($pathhd))
p4::fullmkdir($pathhd);
$newname = $rid . "_document.".$propfile["extension"];
if(copy($propfile["hotfolderfile"], $pathhd . $newname))
{
$propfile['pathhd'] = $pathhd;
$propfile['file'] = $newname;
$propfile['inbase'] = 1;
$fl = 'record_id'; $vl = $rid;
$fl .= ', name'; $vl .= ", 'document'";
$fl .= ', path'; $vl .= ", '" . $this->connbas->escape_string($propfile['pathhd']) . "'";
$fl .= ', file'; $vl .= ", '" . $this->connbas->escape_string($propfile['file']) . "'";
$fl .= ', baseurl'; $vl .= ", ''";
$fl .= ', inbase'; $vl .= ", '" . $propfile['inbase'] . "'";
if(isset($propfile['width']))
{
$fl .= ', width'; $vl .= ", '" . $this->connbas->escape_string($propfile['width']) . "'";
}
if(isset($propfile['height']))
{
$fl .= ', height'; $vl .= ", '" . $this->connbas->escape_string($propfile['height']) . "'";
}
$fl .= ', mime'; $vl .= ", '" . $this->connbas->escape_string($propfile['mime']) . "'";
$fl .= ', size'; $vl .= ", '" . $this->connbas->escape_string($propfile['size']) . "'";
$fl .= ', dispatched'; $vl .= ", '1'";
$sql = "INSERT INTO subdef (".$fl.") VALUES (".$vl.")";
$this->connbas->query($sql);
}
$this->archivedFiles++;
}
else
{
// archiving 'in place'
}
if($genericdoc)
unlink($genericdoc);
// ok here
// , create a .grouping.xml in the hotfolder
file_put_contents($groupingFile, '<?xml version="1.0" encoding="ISO-8859-1" ?><record grouping="'.$rid.'" />');
$n = $node->appendChild($dom->createElement('file'));
$n->setAttribute('name', '.grouping.xml');
$n->setAttribute('temperature', 'cold');
$n->setAttribute('grp', '1');
// $n->setAttribute('archived', '1');
$n->setAttribute('match', '*');
if($this->move_archived)
{
$this->log(sprintf(('copy \'%s\' to \'archived\''), $subpath.'/'.$grpFolder.'/.grouping.xml'));
@mkdir($path_archived.'/'.$grpFolder, 0755, true);
@copy($path.'/'.$grpFolder.'/.grouping.xml', $path_archived.'/'.$grpFolder.'/.grouping.xml');
}
//
if($captionFileNode)
{
$captionFileNode->setAttribute('archived', '1');
if($this->move_archived)
{
$this->log(sprintf(('copy \'%s\' to \'archived\''), $subpath.'/'.$captionFileName));
if(!is_dir($path_archived))
@mkdir($path_archived, 0755, true);
@copy($path.'/'.$captionFileName, $path_archived.'/'.$captionFileName);
}
@unlink($path.'/'.$captionFileName);
$nodesToDel[] = $captionFileNode;
$this->movedFiles++;
}
if($representationFileNode)
{
$representationFileNode->setAttribute('archived', '1');
if($this->move_archived)
{
$this->log(sprintf(('copy \'%s\' to \'archived\''), $subpath.'/'.$representationFileName));
if(!is_dir($path_archived))
@mkdir($path_archived, 0755, true);
@copy($path.'/'.$representationFileName, $path_archived.'/'.$representationFileName);
}
@unlink($path.'/'.$representationFileName);
$nodesToDel[] = $representationFileNode;
$this->movedFiles++;
}
//
$node->setAttribute('grp', 'tocomplete');
}
// here the .grouping.xml should exists
if(file_exists($groupingFile))
{
// a .grouping.xml must stay in place
// -- don't do, done in phase4
//$xpath = new DOMXPath($dom);
//$dnl = $xpath->query('./file[@name=".grouping.xml"]', $node);
//if($dnl->length == 1)
// $dnl->item(0)->setAttribute('keep', '1');
$sxGrouping = simplexml_load_file($groupingFile);
$grp_rid = $sxGrouping['grouping'];
$this->archiveFilesToGrp($dom, $node, $path.'/'.$grpFolder
, $path_archived.'/'.$grpFolder
, $path_error.'/'.$grpFolder
, $grp_rid);
}
}
/**
* archive every files inside a grp
*
* @param unknown_type $dom
* @param unknown_type $node
* @param unknown_type $path
* @param unknown_type $grp_rid
*/
function archiveFilesToGrp($dom, $node, $path, $path_archived, $path_error, $grp_rid)
{
//usleep(1000);
$nodesToDel = array();
for($n=$node->firstChild; $n; $n=$n->nextSibling)
{
if($n->getAttribute('isdir')=='1')
{
// in a grp, all levels goes in the same grp
$node->setAttribute('archived', '1'); // the main grp folder is 'keep'ed, but not subfolders
$this->archiveFilesToGrp($dom, $n, $path.'/'.$n->getAttribute('name')
, $path_archived.'/'.$n->getAttribute('name')
, $path_error.'/'.$n->getAttribute('name')
, $grp_rid);
}
else
{
// a file
$this->archiveFile($dom, $n, $path, $path_archived, $path_error, $nodesToDel, $grp_rid);
}
}
// printf("======== %s %s \n", __LINE__, var_export($nodesToDel, true));
foreach($nodesToDel as $n)
$n->parentNode->removeChild($n);
}
/**
* ARCHIVE FILE !
*
* @param unknown_type $dom
* @param unknown_type $node
* @param unknown_type $path
* @param unknown_type $grp_rid
*/
function archiveFile($dom, $node, $path, $path_archived, $path_error, &$nodesToDel, $grp_rid=0)
{
$match = $node->getAttribute('match');
if($match == '*')
return;
$file = $node->getAttribute('name');
$cid = $node->getAttribute('cid');
$captionFileNode = null;
$rootpath = p4string::delEndSlash(trim((string)($this->sxTaskSettings->hotfolder)));
$subpath = substr($path, strlen($rootpath));
if(!$match)
{
// the file does not match on any mask
$this->log(sprintf(("File '%s' does not match any mask"), $subpath.'/'.$file));
$node->setAttribute('error', '1');
return;
}
elseif($match == '?')
{
// the caption file is missing
$this->log(sprintf(("Caption of file '%s' is missing"), $subpath.'/'.$file));
$node->setAttribute('error', '1');
return;
}
elseif( ($match != '.') ) // match='.' : the file does not have a separate caption
{
$xpath = new DOMXPath($dom);
$dnl = $xpath->query('./file[@name="'.$match.'"]', $node->parentNode);
// in fact, xquery has been done in checkMatch, setting match='?' if caption does not exists...
if($dnl->length == 1)
{
// ...so we ALWAYS come here
$captionFileNode = $dnl->item(0);
}
else
{
// ...so we should NEVER come here
$node->setAttribute('error', '1');
return;
}
}
$this->archiveFileAndCaption($dom, $node, $captionFileNode, $path, $path_archived, $path_error, $grp_rid, $nodesToDel);
}
function archiveFileAndCaption($dom, $node, $captionFileNode, $path, $path_archived, $path_error, $grp_rid, &$nodesToDel)
{
$ret = false;
$file = $node->getAttribute('name');
$captionFileName = $captionFileNode ? $captionFileNode->getAttribute('name') : NULL;
$rootpath = p4string::delEndSlash(trim((string)($this->sxTaskSettings->hotfolder)));
$subpath = substr($path, strlen($rootpath));
$this->log(sprintf(("Archiving file '%s'"), $subpath.'/'.$file));
if($captionFileName !== NULL)
$this->log(sprintf(' '.(" (caption in '%s')"), $captionFileName));
if($grp_rid !== 0)
$this->log(sprintf(' '.(" into GRP rid=%s"), $grp_rid));
$stat0 = $stat1= "0";
if($this->sxBasePrefs->status)
$stat0 = (string)($this->sxBasePrefs->status);
if($this->sxTaskSettings->status)
$stat1 = (string)($this->sxTaskSettings->status);
if(!$stat0)
$stat0 = '0';
if(!$stat1)
$stat1 = '0';
$rid = $this->connbas->getId("RECORD", 1);
$mimeExt = giveMimeExt($path.'/'.$file);
$propfile = array(
'recordid' => $rid,
'extension' => $mimeExt['ext'],
'mime' => $mimeExt['mime'],
'size' => sprintf('%u', filesize($path.'/'.$file)),
'subpath' => $subpath,
'parentdirectory' => '',
'hotfolderfile' => $path.'/'.$file,
'hotfoldercaptionfile' => NULL,
'pathhd' => '',
'file' => '',
'originalname' => $file,
'inbase' => 0,
'type' => giveMeDocType($mimeExt['mime'])
);
if($captionFileName != $file)
{
$propfile['hotfoldercaptionfile'] = $path.'/'.$captionFileName;
}
$pi = pathinfo($subpath);
$propfile['parentdirectory'] = $pi["basename"];
// var_dump($propfile);
$meta = get_xml($this->sxBasePrefs, $propfile);
$hexstat = '';
if($meta['status'] !== NULL)
{
$s = strrev($meta['status']) . str_repeat('0', 64);
for($a=0; $a<4; $a++)
$hexstat = substr('0000'.base_convert(strrev(substr($s, $a<<4, 16)), 2, 16), -4) . $hexstat;
}
else
{
$hexstat = '0';
}
$lazaret = false;
$uuid = false;
if($grp_rid == 0 && $captionFileName == NULL)
{
$this->log(sprintf(("Checkin for lazaret")));
try
{
$file_uuid = new uuid($path.'/'.$file);
$uuid = $file_uuid->check_uuid();
$base_id = (int)($this->sxTaskSettings->base_id);
$sbas_id = phrasea::sbasFromBas($base_id);
$error_file = p4file::check_file_error($path.'/'.$file, $sbas_id);
$status = status::or_operation($stat0, $stat1);
if($meta['status'])
$status = status::or_operation($status, $meta['status']);
if(($uuid !== false && !$file_uuid->is_new_in_base(phrasea::sbasFromBas($base_id))) || count($error_file) > 0)
{
$this->log(sprintf(("Trying to move to lazaret")));
if(lazaretFile::move_uploaded_to_lazaret($path.'/'.$file, $base_id, $file, $uuid, implode("\n",$error_file), $status))
{
$this->log("File %s moved to lazaret");
$lazaret = true;
$node->setAttribute('archived', '1');
$this->archivedFiles++;
}
}
else
{
$this->log("No need to lazaret");
}
}
catch (Exception $e)
{
$this->log(sprintf(("Error while checking for lazaret : %s"), $e->getMessage()));
}
}
if(!$lazaret)
{
$cid = $node->getAttribute('cid');
// ... flag the record as 'to reindex' (status-bits 0 and 1 to 0) AND UNLOCK IT (status-bit 2 to 1)
// ... and flag it for subdef creation (jeton bit 0 to 1)
$fl = 'coll_id, record_id, parent_record_id, status, jeton, moddate, credate, xml, type, sha256, uuid';
$vl = $cid . ', ' .$rid . ', ' . '0' . ', (((' . $stat0 . ' | ' . $stat1 . ' | 0x' . $hexstat . ') & ~0x0F) | 0x0C), '.JETON_READ_META_DOC_MAKE_SUBDEF.', NOW(), NOW(), \'' . $this->connbas->escape_string($meta['xml']->saveXML()) . '\', \'' . $this->connbas->escape_string($propfile['type']) . '\', \''.hash_file('sha256',$propfile["hotfolderfile"]).'\', "'.$this->connbas->escape_string($uuid).'"' ;
$sql = 'INSERT INTO record ('.$fl.') VALUES ('.$vl.')';
//printf("sql:%s\n", $sql);
if($this->connbas->query($sql))
{
if($grp_rid !== NULL)
{
$this->connbas->query("INSERT INTO regroup (id, rid_parent, rid_child, dateadd, ord) VALUES (NULL, $grp_rid, $rid, NOW(), 0)");
}
$copyhd = true;
$documentisok = false;
if($copyhd)
{
// archiving with copy into base
$pathhd = trim((string)($this->sxBasePrefs->path));
$pathhd = p4::dispatch($pathhd);
$newname = $rid . "_document.".$propfile["extension"];
if(copy($propfile["hotfolderfile"], $pathhd . $newname))
{
$propfile['pathhd'] = $pathhd;
$propfile['file'] = $newname;
$propfile['inbase'] = 1;
$fl = 'record_id'; $vl = $rid;
$fl .= ', name'; $vl .= ", 'document'";
$fl .= ', path'; $vl .= ", '" . $this->connbas->escape_string($propfile['pathhd']) . "'";
$fl .= ', file'; $vl .= ", '" . $this->connbas->escape_string($propfile['file']) . "'";
$fl .= ', inbase'; $vl .= ", '" . $propfile['inbase'] . "'";
if(isset($propfile['width']))
{
$fl .= ', width'; $vl .= ", '" . $this->connbas->escape_string($propfile['width']) . "'";
}
if(isset($propfile['height']))
{
$fl .= ', height'; $vl .= ", '" . $this->connbas->escape_string($propfile['height']) . "'";
}
$fl .= ', mime'; $vl .= ", '" . $this->connbas->escape_string($propfile['mime']) . "'";
$fl .= ', size'; $vl .= ", '" . $this->connbas->escape_string($propfile['size']) . "'";
$fl .= ', dispatched'; $vl .= ", '1'";
$sql = "INSERT INTO subdef (".$fl.") VALUES (".$vl.")";
if($this->connbas->query($sql))
{
$node->setAttribute('archived', '1');
if($captionFileNode)
$captionFileNode->setAttribute('archived', '1');
}
else
{
$this->log(("Error : can't insert into subdef"));
$node->setAttribute('error', '1');
if($captionFileNode)
$captionFileNode->setAttribute('error', '1');
}
}
else
{
$this->log(sprintf(('Error : can\'t copy from \'%1$s\' to \'%$2s\''), $propfile["hotfolderfile"], $pathhd.$newname));
$node->setAttribute('error', '1');
if($captionFileNode)
$captionFileNode->setAttribute('error', '1');
}
}
else
{
// archiving 'in place'
$node->setAttribute('archived', '1');
if($captionFileNode)
$captionFileNode->setAttribute('archived', '1');
}
$this->archivedFiles++;
}
else
{
$this->log(("Error : can't insert into record"));
$node->setAttribute('error', '1');
if($captionFileNode)
$captionFileNode->setAttribute('error', '1');
}
}
if($node->getAttribute('archived') && $this->move_archived)
{
$this->log(sprintf(('copy \'%s\' to \'archived\''), $subpath.'/'.$file));
@mkdir($path_archived, 0755, true);
@copy($path.'/'.$file, $path_archived.'/'.$file);
if($captionFileName != $file)
{
$this->log(sprintf(('copy \'%s\' to \'archived\''), $subpath.'/'.$captionFileName));
@copy($path.'/'.$captionFileName, $path_archived.'/'.$captionFileName);
}
if(!$node->getAttribute('keep')) // do not count copy of special files as a real event
$ret = true;
}
if($node->getAttribute('error') && $this->move_error)
{
$this->log(sprintf(('copy \'%s\' to \'error\''), $subpath.'/'.$file));
@mkdir($path_error, 0755, true);
@copy($path.'/'.$file, $path_error.'/'.$file);
if($captionFileName != $file)
{
$this->log(sprintf(('copy \'%s\' to \'error\''), $subpath.'/'.$captionFileName));
@copy($path.'/'.$captionFileName, $path_error.'/'.$captionFileName);
}
if(!$node->getAttribute('keep')) // do not count copy of special files as a real event
$ret = true;
}
if(!$node->getAttribute('keep'))
{
$file = $node->getAttribute('name');
@unlink($path.'/'.$file);
$nodesToDel[] = $node;
$this->movedFiles++;
}
if($captionFileNode && !$captionFileNode->getAttribute('keep'))
{
$file = $captionFileNode->getAttribute('name');
@unlink($path.'/'.$file);
$nodesToDel[] = $captionFileNode;
$this->movedFiles++;
}
// printf("======== %s %s \n", __LINE__, var_export($nodesToDel, true));
}
/**
* xml facility :
* set attributes to a node and all children
*
* @param unknown_type $dom
* @param unknown_type $node
* @param unknown_type $attributes
*/
function setAllChildren($dom, $node, $attributes, $depth=0)
{
static $iloop=0;
if($depth==0)
$iloop = 0;
foreach($attributes as $a=>$v)
$node->setAttribute($a, $v);
if( ($iloop++ % 100) == 0)
usleep(1000);
for($n=$node->firstChild; $n; $n=$n->nextSibling)
$this->setAllChildren($dom, $n, $attributes, $depth+1);
}
function getGrpSettings($file)
{
$matched = FALSE;
foreach($this->tmaskgrp as $maskgrp)
{
//$attachments = null;
//$attachments["representation"] = null;
//$attachments["caption"] = null;
$preg_maskgrp = "/" . $maskgrp["mask"] . "/";
if(preg_match($preg_maskgrp, $file))
{
$matched = $maskgrp;
}
if($matched)
break;
}
return($matched);
}
}
class CListFolder
{
private $list;
function __construct($path, $sorted = false)
{
$this->list = array();
if($hdir = opendir($path))
{
while(false !== ($file = readdir($hdir)))
$this->list[] = $file;
closedir($hdir);
if($sorted)
natcasesort($this->list);
}
}
function __destruct()
{
unset($this->list);
}
function read()
{
return(array_shift($this->list));
}
}