Remove old files

This commit is contained in:
Romain Neutron
2013-09-30 12:12:18 +02:00
parent 598475e5b5
commit 36cc33b05a
18 changed files with 1 additions and 8698 deletions

View File

@@ -1,630 +0,0 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2013 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Alchemy\Phrasea\Application;
use Monolog\Logger;
use Symfony\Component\Process\PhpExecutableFinder;
/**
*
* @license http://opensource.org/licenses/gpl-3.0 GPLv3
* @link www.phraseanet.com
*/
class task_Scheduler
{
const TASKDELAYTOQUIT = 60;
// how to schedule tasks (choose in 'run' method)
const METHOD_FORK = 'METHOD_FORK';
const METHOD_PROC_OPEN = 'METHOD_PROC_OPEN';
const ERR_ALREADY_RUNNING = 114; // aka EALREADY (Operation already in progress)
/**
*
* @var \Monolog\Logger
*/
private $logger;
private $method;
private $dependencyContainer;
private $schedstatus;
public function __construct(Application $application, Logger $logger)
{
declare(ticks = 1);
$this->dependencyContainer = $application;
$this->logger = $logger;
$this->schedstatus = '';
}
protected function log($message)
{
$this->logger->addInfo($message);
return $this;
}
protected function sleep($nsec)
{
$nsec = (integer) $nsec;
if ($nsec < 0) {
throw new \InvalidArgumentException(sprintf("(%s) is not > 0"));
}
$end = microtime(true) + $nsec;
while (microtime(true) < $end) {
usleep(10000);
}
}
/**
* @throws Exception if scheduler is already running
* @todo doc all possible exception
*/
public function sigHandler($signal)
{
switch ($signal) {
case SIGCHLD:
$status = null;
$pid = pcntl_wait($status);
$exitstatus = pcntl_wexitstatus($status);
$this->log(sprintf("SIGCHLD (%s) received from pid=%s, status=%s, exitstatus=%s", $signal, $pid, var_export($status, true), $exitstatus));
break;
case SIGINT: // ctrl C
$this->log(sprintf("SIGINT (%s) Ctrl-C received, schedstatus='tostop'", $signal));
$this->schedstatus = 'tostop';
break;
case SIGTERM:
$this->log(sprintf("SIGTERM (%s) received but ignored, http timeout ?", $signal));
break;
}
}
public function run()
{
//prevent scheduler to fail if GV_cli is not provided
if (isset($this->dependencyContainer['phraseanet.configuration']['binaries']['php_binary'])) {
$php = $this->dependencyContainer['phraseanet.configuration']['binaries']['php_binary'];
} else {
$finder = new PhpExecutableFinder();
$php = $finder->find();
}
if ( ! is_executable($php)) {
throw new \RuntimeException('PHP cli is not provided in binary configuration');
}
$this->method = self::METHOD_PROC_OPEN;
$nullfile = '/dev/null';
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
$nullfile = 'NUL';
}
if (\task_manager::isPosixPcntlSupported()) {
// avoid <defunct> php when a task ends
// pcntl_signal(SIGCHLD, SIG_IGN); // no zombies but no returnValue
// pcntl_signal(SIGCHLD, SIG_DFL); // with "declare(ticks=1)" returnValue ok but zombies
pcntl_signal(SIGCHLD, array($this, 'sigHandler')); // ok
pcntl_signal(SIGINT, array($this, 'sigHandler'));
pcntl_signal(SIGTERM, array($this, 'sigHandler'));
$this->method = self::METHOD_FORK;
}
$lockdir = $this->dependencyContainer['root.path'] . '/tmp/locks/';
for ($try = 1; true; $try ++) {
$lockfile = ($lockdir . 'scheduler.lock');
if (($schedlock = fopen($lockfile, 'a+')) != FALSE) {
if (flock($schedlock, LOCK_EX | LOCK_NB) === FALSE) {
$this->log(sprintf("failed to lock '%s' (try=%s/4)", $lockfile, $try));
if ($try == 4) {
$this->log("scheduler already running.");
fclose($schedlock);
throw new Exception('scheduler already running.', self::ERR_ALREADY_RUNNING);
return;
} else {
$this->sleep(2);
}
} else {
// locked
ftruncate($schedlock, 0);
fwrite($schedlock, '' . getmypid());
fflush($schedlock);
// for windows : unlock then lock shared to allow OTHER processes to read the file
// too bad : no critical section nor atomicity
flock($schedlock, LOCK_UN);
flock($schedlock, LOCK_SH);
break;
}
}
}
$this->log(sprintf("running scheduler with method %s", $this->method));
$conn = $this->dependencyContainer['phraseanet.appbox']->get_connection();
$taskPoll = array(); // the poll of tasks
$sleeptime = 3;
$sql = "UPDATE sitepreff SET schedstatus='started'";
$conn->exec($sql);
$task_manager = $this->dependencyContainer['task-manager'];
// set every 'auto-start' task to start
foreach ($task_manager->getTasks() as $task) {
if ($task->isActive()) {
if ( ! $task->getPID()) {
/* @var $task task_abstract */
$task->resetCrashCounter();
$task->setState(task_abstract::STATE_TOSTART);
}
}
}
$this->schedstatus = 'started';
$runningtask = 0;
$connwaslost = false;
while ($this->schedstatus == 'started' || $runningtask > 0) {
while (1) {
try {
assert(is_object($conn));
$ping = @$conn->ping();
} catch (ErrorException $e) {
$ping = false;
}
if ($ping) {
break;
}
unset($conn);
if (! $connwaslost) {
$this->log(sprintf("Warning : abox connection lost, restarting in 10 min."));
}
$this->sleep(60 * 10);
try {
$conn = $this->dependencyContainer['phraseanet.appbox']->get_connection();
} catch (ErrorException $e) {
$ping = false;
}
$connwaslost = true;
}
if ($connwaslost) {
$this->log("abox connection restored");
$sql = 'UPDATE task SET crashed=0';
$stmt = $conn->prepare($sql);
$stmt->execute();
$stmt->closeCursor();
$connwaslost = false;
}
if ($this->schedstatus == "started") {
$this->schedstatus = '';
$row = NULL;
try {
$sql = "SELECT schedstatus FROM sitepreff";
$stmt = $conn->prepare($sql);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
} catch (ErrorException $e) {
continue;
}
if ($row) {
$this->schedstatus = $row["schedstatus"];
}
}
if ($this->schedstatus == 'tostop') {
$sql = 'UPDATE sitepreff SET schedstatus = "stopping"';
$stmt = $conn->prepare($sql);
$stmt->execute();
$stmt->closeCursor();
// if scheduler is stopped, stop the tasks
$sql = 'UPDATE task2 SET status="tostop" WHERE status != "stopped" and status != "manual"';
$stmt = $conn->prepare($sql);
$stmt->execute();
$stmt->closeCursor();
$this->log("schedstatus == 'stopping', waiting tasks to end");
}
// initialy, all tasks are supposed to be removed from the poll
foreach ($taskPoll as $tkey => $task) {
$taskPoll[$tkey]["todel"] = true;
}
foreach ($task_manager->getTasks(true) as $task) {
$tkey = "t_" . $task->getID();
$status = $task->getState();
if ( ! isset($taskPoll[$tkey])) {
// the task is not in the poll, add it
$arguments = array();
if ($this->dependencyContainer['phraseanet.registry']->get('GV_PHP_INI')) {
$arguments[] = '-c';
$arguments[] = $this->dependencyContainer['phraseanet.registry']->get('GV_PHP_INI');
}
$arguments = array_merge($arguments, array(
'-f',
$this->dependencyContainer['root.path'] . '/bin/console',
'--',
'-q',
'task:run',
$task->getID(), '--runner=scheduler'
));
$taskPoll[$tkey] = array(
"task" => $task,
"current_status" => $status,
"cmd" => $php,
"args" => $arguments,
"killat" => null,
"sigterm_sent" => false,
"pid" => false
);
if ($this->method == self::METHOD_PROC_OPEN) {
$taskPoll[$tkey]['process'] = NULL;
$taskPoll[$tkey]['pipes'] = NULL;
}
$this->log(
sprintf(
"new Task %s, status=%s"
, $taskPoll[$tkey]["task"]->getID()
, $status
)
);
} else {
// the task is already in the poll, update its status
if ($taskPoll[$tkey]["current_status"] != $status) {
$this->log(
sprintf(
"Task %s, oldstatus=%s, newstatus=%s"
, $taskPoll[$tkey]["task"]->getID()
, $taskPoll[$tkey]["current_status"]
, $status
)
);
$taskPoll[$tkey]["current_status"] = $status;
}
// update the whole task object
unset($taskPoll[$tkey]["task"]);
$taskPoll[$tkey]["task"] = $task;
}
unset($task);
$taskPoll[$tkey]["todel"] = false; // this task exists, do not remove from poll
}
// remove not-existing task from poll
foreach ($taskPoll as $tkey => $task) {
if ($task["todel"]) {
$this->log(sprintf("Task %s deleted", $taskPoll[$tkey]["task"]->getID()));
unset($taskPoll[$tkey]);
}
}
// Launch task that are not yet launched
$runningtask = 0;
foreach ($taskPoll as $tkey => $tv) {
$status = $tv['task']->getState();
switch ($status) {
default:
$this->log(sprintf('Unknow status `%s`', $status));
break;
case task_abstract::STATE_TORESTART:
if ( ! $taskPoll[$tkey]['task']->getPID()) {
if ($this->method == self::METHOD_PROC_OPEN) {
@fclose($taskPoll[$tkey]["pipes"][1]);
@fclose($taskPoll[$tkey]["pipes"][2]);
@proc_close($taskPoll[$tkey]["process"]);
$taskPoll[$tkey]["process"] = null;
} elseif ($this->method == self::METHOD_FORK) {
$pid = $taskPoll[$tkey]['pid'];
if ($pid) {
$status = NULL;
if (pcntl_waitpid($pid, $status, WNOHANG) === $pid) {
// pid has quit
$taskPoll[$tkey]['pid'] = false;
}
}
}
if ($this->schedstatus == 'started') {
$taskPoll[$tkey]["task"]->setState(task_abstract::STATE_TOSTART);
}
// trick to start the task immediatly : DON'T break if ending with 'tostart'
// so it will continue with 'tostart' case !
} else {
break;
}
case task_abstract::STATE_TOSTART:
// if scheduler is 'tostop', don't launch a new task !
if ($this->schedstatus != 'started') {
break;
}
$taskPoll[$tkey]["killat"] = NULL;
if ($this->method == self::METHOD_PROC_OPEN) {
if (! $taskPoll[$tkey]["process"]) {
$nullfile = defined('PHP_WINDOWS_VERSION_BUILD') ? 'NUL' : '/dev/null';
$descriptors[1] = array('file', $nullfile, 'a+');
$descriptors[2] = array('file', $nullfile, 'a+');
$taskPoll[$tkey]["process"] = proc_open(
escapeshellarg($taskPoll[$tkey]["cmd"]) . ' ' . implode(' ', array_map('escapeshellarg', $taskPoll[$tkey]["args"]))
, $descriptors
, $taskPoll[$tkey]["pipes"]
, $this->dependencyContainer['root.path'] . "/bin/"
, null
, array('bypass_shell' => true)
);
if (is_resource($taskPoll[$tkey]["process"])) {
// let the process lock and write it's pid
$sleepTimeout = microtime(true) + 10;
do {
usleep(500000);
if (null !== $taskPoll[$tkey]['task']->getPID()) {
break;
}
} while (microtime(true) < $sleepTimeout);
}
if (is_resource($taskPoll[$tkey]["process"]) && ($pid = $taskPoll[$tkey]['task']->getPID()) !== null) {
$taskPoll[$tkey]['pid'] = $pid;
$this->log(
sprintf(
"Task %s '%s' started (pid=%s)"
, $taskPoll[$tkey]['task']->getID()
, $taskPoll[$tkey]["cmd"] . ' ' . implode(' ', $taskPoll[$tkey]["args"])
, $pid
)
);
$runningtask ++;
} else {
$taskPoll[$tkey]["task"]->incrementCrashCounter();
@fclose($taskPoll[$tkey]["pipes"][1]);
@fclose($taskPoll[$tkey]["pipes"][2]);
@proc_close($taskPoll[$tkey]["process"]);
$taskPoll[$tkey]["process"] = null;
$this->log(
sprintf(
"Task %s '%s' failed to start %d times"
, $taskPoll[$tkey]["task"]->getID()
, $taskPoll[$tkey]["cmd"]
, $taskPoll[$tkey]["task"]->getCrashCounter()
)
);
if ($taskPoll[$tkey]["task"]->getCrashCounter() > 5) {
$taskPoll[$tkey]["task"]->setState(task_abstract::STATE_STOPPED);
} else {
$taskPoll[$tkey]["task"]->setState(task_abstract::STATE_TOSTART);
}
}
}
} elseif ($this->method == self::METHOD_FORK) {
$pid = pcntl_fork();
if ($pid == -1) {
die("failed to fork");
} elseif ($pid == 0) {
umask(0);
if (posix_setsid() < 0) {
die("Forked process could not detach from terminal\n");
}
// todo (if possible) : redirecting stdin, stdout to log files ?
$this->log(sprintf("exec('%s %s')", $taskPoll[$tkey]["cmd"], implode(' ', $taskPoll[$tkey]["args"])));
pcntl_exec($taskPoll[$tkey]["cmd"], $taskPoll[$tkey]["args"]);
} else {
// parent (scheduler)
$taskPoll[$tkey]['pid'] = $pid;
}
}
break;
case task_abstract::STATE_STARTED:
$crashed = false;
// If no process, the task is probably manually ran
if ($this->method == self::METHOD_PROC_OPEN) {
if ($taskPoll[$tkey]["process"]) {
$taskPoll[$tkey]["killat"] = NULL;
if (is_resource($taskPoll[$tkey]["process"])) {
$proc_status = proc_get_status($taskPoll[$tkey]["process"]);
if ($proc_status['running']) {
$runningtask ++;
} else {
$crashed = true;
}
} else {
$crashed = true;
}
}
}
if ( ! $crashed && ! $taskPoll[$tkey]['task']->getPID()) {
$crashed = true;
}
if (! $crashed) {
$taskPoll[$tkey]["killat"] = NULL;
$runningtask ++;
} else {
// crashed !
$taskPoll[$tkey]["task"]->incrementCrashCounter();
if ($this->method == self::METHOD_PROC_OPEN) {
@fclose($taskPoll[$tkey]["pipes"][1]);
@fclose($taskPoll[$tkey]["pipes"][2]);
@proc_close($taskPoll[$tkey]["process"]);
$taskPoll[$tkey]["process"] = null;
}
$this->log(
sprintf(
"Task %s crashed %d times"
, $taskPoll[$tkey]["task"]->getID()
, $taskPoll[$tkey]["task"]->getCrashCounter()
)
);
if ($taskPoll[$tkey]["task"]->getCrashCounter() > 5) {
$taskPoll[$tkey]["task"]->setState(task_abstract::STATE_STOPPED);
} else {
$taskPoll[$tkey]["task"]->setState(task_abstract::STATE_TOSTART);
}
}
break;
case task_abstract::STATE_TOSTOP:
if ($taskPoll[$tkey]["killat"] === NULL) {
$taskPoll[$tkey]["killat"] = time() + self::TASKDELAYTOQUIT;
}
$pid = $taskPoll[$tkey]['task']->getPID();
if ($pid) {
// send ctrl-c to tell the task to CLEAN quit
// (just in case the task doesn't pool his status 'tostop' fast enough)
if (function_exists('posix_kill')) {
if (! $taskPoll[$tkey]['sigterm_sent']) {
posix_kill($pid, SIGTERM);
$this->log(
sprintf(
"SIGTERM sent to task %s (pid=%s)"
, $taskPoll[$tkey]["task"]->getID()
, $pid
)
);
}
}
if (($dt = $taskPoll[$tkey]["killat"] - time()) < 0) {
// task still alive, time to kill
if ($this->method == self::METHOD_PROC_OPEN) {
proc_terminate($taskPoll[$tkey]["process"], 9);
@fclose($taskPoll[$tkey]["pipes"][1]);
@fclose($taskPoll[$tkey]["pipes"][2]);
proc_close($taskPoll[$tkey]["process"]);
$this->log(
sprintf(
"proc_terminate(...) done on task %s (pid=%s)"
, $taskPoll[$tkey]["task"]->getID()
, $pid
)
);
} else { // METHOD_FORK, I guess we have posix
posix_kill($pid, 9);
$this->log(
sprintf(
"SIGKILL sent to task %s (pid=%s)"
, $taskPoll[$tkey]["task"]->getID()
, $pid
)
);
}
} else {
$this->log(
sprintf(
"waiting task %s to quit (kill in %d seconds)"
, $taskPoll[$tkey]["task"]->getID()
, $dt
)
);
$runningtask ++;
}
} else {
$this->log(
sprintf(
"task %s has quit"
, $taskPoll[$tkey]["task"]->getID()
)
);
$taskPoll[$tkey]["task"]->setState(task_abstract::STATE_STOPPED);
}
break;
case task_abstract::STATE_STOPPED:
case task_abstract::STATE_TODELETE:
if ($this->method == self::METHOD_PROC_OPEN) {
if ($taskPoll[$tkey]["process"]) {
@fclose($taskPoll[$tkey]["pipes"][1]);
@fclose($taskPoll[$tkey]["pipes"][2]);
@proc_close($taskPoll[$tkey]["process"]);
$taskPoll[$tkey]["process"] = null;
}
} elseif ($this->method == self::METHOD_FORK) {
$pid = $taskPoll[$tkey]['pid'];
if ($pid) {
$status = NULL;
if (pcntl_waitpid($pid, $status, WNOHANG) === $pid) {
// pid has quit
$taskPoll[$tkey]['pid'] = false;
}
}
}
break;
}
}
if(function_exists('pcntl_sigprocmask')) {
@pcntl_sigprocmask(SIG_BLOCK, array(SIGCHLD));
}
$this->sleep(1);
for ($i = 0; $this->schedstatus=='started' && $i < $sleeptime; $i++) {
$this->sleep(1);
}
if(function_exists('pcntl_sigprocmask')) {
@pcntl_sigprocmask(SIG_UNBLOCK, array(SIGCHLD));
}
}
$sql = "UPDATE sitepreff SET schedstatus='stopped', schedpid='0'";
$stmt = $conn->prepare($sql);
$stmt->execute();
$stmt->closeCursor();
$this->log("Scheduler2 is quitting.");
ftruncate($schedlock, 0);
fclose($schedlock);
$this->log("Scheduler2 has quit.\n");
}
}

View File

@@ -1,909 +0,0 @@
<?php
use Monolog\Logger;
use Alchemy\Phrasea\Core\Configuration\Configuration;
abstract class task_abstract
{
const LAUCHED_BY_BROWSER = 1;
const LAUCHED_BY_COMMANDLINE = 2;
const STATE_TOSTOP = 'tostop';
const STATE_STARTED = 'started';
const STATE_TOSTART = 'tostart';
const STATE_TORESTART = 'torestart';
const STATE_STOPPED = 'stopped';
const STATE_TODELETE = 'todelete';
const RUNNER_MANUAL = 'manual';
const RUNNER_SCHEDULER = 'scheduler';
const STATE_OK = 'STATE_OK';
const STATE_MAXMEGSREACHED = 'STATE_MAXMEGS';
const STATE_MAXRECSDONE = 'STATE_MAXRECS';
const STATE_FINISHED = 'STATE_FINISHED';
const SIGNAL_SCHEDULER_DIED = 'SIGNAL_SCHEDULER_DIED';
const ERR_ALREADY_RUNNING = 114; // aka EALREADY (Operation already in progress)
// default min/max values for the 'restart task every n records' setting on tasks
const MINRECS = 10;
const MAXRECS = 100;
// default min/max values for the 'overflow memory (Mo)' setting on tasks
const MINMEGS = 64;
const MAXMEGS = 256;
// default memory value
const DEFMEGS = 92;
// default min/max values for the 'period (seconds)' setting on tasks
const MINPERIOD = 10;
const MAXPERIOD = 3600;
// default min/max values for the 'flush every n records' setting on tasks
const MINFLUSH = 1;
const MAXFLUSH = 100;
const LOG_DEBUG = Logger::DEBUG;
const LOG_INFO = Logger::INFO;
const LOG_WARNING = Logger::WARNING;
const LOG_ERROR = Logger::ERROR;
const LOG_CRITICAL = Logger::CRITICAL;
const LOG_ALERT = Logger::ALERT;
/**
*
* @var Logger
*/
protected $logger;
protected $suicidable = false;
protected $launched_by = 0;
/**
* Number of records done
*
* @var <type>
*/
protected $records_done = 0;
/**
* Maximum number of records before we restart the task
*
* @var <type>
*/
protected $maxrecs;
/**
* Boolean switch to stop the task
*
* @var <type>
*/
protected $running = false;
/**
* current number of loops done
*
* @var <type>
*/
protected $loop = 0;
/**
* max number of loops before the task is restarted
*
* @var <type>
*/
protected $maxloops = 5;
/**
* task state, either ok, maxmemory or maxrecords reached
*
* @var <type>
*/
protected $current_state;
/**
* maximum memory allowed
*
* @var <type>
*/
protected $maxmegs;
protected $runner;
/**
* delay between two loops
*
* @var <type>
*/
protected $title;
protected $settings;
protected $crash_counter;
protected $status;
protected $active;
protected $debug = false;
protected $completed_percentage;
protected $period = 60;
protected $taskid = NULL;
protected $system = '';
protected $dependencyContainer;
public function __construct($taskid, Pimple $dependencyContainer, Logger $logger)
{
$this->dependencyContainer = $dependencyContainer;
$this->logger = $logger;
$this->taskid = (integer) $taskid;
phrasea::use_i18n($this->dependencyContainer['locale']);
$this->launched_by = array_key_exists("REQUEST_URI", $_SERVER) ? self::LAUCHED_BY_BROWSER : self::LAUCHED_BY_COMMANDLINE;
try {
$conn = connection::getPDOConnection($this->dependencyContainer);
} catch (Exception $e) {
$this->log($e->getMessage());
$this->log(("Warning : abox connection lost, restarting in 10 min."));
$this->sleep(60 * 10);
$this->running = false;
return '';
}
$sql = 'SELECT crashed, pid, status, active, settings, name, completed, runner
FROM task2 WHERE task_id = :taskid';
$stmt = $conn->prepare($sql);
$stmt->execute(array(':taskid' => $this->getID()));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
if (! $row) {
throw new Exception('Unknown task id');
}
$this->title = $row['name'];
$this->crash_counter = (integer) $row['crashed'];
$this->active = ! ! $row['active'];
$this->settings = $row['settings'];
$this->runner = $row['runner'];
$this->completed_percentage = (int) $row['completed'];
$this->settings = $row['settings'];
if (false !== $sx = @simplexml_load_string($this->settings)) {
$this->loadSettings($sx);
}
}
/**
*
* @return string
*/
public function getClass()
{
return get_class($this);
}
/**
* get the state of the task (task_abstract::STATE_*)
*
* @return String
*/
public function getState()
{
static $stmt = NULL;
$conn = connection::getPDOConnection($this->dependencyContainer);
if (! $stmt) {
$sql = 'SELECT status FROM task2 WHERE task_id = :taskid';
$stmt = $conn->prepare($sql);
}
$stmt->execute(array(':taskid' => $this->taskid));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
if (! $row) {
throw new Exception('Unknown task id');
}
unset($conn);
return $row['status'];
}
/**
* to be overwritten by tasks : ECHO text to be included in <head> in task interface
*/
public function printInterfaceHEAD()
{
}
/**
* to be overwritten by tasks : ECHO javascript to be included in <head> in task interface
*/
public function printInterfaceJS()
{
}
/**
*
* @return boolean
*/
public function hasInterfaceHTML()
{
return method_exists($this, "getInterfaceHTML");
}
/**
* set the state of the task (task_abstract::STATE_*)
*
* @param String $status
* @throws Exception_InvalidArgument
*/
public function setState($status)
{
$av_status = array(
self::STATE_STARTED,
self::STATE_TOSTOP,
self::STATE_STOPPED,
self::STATE_TORESTART,
self::STATE_TOSTART,
self::STATE_TODELETE
);
if ( ! in_array($status, $av_status)) {
throw new Exception_InvalidArgument(sprintf('unknown status `%s`', $status));
}
$conn = connection::getPDOConnection($this->dependencyContainer);
$sql = 'UPDATE task2 SET status = :status WHERE task_id = :taskid';
$stmt = $conn->prepare($sql);
$stmt->execute(array(':status' => $status, ':taskid' => $this->getID()));
$stmt->closeCursor();
$this->log(sprintf("task %d <- %s", $this->getID(), $status));
}
/**
*
* @param boolean $active 'active' means 'auto-start when scheduler starts'
* @return \task_abstract
*/
public function setActive($active)
{
$conn = connection::getPDOConnection($this->dependencyContainer);
$sql = 'UPDATE task2 SET active = :active WHERE task_id = :taskid';
$stmt = $conn->prepare($sql);
$stmt->execute(array(':active' => ($active ? '1' : '0'), ':taskid' => $this->getID()));
$stmt->closeCursor();
$this->active = ! ! $active;
return $this;
}
/**
*
* @param string $title
* @return \task_abstract
*/
public function setTitle($title)
{
$title = strip_tags($title);
$conn = connection::getPDOConnection($this->dependencyContainer);
$sql = 'UPDATE task2 SET name = :title WHERE task_id = :taskid';
$stmt = $conn->prepare($sql);
$stmt->execute(array(':title' => $title, ':taskid' => $this->getID()));
$stmt->closeCursor();
$this->title = $title;
return $this;
}
/**
*
* @param string $settings xml settings as STRING
* @throws Exception_InvalidArgument if not proper xml
* @return \task_abstract
*/
public function setSettings($settings)
{
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
if (!@$dom->loadXML($settings)) {
throw new Exception_InvalidArgument('Bad XML');
}
$conn = connection::getPDOConnection($this->dependencyContainer);
$sql = 'UPDATE task2 SET settings = :settings WHERE task_id = :taskid';
$stmt = $conn->prepare($sql);
$stmt->execute(array(':settings' => $dom->saveXML(), ':taskid' => $this->getID()));
$stmt->closeCursor();
$this->settings = $settings;
$this->loadSettings(simplexml_load_string($dom->saveXML()));
return $this;
}
/**
*
* @return \task_abstract
*/
public function resetCrashCounter()
{
$conn = connection::getPDOConnection($this->dependencyContainer);
$sql = 'UPDATE task2 SET crashed = 0 WHERE task_id = :taskid';
$stmt = $conn->prepare($sql);
$stmt->execute(array(':taskid' => $this->getID()));
$stmt->closeCursor();
$this->crash_counter = 0;
return $this;
}
/**
*
* @return int
*/
public function getCrashCounter()
{
return $this->crash_counter;
}
/**
*
* @return int
*/
public function incrementCrashCounter()
{
$conn = connection::getPDOConnection($this->dependencyContainer);
$sql = 'UPDATE task2 SET crashed = crashed + 1 WHERE task_id = :taskid';
$stmt = $conn->prepare($sql);
$stmt->execute(array(':taskid' => $this->getID()));
$stmt->closeCursor();
return ++ $this->crash_counter;
}
/**
*
* @return string
*/
public function getSettings()
{
return $this->settings;
}
/**
* 'active' means 'auto-start when scheduler starts'
*
* @return boolean
*
*/
public function isActive()
{
return $this->active;
}
/**
*
* @return int
*/
public function getCompletedPercentage()
{
return $this->completed_percentage;
}
public static function getName()
{
throw new \LogicException('This method must be implemented');
}
public static function help()
{
throw new \LogicException('This method must be implemented');
}
/**
*
* @return enum (self::RUNNER_MANUAL or self::RUNNER_SCHEDULER)
*/
public function getRunner()
{
return $this->runner;
}
/**
*
* @param enum $runner (self::RUNNER_MANUAL or self::RUNNER_SCHEDULER)
* @throws Exception_InvalidArgument
* @return \task_abstract
*/
public function setRunner($runner)
{
if ($runner != self::RUNNER_MANUAL && $runner != self::RUNNER_SCHEDULER) {
throw new Exception_InvalidArgument(sprintf('unknown runner `%s`', $runner));
}
$this->runner = $runner;
$conn = connection::getPDOConnection($this->dependencyContainer);
$sql = 'UPDATE task2 SET runner = :runner WHERE task_id = :taskid';
$params = array(
':taskid' => $this->getID()
, ':runner' => $this->runner
);
$stmt = $conn->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
return $this;
}
/**
*
* @return string
*/
public function getTitle()
{
return $this->title;
}
/**
*
*/
public function delete()
{
if ( ! $this->getPID()) { // do not delete a running task
$conn = connection::getPDOConnection($this->dependencyContainer);
$sql = "DELETE FROM task2 WHERE task_id = :task_id";
$stmt = $conn->prepare($sql);
$stmt->execute(array(':task_id' => $this->getID()));
$stmt->closeCursor();
$lock_file = $this->dependencyContainer['root.path'] . '/tmp/locks/task_' . $this->getID() . '.lock';
@unlink($lock_file);
}
}
/**
* set last execution time to now()
*/
public function setLastExecTime()
{
$conn = connection::getPDOConnection($this->dependencyContainer);
$sql = 'UPDATE task2 SET last_exec_time=NOW() WHERE task_id = :taskid';
$stmt = $conn->prepare($sql);
$stmt->execute(array(':taskid' => $this->getID()));
$stmt->closeCursor();
}
/**
* Return the last time the task was executed
*
* @return null|\DateTime
*/
public function getLastExecTime()
{
$conn = connection::getPDOConnection($this->dependencyContainer);
$sql = 'SELECT last_exec_time FROM task2 WHERE task_id = :taskid';
$stmt = $conn->prepare($sql);
$stmt->execute(array(':taskid' => $this->getID()));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
$time = null;
if ($row['last_exec_time'] != '0000-00-00 00:00:00') {
$time = new \DateTime($row['last_exec_time']);
}
return $time;
}
/**
*
* @return null|integer
* pid (int) of the task
* NULL : the pid file is not locked (task no running)
*/
public function getPID()
{
$pid = NULL;
$lockfile = $this->getLockfilePath();
if (($fd = fopen($lockfile, 'a+')) != FALSE) {
if (flock($fd, LOCK_EX | LOCK_NB) === FALSE) {
// already locked ? : task running
$pid = (integer) fgets($fd);
} else {
// can lock : not running
flock($fd, LOCK_UN);
}
fclose($fd);
}
return $pid;
}
/**
* set to false to ask the task to quit its loop
* @param boolean $stat
*
*/
public function setRunning($stat)
{
$this->running = $stat;
}
protected function pause($when_started = 0)
{
$this->log($this->records_done . ' records done');
if ($this->running) { // && $this->records_done == 0)
$when_started = time() - $when_started;
if ($when_started < $this->period) {
for ($t = $this->period - $when_started; $this->running && $t > 0; $t --) {
// DON'T do sleep($this->period - $when_started) because it prevents ticks !
$s = $this->getState();
if ($s == self::STATE_TOSTOP) {
$this->setState(self::STATE_STOPPED);
$this->running = FALSE;
} else {
$this->sleep(1);
}
}
}
}
}
/**
* sleep n seconds
*
* @param int $nsec
* @throws \InvalidArgumentException
*/
protected function sleep($nsec)
{
$nsec = (integer) $nsec;
if ($nsec < 0) {
throw new \InvalidArgumentException(sprintf("(%s) is not > 0"));
}
$end = microtime(true) + $nsec;
while ($this->running && microtime(true) < $end) {
usleep(10000);
}
}
/**
*
* @return string fullpath to the pid file for the task
*/
private function getLockfilePath()
{
$lockdir = $this->dependencyContainer['root.path'] . '/tmp/locks/';
$lockfilePath = ($lockdir . 'task_' . $this->getID() . '.lock');
return $lockfilePath;
}
/**
*
* @return resource file descriptor of the OPENED pid file
* @throws Exception if file is already locked (task running)
*/
private function lockTask()
{
$lockfile = $this->getLockfilePath();
$lockFD = fopen($lockfile, 'a+');
$locker = true;
if (flock($lockFD, LOCK_EX | LOCK_NB, $locker) === FALSE) {
$this->log("runtask::ERROR : task already running.");
fclose($lockFD);
throw new Exception('task already running.', self::ERR_ALREADY_RUNNING);
}
// here we run the task
ftruncate($lockFD, 0);
fwrite($lockFD, '' . getmypid());
fflush($lockFD);
// for windows : unlock then lock shared to allow OTHER processes to read the file
// too bad : no critical section nor atomicity
flock($lockFD, LOCK_UN);
flock($lockFD, LOCK_SH);
return $lockFD;
}
final public function run($runner)
{
$lockFD = $this->lockTask();
$this->setRunner($runner);
$this->setState(self::STATE_STARTED);
// run the real code of the task -into the task's class- (may throw an exception)
$exception = NULL;
try {
$this->run2();
} catch (\Exception $exception) {
}
if ($this->getState() === self::STATE_STARTED && $this->runner === self::RUNNER_MANUAL) {
$this->setState(self::STATE_STOPPED);
}
// in any case, exception or not, the task is ending so unlock the pid file
$this->unlockTask($lockFD);
// if something went wrong, report
if ($exception) {
throw $exception;
}
}
/**
*
* @param resource $lockFD file descriptor of the OPENED lock file
*/
private function unlockTask($lockFD)
{
flock($lockFD, LOCK_UN | LOCK_NB);
ftruncate($lockFD, 0);
fclose($lockFD);
$lockfile = $this->getLockfilePath();
@unlink($lockfile);
switch ($this->getState()) {
case self::STATE_TODELETE:
$this->delete();
break;
case self::STATE_TOSTOP:
$this->setState(self::STATE_STOPPED);
break;
}
}
abstract protected function run2();
protected function processLoop(&$box, &$rs)
{
$ret = self::STATE_OK;
$rowstodo = count($rs);
$rowsdone = 0;
if ($rowstodo > 0) {
$this->setProgress(0, $rowstodo);
}
foreach ($rs as $row) {
try {
// process one record
$this->processOneContent($box, $row);
} catch (Exception $e) {
$this->log("Exception : " . $e->getMessage() . " " . basename($e->getFile()) . " " . $e->getLine());
}
$this->records_done ++;
$this->setProgress($rowsdone, $rowstodo);
// post-process
$this->postProcessOneContent($box, $row);
$rowsdone ++;
$current_memory = memory_get_usage();
if ($current_memory >> 20 >= $this->maxmegs) {
$this->log(sprintf("Max memory (%s M) reached (actual is %.02f M)", $this->maxmegs, ($current_memory >> 10) / 1024), self::LOG_ERROR);
$this->running = FALSE;
$ret = self::STATE_MAXMEGSREACHED;
}
if ($this->records_done >= (integer) ($this->maxrecs)) {
$this->log(sprintf("Max records done (%s) reached (actual is %s)", $this->maxrecs, $this->records_done));
$this->running = FALSE;
$ret = self::STATE_MAXRECSDONE;
}
try {
if ($this->getState() == self::STATE_TOSTOP) {
$this->running = FALSE;
$ret = self::STATE_TOSTOP;
}
} catch (Exception $e) {
$this->running = FALSE;
}
if (! $this->running) {
break;
}
}
//
// if nothing was done, at least check the status
if ($rowsdone == 0 && $this->running) {
$current_memory = memory_get_usage();
if ($current_memory >> 20 >= $this->maxmegs) {
$this->log(sprintf("Max memory (%s M) reached (current is %.02f M)", $this->maxmegs, ($current_memory >> 10) / 1024), self::LOG_ERROR);
$this->running = FALSE;
$ret = self::STATE_MAXMEGSREACHED;
}
if ($this->records_done >= (integer) ($this->maxrecs)) {
$this->log(sprintf("Max records done (%s) reached (actual is %s)", $this->maxrecs, $this->records_done));
$this->running = FALSE;
$ret = self::STATE_MAXRECSDONE;
}
try {
$status = $this->getState();
if ($status == self::STATE_TOSTOP) {
$this->running = FALSE;
$ret = self::STATE_TOSTOP;
}
} catch (Exception $e) {
$this->running = FALSE;
}
}
if ($rowstodo > 0) {
$this->setProgress(0, 0);
}
return $ret;
}
protected function loadSettings(SimpleXMLElement $sx_task_settings)
{
$this->period = (integer) $sx_task_settings->period;
if ($this->period < self::MINPERIOD || $this->period > self::MAXPERIOD) {
$this->period = self::MINPERIOD;
}
$this->maxrecs = (integer) $sx_task_settings->maxrecs;
if ($sx_task_settings->maxrecs < self::MINRECS || $sx_task_settings->maxrecs > self::MAXRECS) {
$this->maxrecs = self::MINRECS;
}
$this->maxmegs = (integer) $sx_task_settings->maxmegs;
if ($sx_task_settings->maxmegs < self::MINMEGS || $sx_task_settings->maxmegs > self::MAXMEGS) {
$this->maxmegs = self::DEFMEGS;
}
$this->record_buffer_size = (integer) $sx_task_settings->flush;
if ($sx_task_settings->flush < self::MINFLUSH || $sx_task_settings->flush > self::MAXFLUSH) {
$this->record_buffer_size = self::MINFLUSH;
}
}
protected function incrementLoops()
{
if ($this->getRunner() == self::RUNNER_SCHEDULER && ++ $this->loop >= $this->maxloops) {
$this->log(sprintf(('%d loops done, restarting'), $this->loop));
$this->setState(self::STATE_TORESTART);
$this->running = false;
}
}
public function log($message, $level=self::LOG_INFO)
{
// nb : self::log_levels ARE standard log levels, ok with monolog
$this->logger->addRecord($level, $message);
return $this;
}
public static function interfaceAvailable()
{
return true;
}
public function getInterfaceHTML()
{
return '';
}
public function graphic2xml($oldxml)
{
return $oldxml;
}
/**
*
* @param \Pimple $dependencyContainer
* @param string $class_name
* @param string $settings
* @return task_abstract
*/
public static function create(\Pimple $dependencyContainer, $settings = null)
{
$sql = 'INSERT INTO task2
(task_id, usr_id_owner, status, crashed, active,
name, last_exec_time, class, settings)
VALUES
(null, 0, "stopped", 0, :active,
:name, "0000/00/00 00:00:00", :class, :settings)';
$domdoc = new DOMDocument();
if ($settings && ! $domdoc->loadXML($settings)) {
throw new Exception('Invalid settings');
} elseif (! $settings) {
$settings = static::getDefaultSettings($dependencyContainer['phraseanet.configuration']);
}
$params = array(
':active' => 1
, ':name' => ''
, ':class' => get_called_class()
, ':settings' => $settings
);
$stmt = $dependencyContainer['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
$tid = $dependencyContainer['phraseanet.appbox']->get_connection()->lastInsertId();
$task = new static($tid, $dependencyContainer, $dependencyContainer['task-manager.logger']);
$task->setTitle($task->getName());
return $task;
}
/**
*
* @return int id of the task
*/
public function getID()
{
return $this->taskid;
}
/**
*
* @param int $done
* @param int $todo
* @return \task_abstract
*/
public function setProgress($done, $todo)
{
$p = ($todo > 0) ? ((100 * $done) / $todo) : -1;
try {
$conn = connection::getPDOConnection($this->dependencyContainer);
$sql = 'UPDATE task2 SET completed = :p WHERE task_id = :taskid';
$stmt = $conn->prepare($sql);
$stmt->execute(array(
':p' => $p,
':taskid' => $this->getID()
));
$stmt->closeCursor();
$this->completed_percentage = $p;
} catch (Exception $e) {
}
return $this;
}
/**
* @param Configuration $config
* @param array $params
* @return string
*/
public static function getDefaultSettings(Configuration $config, array $params = array())
{
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<tasksettings>\n</tasksettings>";
}
}

View File

@@ -1,131 +0,0 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2013 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
*
* @license http://opensource.org/licenses/gpl-3.0 GPLv3
* @link www.phraseanet.com
*/
abstract class task_appboxAbstract extends task_abstract
{
abstract protected function retrieveContent(appbox $appbox);
abstract protected function processOneContent(appbox $appbox, $row);
abstract protected function postProcessOneContent(appbox $appbox, $row);
protected function run2()
{
$this->running = TRUE;
while ($this->running) {
try {
$conn = connection::getPDOConnection($this->dependencyContainer);
} catch (Exception $e) {
$this->log($e->getMessage());
if ($this->getRunner() == self::RUNNER_SCHEDULER) {
$this->log(("Warning : abox connection lost, restarting in 10 min."));
$this->sleep(60 * 10);
// because connection is lost we cannot change status to 'torestart'
// anyway the current status 'running' with no pid
// will enforce the scheduler to restart the task
} else {
// runner = manual : can't restart so simply quit
}
$this->running = FALSE;
return;
}
$this->setLastExecTime();
try {
$sql = 'SELECT settings FROM task2 WHERE task_id = :taskid';
$stmt = $conn->prepare($sql);
$stmt->execute(array(':taskid' => $this->getID()));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
$this->records_done = 0;
$duration = time();
} catch (Exception $e) {
// failed sql, simply return
$this->running = FALSE;
return;
}
if ($row) {
if (! $this->running) {
break;
}
try {
$this->loadSettings(simplexml_load_string($row['settings']));
} catch (Exception $e) {
$this->log($e->getMessage());
continue;
}
$process_ret = $this->process($this->dependencyContainer['phraseanet.appbox']);
switch ($process_ret) {
case self::STATE_MAXMEGSREACHED:
case self::STATE_MAXRECSDONE:
if ($this->getRunner() == self::RUNNER_SCHEDULER) {
$this->setState(self::STATE_TORESTART);
$this->running = FALSE;
}
break;
case self::STATE_TOSTOP:
$this->setState(self::STATE_TOSTOP);
$this->running = FALSE;
break;
case self::STATE_TODELETE: // formal 'suicidable'
$this->setState(self::STATE_TODELETE);
$this->running = FALSE;
break;
}
} // if(row)
$this->incrementLoops();
if ($this->running) {
$this->pause($duration);
}
} // while running
return;
}
/**
*
* @return <type>
*/
protected function process(appbox $appbox)
{
$ret = self::STATE_OK;
try {
// get the records to process
$rs = $this->retrieveContent($appbox);
// process the records
$ret = $this->processLoop($appbox, $rs);
} catch (Exception $e) {
$this->log('Error : ' . $e->getMessage());
}
return $ret;
}
}

View File

@@ -1,173 +0,0 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2013 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
*
* @license http://opensource.org/licenses/gpl-3.0 GPLv3
* @link www.phraseanet.com
*/
abstract class task_databoxAbstract extends task_abstract
{
protected $mono_sbas_id;
abstract protected function retrieveSbasContent(databox $databox);
abstract protected function processOneContent(databox $databox, $row);
abstract protected function flushRecordsSbas();
abstract protected function postProcessOneContent(databox $databox, $row);
protected function run2()
{
$task_must_delete = FALSE; // if the task must be deleted (suicide) after run
$this->running = TRUE;
while ($this->running) {
try {
$conn = connection::getPDOConnection($this->dependencyContainer);
} catch (PDOException $e) {
$this->log($e->getMessage(), self::LOG_ERROR );
if ($this->getRunner() == self::RUNNER_SCHEDULER) {
$this->log("appbox connection lost, restarting in 10 min.", self::LOG_ERROR);
$this->sleep(60 * 10);
// because connection is lost we cannot change status to 'torestart'
// anyway the current status 'running' with no pid
// will enforce the scheduler to restart the task
} else {
// runner = manual : can't restart so simply quit
$this->log("appbox connection lost, quit.", self::LOG_ERROR);
}
$this->running = FALSE;
return;
}
$this->setLastExecTime();
try {
if ($this->mono_sbas_id) {
$sql = 'SELECT sbas_id, task2.* FROM sbas, task2 WHERE task_id=:taskid AND sbas_id=:sbas_id';
$stmt = $conn->prepare($sql);
$stmt->execute(array(':taskid' => $this->getID(), ':sbas_id' => $this->mono_sbas_id));
} else {
$sql = 'SELECT sbas_id, task2.* FROM sbas, task2 WHERE task_id = :taskid';
$stmt = $conn->prepare($sql);
$stmt->execute(array(':taskid' => $this->getID()));
}
$rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
$this->records_done = 0;
$duration = time();
} catch (Exception $e) {
// failed sql, simply return
$this->running = FALSE;
return;
}
foreach ($rs as $row) { // every sbas
if (! $this->running) {
break;
}
$this->sbas_id = (int) $row['sbas_id'];
$this->log(sprintf('This task works now on sbasid=%s ', $this->sbas_id), self::LOG_INFO);
try {
// get the records to process
$databox = $this->dependencyContainer['phraseanet.appbox']->get_databox((int) $row['sbas_id']);
} catch (Exception $e) {
$this->log(sprintf('can\'t connect to sbas(%s) because "%s"',
$row['sbas_id'],
$e->getMessage()),
self::LOG_WARNING
);
continue;
}
try {
$this->loadSettings(simplexml_load_string($row['settings']));
} catch (Exception $e) {
$this->log(sprintf('can\'t get get settings of task because "%s"',
$e->getMessage()),
self::LOG_WARNING
);
continue;
}
$process_ret = $this->processSbas($databox);
// close the cnx to the dbox
$connbas = $databox->get_connection();
if ($connbas instanceof PDO) {
$connbas->close();
unset($connbas);
}
switch ($process_ret) {
case self::STATE_MAXMEGSREACHED:
case self::STATE_MAXRECSDONE:
if ($this->getRunner() == self::RUNNER_SCHEDULER) {
$this->setState(self::STATE_TORESTART);
$this->running = FALSE;
}
break;
case self::STATE_TOSTOP:
$this->setState(self::STATE_TOSTOP);
$this->running = FALSE;
break;
case self::STATE_TODELETE: // formal 'suicidable'
// DO NOT SUICIDE IN THE LOOP, may have to work on other sbas !!!
$task_must_delete = TRUE;
break;
}
$this->flushRecordsSbas();
}
$this->incrementLoops();
if ($this->running) {
$this->pause($duration);
}
}
if ($task_must_delete) {
$this->setState(self::STATE_TODELETE);
$this->log('task will self delete', self::LOG_INFO);
}
return;
}
/**
*
* @return <type>
*/
protected function processSbas(databox $databox)
{
$ret = self::STATE_OK;
try {
// get the records to process
$rs = $this->retrieveSbasContent($databox);
// process the records
$ret = $this->processLoop($databox, $rs);
} catch (Exception $e) {
$this->log($e->getMessage(), self::LOG_ERROR);
}
return $ret;
}
}

View File

@@ -1,267 +0,0 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2013 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Monolog\Logger;
use Alchemy\Phrasea\Application;
use Symfony\Component\Process\ProcessBuilder;
use Symfony\Component\Process\PhpExecutableFinder;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Finder\Finder;
/**
*
* @license http://opensource.org/licenses/gpl-3.0 GPLv3
* @link www.phraseanet.com
*/
class task_manager
{
const STATE_STOPPED = 'stopped';
const STATE_STOPPING = 'stopping';
const STATE_STARTED = 'started';
const STATE_TOSTOP = 'tostop';
protected $app;
protected $logger;
protected $tasks;
public function __construct(Application $app, Logger $logger)
{
$this->app = $app;
$this->logger = $logger;
return $this;
}
public function getLogger()
{
return $this->logger;
}
/**
* status of scheduler and tasks
* used do refresh the taskmanager page
*
* @return array
*/
public function toArray()
{
$ret = array(
'time' => date("H:i:s"),
'scheduler' => $this->getSchedulerState(),
'tasks' => array()
);
foreach ($this->getTasks(true) as $task) {
if ($task->getState() == self::STATE_TOSTOP && $task->getPID() === NULL) {
// fix
$task->setState(self::STATE_STOPPED);
}
$id = $task->getID();
$ret['tasks'][$id] = array(
'id' => $id,
'pid' => $task->getPID(),
'crashed' => $task->getCrashCounter(),
'completed' => $task->getCompletedPercentage(),
'status' => $task->getState()
);
}
return $ret;
}
public function getTasks($refresh = false)
{
if ($this->tasks && !$refresh) {
return $this->tasks;
}
$sql = "SELECT task2.* FROM task2 ORDER BY task_id ASC";
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute();
$rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
$tasks = array();
foreach ($rs as $row) {
$row['pid'] = NULL;
$classname = $row['class'];
if (!class_exists($classname)) {
if(substr($classname, 0, 12) == "task_period_") {
$classfile = substr($classname, 12) . ".php";
@require_once(__DIR__ . "/../../../config/classes/task/period/" . $classfile);
}
}
if (!class_exists($classname)) {
continue;
}
try {
$tasks[$row['task_id']] = new $classname($row['task_id'], $this->app, $this->logger);
} catch (Exception $e) {
}
}
$this->tasks = $tasks;
return $this->tasks;
}
/**
*
* @param int $task_id
* @return task_abstract
*/
public function getTask($task_id)
{
$tasks = $this->getTasks(false);
if (!isset($tasks[$task_id])) {
throw new NotFoundHttpException('Unknown task_id ' . $task_id);
}
return $tasks[$task_id];
}
public function getSchedulerProcess()
{
//prevent scheduler to fail if GV_cli is not provided
if (isset($this->app['phraseanet.configuration']['binaries']['php_binary'])) {
$php = $this->app['phraseanet.configuration']['binaries']['php_binary'];
} else {
$finder = new PhpExecutableFinder();
$php = $finder->find();
}
$builder = ProcessBuilder::create(array($php));
if ($this->app['phraseanet.registry']->get('GV_PHP_INI')) {
$builder->add('-c')->add($this->app['phraseanet.registry']->get('GV_PHP_INI'));
}
return $builder
->add('-f')
->add($this->app['root.path'] . "/bin/console")
->add('scheduler:start')
->getProcess();
}
public function setSchedulerState($status)
{
$av_status = array(
self::STATE_STARTED,
self::STATE_STOPPED,
self::STATE_STOPPING,
self::STATE_TOSTOP
);
if (!in_array($status, $av_status))
throw new Exception(sprintf('unknown status `%s` ', $status));
if ($status == self::STATE_TOSTOP && function_exists('posix_kill')) {
$gs = $this->getSchedulerState();
if ($gs['pid'] !== NULL) {
@posix_kill($gs['pid'], 2); // 2 = SIGINT
}
}
$sql = "UPDATE sitepreff SET schedstatus = :schedstatus, schedqtime=NOW()";
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute(array(':schedstatus' => $status));
$stmt->closeCursor();
return $this;
}
public function getSchedulerState()
{
$sql = "SELECT UNIX_TIMESTAMP()-UNIX_TIMESTAMP(schedqtime) AS qdelay
, schedqtime AS updated_on
, schedstatus AS status FROM sitepreff";
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute();
$ret = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
$pid = NULL;
$lockdir = $this->app['root.path'] . '/tmp/locks/';
if (($schedlock = fopen($lockdir . 'scheduler.lock', 'a+')) != FALSE) {
if (flock($schedlock, LOCK_EX | LOCK_NB) === FALSE) {
// already locked : running !
$pid = trim(fgets($schedlock, 512));
} else {
// can lock : not running
flock($schedlock, LOCK_UN);
}
fclose($schedlock);
}
if ($ret['updated_on'] == '0000-00-00 00:00:00') {
$ret['updated_on'] = null;
} else {
$ret['updated_on'] = new \DateTime($ret['updated_on']);
}
if ($pid === NULL && $ret['status'] !== 'stopped') {
// auto fix
$this->app['phraseanet.appbox']->get_connection()->exec('UPDATE sitepreff SET schedstatus=\'stopped\'');
$ret['status'] = 'stopped';
}
$ret['pid'] = $pid;
return $ret;
}
/**
* Returns true if Pcntl posix supported is enabled, false otherwise
*
* @return Boolean
*/
public static function isPosixPcntlSupported()
{
return extension_loaded('pcntl') && extension_loaded('posix');
}
public static function getAvailableTasks()
{
$dirs = array(__DIR__ . "/period");
if (is_dir($configDir = __DIR__ . "/../../../config/classes/task/period")) {
$dirs[] = $configDir;
}
$tasks = array();
$finder = new Finder();
foreach ($finder->files()->in($dirs)->name("*.php") as $file) {
$classname = 'task_period_' . $file->getBasename('.php');
try {
if (!class_exists($classname)) {
@require_once($file->getRealPath());
}
if (class_exists($classname) && $classname::interfaceAvailable()) {
$tasks[] = array(
"class" => $classname,
"name" => $classname::getName(),
"err" => null
);
}
} catch (Exception $e) {
}
}
return $tasks;
}
}

View File

@@ -1,878 +0,0 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2013 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Alchemy\Phrasea\Core\Configuration\Configuration;
class task_period_RecordMover extends task_appboxAbstract
{
/**
*
* @return string
*/
public static function getName()
{
return _("Record Mover");
}
/**
*
* @return string
*/
public static function help()
{
return '';
}
/**
* return the xml (text) version of the form filled by the gui
*
* @param string $oldxml
* @return string
*/
public function graphic2xml($oldxml)
{
$request = http_request::getInstance();
$parm2 = $request->get_parms('period', 'logsql');
$dom = new DOMDocument();
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
if ($dom->loadXML($oldxml)) {
foreach (array(
'str:period'
, 'boo:logsql'
) as $pname) {
$ptype = substr($pname, 0, 3);
$pname = substr($pname, 4);
$pvalue = $parm2[$pname];
if (($ns = $dom->getElementsByTagName($pname)->item(0))) {
// field did exists, remove thevalue
while (($n = $ns->firstChild))
$ns->removeChild($n);
} else {
// field did not exists, create it
$ns = $dom->documentElement->appendChild($dom->createElement($pname));
}
// set the value
switch ($ptype) {
case "str":
case "pop":
$ns->appendChild($dom->createTextNode($pvalue));
break;
case "boo":
$ns->appendChild($dom->createTextNode($pvalue ? '1' : '0'));
break;
}
}
}
return $dom->saveXML();
}
/**
* PRINT head for the gui
*/
public function printInterfaceHEAD()
{
?>
<style>
OPTION.jsFilled
{
padding-left:10px;
padding-right:20px;
}
#OUTOFDATETAB TD
{
text-align:center;
}
DIV.terminal
{
margin:5px;
border:1px #000000 solid;
font-family:monospace;
font-size:13px;
text-align:left;
color:#00FF00;
background-color:#182018
}
DIV.terminal DIV.title
{
color:#303830;
background-color:#00C000;
padding:2px;
}
DIV.terminal DIV.sql
{
padding:5px;
}
DIV.terminal DIV.sqltest
{
padding-left:45px;
padding-right:25px;
}
SPAN.active
{
font-weight: bold;
background-color: #000000;
color:#00FF00;
}
SPAN.notactive
{
font-weight: bold;
background-color: #000000;
color:#FF0000;
}
</style>
<?php
}
/**
* PRINT js of the gui
*/
public function printInterfaceJS()
{
?>
<script type="text/javascript">
function taskFillGraphic_<?php echo(get_class($this));?>(xml)
{
$("#sqlu").text("");
$("#sqls").text("");
if (xml) {
xml2 = $.parseXML(xml);
xml2 = $(xml2);
with(document.forms['graphicForm'])
{
period.value = xml2.find("period").text();
logsql.checked = Number(xml2.find("logsql").text()) > 0;
}
var data = {};
data["ACT"] = "CALCTEST";
data["taskid"]=<?php echo $this->getID(); ?>;
data["cls"]="RecordMover";
data["xml"] = xml;
$.ajax({ url: "/admin/task-manager/task/<?php echo $this->getID(); ?>/facility/"
, data: data
, dataType:'json'
, type:"POST"
, async:true
, success:function(data) {
t = "";
for (i in data.tasks) {
t += "<div class=\"title\">&nbsp;";
if(data.tasks[i].active)
t += "<span class=\"active\">&nbsp;X&nbsp;</span>&nbsp;";
else
t += "<span class=\"notactive\">&nbsp;X&nbsp;</span>&nbsp;";
if(data.tasks[i].name_htmlencoded)
t += "<b>" + data.tasks[i].name_htmlencoded + "</b>";
else
t += "<b><i>sans nom</i></b>";
if(data.tasks[i].basename_htmlencoded)
t += " (action=" + data.tasks[i].action + ' on ' + data.tasks[i].basename_htmlencoded + ')';
else
t += " (action=" + data.tasks[i].action + ' on <i>Unknown</i>)';
t += "</div>";
if(data.tasks[i].err_htmlencoded) ;
t += "<div class=\"err\">" + data.tasks[i].err_htmlencoded + "</div>";
t += "<div class=\"sql\">";
if(data.tasks[i].sql && data.tasks[i].sql.test.sql_htmlencoded)
t += "<div class=\"sqltest\">" + data.tasks[i].sql.test.sql_htmlencoded + "</div>";
t += "--&gt; <span id=\"SQLRET"+i+"\"><i>wait...</i></span><br/>";
t += "</div>";
}
$("#sqla").html(t);
var data = {};
data["ACT"] = "PLAYTEST";
data["taskid"]=<?php echo $this->getID(); ?>;
data["cls"]="RecordMover";
data["xml"] = xml;
$.ajax({ url: "/admin/task-manager/task/<?php echo $this->getID(); ?>/facility/"
, data: data
, dataType:'json'
, type:"POST"
, async:true
, success:function(data) {
for (i in data.tasks) {
if (data.tasks[i].sql) {
if (data.tasks[i].sql.test.err) {
$("#SQLRET"+i).html("err: " + data.tasks[i].sql.test.err);
} else {
t = '';
for(j in data.tasks[i].sql.test.result.rids)
t += (t?', ':'') + data.tasks[i].sql.test.result.rids[j];
if(data.tasks[i].sql.test.result.rids.length < data.tasks[i].sql.test.result.n)
t += ', ...';
$("#SQLRET"+i).html("n=" + data.tasks[i].sql.test.result.n + ", rids:(" + t + ")");
}
} else {
$("#SQLRET"+i).html("");
}
}
}
});
}
});
}
}
$(document).ready(
function(){
(function( $ ){
$.fn.serializeJSON=function() {
var json = {};
jQuery.map($(this).serializeArray(), function(n, i){
json[n['name']] = n['value'];
});
return json;
};
})( jQuery );
var limits = {
'period':{'min':<?php echo self::MINPERIOD; ?>, 'max':<?php echo self::MAXPERIOD; ?>},
'delay':{min:0}
} ;
$(".formElem").change(function(){
fieldname = $(this).attr("name");
switch ((this.nodeName+$(this).attr("type")).toLowerCase()) {
case "inputtext":
if (typeof(limits[fieldname])!='undefined') {
var v = 0|this.value;
if(v < limits[fieldname].min)
v = limits[fieldname].min;
else if(v > limits[fieldname].max)
v = limits[fieldname].max;
this.value = v;
}
break;
}
setDirty();
});
}
);
</script>
<?php
}
/**
* RETURN the html gui
*
* @return string
*/
public function getInterfaceHTML()
{
ob_start();
?>
<form class="form-horizontal" name="graphicForm" onsubmit="return(false);" method="post">
<div class="control-group">
<label class="control-label"><?php echo _('task::_common_:periodicite de la tache') ?></label>
<div class="controls">
<input class="formElem input-small" type="text" name="period" value="" />
<span class="help-inline"><?php echo _('task::_common_:secondes (unite temporelle)') ?></span>
</div>
</div>
<div class="control-group">
<div class="controls">
<label class="checkbox">
<input class="formElem" type="checkbox" name="logsql" /><?php echo _('Log changes') ?>
</label>
</div>
</div>
</form>
<center>
<div class="terminal" id="sqla"></div>
</center>
<?php
return ob_get_clean();
}
/**
*
* retrieveContent & processOneContent : work done by the task
*/
private $sxTaskSettings = null; // settings in simplexml
/**
* return array of records to work on, from sql generated by 'from' clause
*
* @param appbox $appbox
* @return array()
*/
protected function retrieveContent(appbox $appbox)
{
$this->maxrecs = 1000;
$this->sxTaskSettings = simplexml_load_string($this->getSettings());
if (false === $this->sxTaskSettings || !$this->sxTaskSettings->tasks) {
return array();
}
$ret = array();
$logsql = (int) ($this->sxTaskSettings->logsql) > 0;
foreach ($this->sxTaskSettings->tasks->task as $sxtask) {
if (!$this->running) {
break;
}
$task = $this->calcSQL($sxtask);
if (!$task['active']) {
continue;
}
if ($logsql) {
$this->log(sprintf("playing task '%s' on base '%s'"
, $task['name']
, $task['basename'] ? $task['basename'] : '<unknown>'), self::LOG_INFO);
}
try {
$connbas = connection::getPDOConnection($this->dependencyContainer, $task['sbas_id']);
} catch (Exception $e) {
$this->log(sprintf("can't connect sbas %s", $task['sbas_id']), self::LOG_ERROR);
continue;
}
$stmt = $connbas->prepare($task['sql']['real']['sql']);
if ($stmt->execute(array())) {
while ($this->running && ($row = $stmt->fetch(PDO::FETCH_ASSOC)) !== FALSE) {
$tmp = array('sbas_id' => $task['sbas_id'], 'record_id' => $row['record_id'], 'action' => $task['action']);
$rec = new record_adapter($this->dependencyContainer, $task['sbas_id'], $row['record_id']);
switch ($task['action']) {
case 'UPDATE':
// change collection ?
if (($x = (int) ($sxtask->to->coll['id'])) > 0) {
$tmp['coll'] = $x;
}
// change sb ?
if (($x = $sxtask->to->status['mask'])) {
$tmp['sb'] = $x;
}
$ret[] = $tmp;
break;
case 'DELETE':
$tmp['deletechildren'] = false;
if ($sxtask['deletechildren'] && $rec->is_grouping()) {
$tmp['deletechildren'] = true;
}
$ret[] = $tmp;
break;
}
}
$stmt->closeCursor();
}
}
return $ret;
}
/**
* work on ONE record
*
* @param appbox $appbox
* @param array $row
* @return \task_period_RecordMover
*/
protected function processOneContent(appbox $appbox, $row)
{
$logsql = (int) ($this->sxTaskSettings->logsql) > 0;
$databox = $this->dependencyContainer['phraseanet.appbox']->get_databox($row['sbas_id']);
$rec = new record_adapter($this->dependencyContainer, $row['sbas_id'], $row['record_id']);
switch ($row['action']) {
case 'UPDATE':
// change collection ?
if (array_key_exists('coll', $row)) {
$coll = collection::get_from_coll_id($this->dependencyContainer, $databox, $row['coll']);
$rec->move_to_collection($coll, $this->dependencyContainer['phraseanet.appbox']);
$this['phraseanet.SE']->updateRecord($rec);
if ($logsql) {
$this->log(sprintf("on sbas %s move rid %s to coll %s \n", $row['sbas_id'], $row['record_id'], $coll->get_coll_id()));
}
}
// change sb ?
if (array_key_exists('sb', $row)) {
$status = str_split($rec->get_status());
foreach (str_split(strrev($row['sb'])) as $bit => $val) {
if ($val == '0' || $val == '1') {
$status[31 - $bit] = $val;
}
}
$status = implode('', $status);
$rec->set_binary_status($status);
$this->dependencyContainer['phraseanet.SE']->updateRecord($rec);
if ($logsql) {
$this->log(sprintf("on sbas %s set rid %s status to %s \n", $row['sbas_id'], $row['record_id'], $status));
}
}
break;
case 'DELETE':
if ($row['deletechildren'] && $rec->is_grouping()) {
foreach ($rec->get_children() as $child) {
$child->delete();
$this->dependencyContainer['phraseanet.SE']->removeRecord($child);
if ($logsql) {
$this->log(sprintf("on sbas %s delete (grp child) rid %s \n", $row['sbas_id'], $child->get_record_id()));
}
}
}
$rec->delete();
$this->dependencyContainer['phraseanet.SE']->removeRecord($rec);
if ($logsql) {
$this->log(sprintf("on sbas %s delete rid %s \n", $row['sbas_id'], $rec->get_record_id()));
}
break;
}
return $this;
}
/**
* all work done on processOneContent, so nothing to do here
*
* @param appbox $appbox
* @param array $row
* @return \task_period_RecordMover
*/
protected function postProcessOneContent(appbox $appbox, $row)
{
return $this;
}
/**
* compute sql for a task (<task> entry in settings)
*
* @param simplexml $sxtask
* @param boolean $playTest
* @return array
*/
private function calcSQL($sxtask, $playTest = false)
{
$sbas_id = (int) ($sxtask['sbas_id']);
$ret = array(
'name' => $sxtask['name'] ? (string) $sxtask['name'] : 'sans nom',
'name_htmlencoded' => \p4string::MakeString(($sxtask['name'] ? $sxtask['name'] : 'sans nom'), 'html'),
'active' => trim($sxtask['active']) === '1',
'sbas_id' => $sbas_id,
'basename' => '',
'basename_htmlencoded' => '',
'action' => strtoupper($sxtask['action']),
'sql' => NULL,
'err' => '',
'err_htmlencoded' => '',
);
try {
$dbox = $this->dependencyContainer['phraseanet.appbox']->get_databox($sbas_id);
$ret['basename'] = $dbox->get_label($this->dependencyContainer['locale.I18n']);
$ret['basename_htmlencoded'] = htmlentities($ret['basename']);
switch ($ret['action']) {
case 'UPDATE':
$ret['sql'] = $this->calcUPDATE($sbas_id, $sxtask, $playTest);
break;
case 'DELETE':
$ret['sql'] = $this->calcDELETE($sbas_id, $sxtask, $playTest);
$ret['deletechildren'] = (int) ($sxtask['deletechildren']);
break;
default:
$ret['err'] = "bad action '" . $ret['action'] . "'";
$ret['err_htmlencoded'] = htmlentities($ret['err']);
break;
}
} catch (Exception $e) {
$ret['err'] = "bad sbas '" . $sbas_id . "'";
$ret['err_htmlencoded'] = htmlentities($ret['err']);
}
return $ret;
}
/**
* compute entry for a UPDATE query
*
* @param integer $sbas_id
* @param simplexml $sxtask
* @param boolean $playTest
* @return array
*/
private function calcUPDATE($sbas_id, &$sxtask, $playTest)
{
$tws = array(); // NEGATION of updates, used to build the 'test' sql
//
// set coll_id ?
if (($x = (int) ($sxtask->to->coll['id'])) > 0) {
$tws[] = 'coll_id!=' . $x;
}
// set status ?
$x = $sxtask->to->status['mask'];
$mx = str_replace(' ', '0', ltrim(str_replace(array('0', 'x'), array(' ', ' '), $x)));
$ma = str_replace(' ', '0', ltrim(str_replace(array('x', '0'), array(' ', '1'), $x)));
if ($mx && $ma)
$tws[] = '((status ^ 0b' . $mx . ') & 0b' . $ma . ')!=0';
elseif ($mx)
$tws[] = '(status ^ 0b' . $mx . ')!=0';
elseif ($ma)
$tws[] = '(status & 0b' . $ma . ')!=0';
// compute the 'where' clause
list($tw, $join) = $this->calcWhere($sbas_id, $sxtask);
// ... complete the where to buid the TEST
if (count($tws) == 1)
$tw[] = $tws[0];
elseif (count($tws) > 1)
$tw[] = '(' . implode(') OR (', $tws) . ')';
if (count($tw) == 1)
$where = $tw[0];
if (count($tw) > 1)
$where = '(' . implode(') AND (', $tw) . ')';
// build the TEST sql (select)
$sql_test = 'SELECT record_id FROM record' . $join;
if (count($tw) > 0)
$sql_test .= ' WHERE ' . ((count($tw) == 1) ? $tw[0] : '(' . implode(') AND (', $tw) . ')');
// build the real sql (select)
$sql = 'SELECT record_id FROM record' . $join;
if (count($tw) > 0)
$sql .= ' WHERE ' . ((count($tw) == 1) ? $tw[0] : '(' . implode(') AND (', $tw) . ')');
$ret = array(
'real' => array(
'sql' => $sql,
'sql_htmlencoded' => htmlentities($sql),
),
'test' => array(
'sql' => $sql_test,
'sql_htmlencoded' => htmlentities($sql_test),
'result' => NULL,
'err' => NULL
)
);
if ($playTest) {
$ret['test']['result'] = $this->playTest($sbas_id, $sql_test);
}
return $ret;
}
/**
* compute entry for a DELETE task
*
* @param integer $sbas_id
* @param simplexml $sxtask
* @param boolean $playTest
* @return array
*/
private function calcDELETE($sbas_id, &$sxtask, $playTest)
{
// compute the 'where' clause
list($tw, $join) = $this->calcWhere($sbas_id, $sxtask);
// build the TEST sql (select)
$sql_test = 'SELECT SQL_CALC_FOUND_ROWS record_id FROM record' . $join;
if (count($tw) > 0)
$sql_test .= ' WHERE ' . ((count($tw) == 1) ? $tw[0] : '(' . implode(') AND (', $tw) . ')');
$sql_test .= ' LIMIT 10';
// build the real sql (select)
$sql = 'SELECT record_id FROM record' . $join;
if (count($tw) > 0)
$sql .= ' WHERE ' . ((count($tw) == 1) ? $tw[0] : '(' . implode(') AND (', $tw) . ')');
$ret = array(
'real' => array(
'sql' => $sql,
'sql_htmlencoded' => htmlentities($sql),
),
'test' => array(
'sql' => $sql_test,
'sql_htmlencoded' => htmlentities($sql_test),
'result' => NULL,
'err' => NULL
)
);
if ($playTest) {
$ret['test']['result'] = $this->playTest($sbas_id, $sql_test);
}
return $ret;
}
/**
* compute the 'where' clause
* returns an array of clauses to be joined by 'and'
* and a 'join' to needed tables
*
* @param integer $sbas_id
* @param simplecms $sxtask
* @return array
*/
private function calcWhere($sbas_id, &$sxtask)
{
$connbas = connection::getPDOConnection($this->dependencyContainer, $sbas_id);
$tw = array();
$join = '';
$ijoin = 0;
// criteria <type type="XXX" />
if (($x = $sxtask->from->type['type']) !== NULL) {
switch (strtoupper($x)) {
case 'RECORD':
$tw[] = 'parent_record_id!=record_id';
break;
case 'STORY':
$tw[] = 'parent_record_id=record_id';
break;
}
}
// criteria <text field="XXX" compare="OP" value="ZZZ" />
foreach ($sxtask->from->text as $x) {
$ijoin++;
$comp = strtoupper($x['compare']);
if (in_array($comp, array('<', '>', '<=', '>=', '=', '!='))) {
$s = 'p' . $ijoin . '.name=\'' . $x['field'] . '\' AND p' . $ijoin . '.value' . $comp;
$s .= '' . $connbas->quote($x['value']) . '';
$tw[] = $s;
$join .= ' INNER JOIN prop AS p' . $ijoin . ' USING(record_id)';
} else {
// bad comparison operator
}
}
// criteria <date direction ="XXX" field="YYY" delta="Z" />
foreach ($sxtask->from->date as $x) {
$ijoin++;
$s = 'p' . $ijoin . '.name=\'' . $x['field'] . '\' AND NOW()';
$s .= strtoupper($x['direction']) == 'BEFORE' ? '<' : '>=';
$delta = (int) ($x['delta']);
if ($delta > 0)
$s .= '(p' . $ijoin . '.value+INTERVAL ' . $delta . ' DAY)';
elseif ($delta < 0)
$s .= '(p' . $ijoin . '.value-INTERVAL ' . -$delta . ' DAY)';
else
$s .= 'p' . $ijoin . '.value';
$tw[] = $s;
$join .= ' INNER JOIN prop AS p' . $ijoin . ' USING(record_id)';
}
// criteria <coll compare="OP" id="X,Y,Z" />
if (($x = $sxtask->from->coll) !== NULL) {
$tcoll = explode(',', $x['id']);
foreach ($tcoll as $i => $c)
$tcoll[$i] = (int) $c;
if ($x['compare'] == '=') {
if (count($tcoll) == 1) {
$tw[] = 'coll_id = ' . $tcoll[0];
} else {
$tw[] = 'coll_id IN(' . implode(',', $tcoll) . ')';
}
} elseif ($x['compare'] == '!=') {
if (count($tcoll) == 1) {
$tw[] = 'coll_id != ' . $tcoll[0];
} else {
$tw[] = 'coll_id NOT IN(' . implode(',', $tcoll) . ')';
}
} else {
// bad operator
}
}
// criteria <status mask="XXXXX" />
$x = $sxtask->from->status['mask'];
$mx = str_replace(' ', '0', ltrim(str_replace(array('0', 'x'), array(' ', ' '), $x)));
$ma = str_replace(' ', '0', ltrim(str_replace(array('x', '0'), array(' ', '1'), $x)));
if ($mx && $ma) {
$tw[] = '((status^0b' . $mx . ')&0b' . $ma . ')=0';
} elseif ($mx) {
$tw[] = '(status^0b' . $mx . ')=0';
} elseif ($ma) {
$tw[] = '(status&0b' . $ma . ")=0";
}
if (count($tw) == 1) {
$where = $tw[0];
}
if (count($tw) > 1) {
$where = '(' . implode(') AND (', $tw) . ')';
}
return array($tw, $join);
}
/**
* play a 'test' sql on sbas, return the number of records and the 10 first rids
*
* @param integer $sbas_id
* @param string $sql
* @return array
*/
private function playTest($sbas_id, $sql)
{
$connbas = connection::getPDOConnection($this->dependencyContainer, $sbas_id);
$result = array('rids' => array(), 'err' => '', 'n' => null);
$result['n'] = $connbas->query('SELECT COUNT(*) AS n FROM (' . $sql . ') AS x')->fetchColumn();
$stmt = $connbas->prepare('SELECT record_id FROM (' . $sql . ') AS x LIMIT 10');
if ($stmt->execute(array())) {
while (($row = $stmt->fetch(PDO::FETCH_ASSOC))) {
$result['rids'][] = $row['record_id'];
}
$stmt->closeCursor();
} else {
$result['err'] = $connbas->last_error();
}
return $result;
}
/**
* facility called by xhttp/jquery from interface for ex. when switching interface from gui<->xml
*/
public function facility()
{
$request = http_request::getInstance();
$parm2 = $request->get_parms(
'ACT', 'xml'
);
$ret = array('tasks' => array());
switch ($parm2['ACT']) {
case 'CALCTEST':
$sxml = simplexml_load_string($parm2['xml']);
if (isset($sxml->tasks->task)) {
foreach ($sxml->tasks->task as $sxtask) {
$ret['tasks'][] = $this->calcSQL($sxtask, false);
}
}
break;
case 'PLAYTEST':
$sxml = simplexml_load_string($parm2['xml']);
if (isset($sxml->tasks->task)) {
foreach ($sxml->tasks->task as $sxtask) {
$ret['tasks'][] = $this->calcSQL($sxtask, true);
}
}
break;
case 'CALCSQL':
$xml = $this->graphic2xml('<?xml version="1.0" encoding="UTF-8"?><tasksettings/>');
$sxml = simplexml_load_string($xml);
if (isset($sxml->tasks->task)) {
foreach ($sxml->tasks->task as $sxtask) {
$ret['tasks'][] = $this->calcSQL($sxtask, false);
}
}
break;
}
return json_encode($ret);
}
/**
* @param array $params
*/
public static function getDefaultSettings(Configuration $config, array $params = array())
{
$period = isset($params['period']) ? $params['period'] : self::MINPERIOD;
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<tasksettings>
<period>". min(max($period, self::MINPERIOD), self::MAXPERIOD) ."</period>
<logsql>0</logsql>
<!--
<tasks>
//Maintain offline (sb4 = 1) all docs under copyright
<task active=\"1\" name=\"confidentiel\" action=\"update\" sbas_id=\"1\">
<from>
<date direction=\"before\" field=\"FIN_COPYRIGHT\"/>
</from>
<to>
<status mask=\"x1xxxx\"/>
</to>
</task>
//Put online (sb4 = 0) all docs from 'public' collection and between the copyright date and the date of filing
<task active=\"1\" name=\"visible\" action=\"update\" sbas_id=\"1\">
<from>
<coll compare=\"=\" id=\"5\"/>
<date direction=\"after\" field=\"FIN_COPYRIGHT\"/>
<date direction=\"before\" field=\"ARCHIVAGE\"/>
</from>
<to>
<status mask=\"x0xxxx\"/>
</to>
</task>
// Warn 10 days before archiving (raise sb5)
<task active=\"1\" name=\"bientôt la fin\" action=\"update\" sbas_id=\"1\">
<from>
<coll compare=\"=\" id=\"5\"/>
<date direction=\"after\" field=\"ARCHIVAGE\" delta=\"-10\"/>
</from>
<to>
<status mask=\"1xxxxx\"/>
</to>
</task>
//Move to 'archive' collection
<task active=\"1\" name=\"archivage\" action=\"update\" sbas_id=\"1\">
<from>
<coll compare=\"=\" id=\"5\"/>
<date direction=\"after\" field=\"ARCHIVAGE\" />
</from>
<to>
<status mask=\"00xxxx\"/> on nettoie les status pour la forme
<coll id=\"666\" />
</to>
</task>
//Purge the archived documents from one year that are in the 'archive' collection
<task active=\"1\" name=\"archivage\" action=\"delete\" sbas_id=\"1\">
<from>
<coll compare=\"=\" id=\"666\"/>
<date direction=\"after\" field=\"ARCHIVAGE\" delta=\"+365\" />
</from>
</task>
</tasks>
-->
</tasksettings>";
}
}

View File

@@ -1,178 +0,0 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2013 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class task_period_apibridge extends task_appboxAbstract
{
/**
* Return the name of the task
* @return string
*/
public static function getName()
{
return 'API bridge uploader';
}
/**
* Get help
* @return string
*/
public static function help()
{
return '';
}
/**
*
* @param appbox $appbox
* @return Array
*/
protected function retrieveContent(appbox $appbox)
{
$status = array(Bridge_Element::STATUS_PENDING, Bridge_Element::STATUS_PROCESSING, Bridge_Element::STATUS_PROCESSING_SERVER);
$params = array();
$n = 1;
foreach ($status as $stat) {
$params[':status' . $n] = $stat;
$n ++;
}
$sql = 'SELECT id, account_id FROM bridge_elements'
. ' WHERE (status = ' . implode(' OR status = ', array_keys($params)) . ')';
$stmt = $appbox->get_connection()->prepare($sql);
$stmt->execute($params);
$rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
return $rs;
}
/**
*
* @param appbox $appbox
* @param array $row
* @return task_period_apibridge
*/
protected function processOneContent(appbox $appbox, $row)
{
try {
$account = Bridge_Account::load_account($this->dependencyContainer, $row['account_id']);
$element = new Bridge_Element($this->dependencyContainer, $account, $row['id']);
$this->log("process " . $element->get_id() . " with status " . $element->get_status());
if ($element->get_status() == Bridge_Element::STATUS_PENDING) {
$this->upload_element($element);
} else {
$this->update_element($element);
}
} catch (Exception $e) {
$sql = 'UPDATE bridge_elements SET status = :status WHERE id = :id';
$params = array(
':status' => Bridge_Element::STATUS_ERROR
, ':id' => $row['id']
);
$stmt = $appbox->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
}
return $this;
}
/**
*
* @param appbox $appbox
* @param array $row
* @return task_period_apibridge
*/
protected function postProcessOneContent(appbox $appbox, $row)
{
return $this;
}
/**
*
* @param Bridge_Element $element
* @return task_period_apibridge
*/
private function upload_element(Bridge_Element $element)
{
$account = $element->get_account();
$element->set_status(Bridge_Element::STATUS_PROCESSING);
$dist_id = null;
try {
$dist_id = $account->get_api()->upload($element->get_record(), $element->get_datas());
$element->set_uploaded_on(new DateTime());
} catch (Exception $e) {
$this->log('Error while uploading : ' . $e->getMessage());
$element->set_status(Bridge_Element::STATUS_ERROR);
}
$element->set_dist_id($dist_id);
return $this;
}
/**
*
* @param Bridge_Element $element
* @return task_period_apibridge
*/
protected function update_element(Bridge_Element $element)
{
$account = $element->get_account();
$connector_status = $account->get_api()->get_element_status($element);
$status = $element->get_account()->get_api()->map_connector_to_element_status($connector_status);
$error_message = $element->get_account()->get_api()->get_error_message_from_status($connector_status);
$previous_status = $element->get_status();
if ($status) {
$element->set_status($status);
$this->log('updating status for : ' . $element->get_id() . " to " . $status);
}
$element->set_connector_status($connector_status);
if ($status === $previous_status) {
return;
}
switch ($status) {
case Bridge_Element::STATUS_ERROR:
$params = array(
'usr_id' => $account->get_user()->get_id()
, 'reason' => $error_message
, 'account_id' => $account->get_id()
, 'sbas_id' => $element->get_record()->get_sbas_id()
, 'record_id' => $element->get_record()->get_record_id()
);
$this->dependencyContainer['events-manager']->trigger('__BRIDGE_UPLOAD_FAIL__', $params);
break;
default:
case Bridge_Element::STATUS_DONE:
case Bridge_Element::STATUS_PENDING:
case Bridge_Element::STATUS_PROCESSING_SERVER:
case Bridge_Element::STATUS_PROCESSING:
break;
}
return $this;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,656 +0,0 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2013 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Alchemy\Phrasea\Core\Configuration\Configuration;
use Symfony\Component\Process\ExecutableFinder;
class task_period_cindexer extends task_abstract
{
// how to execute indexer (choose in 'run2' method)
private $method;
const METHOD_FORK = 'METHOD_FORK';
const METHOD_EXEC = 'METHOD_EXEC';
const METHOD_PROC_OPEN = 'METHOD_PROC_OPEN';
const ERR_EXECUTABLE_NOT_FOUND = 2; // aka ENOENT (No such file or directory)
const ERR_CRASHED = 14; // aka EFAULT (Bad address)
const ERR_CANT_FORK = 3; // aka ESRCH (No such process)
/**
*
* @var string
*/
protected $host;
/**
*
* @var int
*/
protected $port;
/**
*
* @var string
*/
protected $base;
/**
*
* @var string
*/
protected $user;
/**
*
* @var string
*/
protected $password;
/**
*
* @var int
*/
protected $socket;
/**
*
* @var string
*/
protected $charset;
/**
*
* @var string
*/
protected $debugmask;
/**
*
* @var string
*/
protected $stem;
/**
*
* @var string
*/
protected $sortempty;
/**
*
* @var string
*/
protected $nolog;
/**
*
* @var string
*/
protected $winsvc_run;
/**
*
* @return string
*/
public static function getName()
{
return(_("Indexation task"));
}
/**
*
* @return string
*/
public static function help()
{
return(_("This task is used to index records for Phrasea engine."));
}
/**
*
* @param string $oldxml
* @return string
*/
public function graphic2xml($oldxml)
{
$request = http_request::getInstance();
$parm2 = $request->get_parms(
'host', 'port', 'base', 'user', 'password', 'socket', 'nolog', 'clng', 'winsvc_run', 'charset', 'debugmask', 'stem', 'sortempty'
);
$dom = new DOMDocument();
$dom->formatOutput = true;
if ($dom->loadXML($oldxml)) {
foreach (array("str:host", "str:port", "str:base", "str:user", "str:password", "str:socket", "boo:nolog", "str:clng", "boo:winsvc_run", "str:charset", 'str:debugmask', 'str:stem', 'str:sortempty') as $pname) {
$ptype = substr($pname, 0, 3);
$pname = substr($pname, 4);
$pvalue = $parm2[$pname];
if (($ns = $dom->getElementsByTagName($pname)->item(0)) != NULL) {
// 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;
}
}
}
return($dom->saveXML());
}
/**
*
* @return void
*/
public function printInterfaceJS()
{
?>
<script type="text/javascript">
function taskFillGraphic_<?php echo(get_class($this));?>(xml)
{
if (xml) {
xml = $.parseXML(xml);
xml = $(xml);
var isyes = function(v) {
v = v.toUpperCase().trim();
return v=='O' || v=='Y' || v=='OUI' || v=='YES' || v=='1';
}
with(document.forms['graphicForm'])
{
host.value = xml.find("host").text();
port.value = xml.find("port").text();
base.value = xml.find("base").text();
user.value = xml.find("user").text();
socket.value = xml.find("socket").text();
password.value = xml.find("password").text();
clng.value = xml.find("clng").text();
nolog.checked = isyes(xml.find("nolog").text());
winsvc_run.checked = isyes(xml.find("winsvc_run").text());
charset.value = xml.find("charset").text();
stem.value = xml.find("stem").text();
sortempty.value = xml.find("sortempty").text();
debugmask.value = 0|xml.find("debugmask").text();
}
}
var cmd = '';
with(document.forms['graphicForm'])
{
cmd += "<?php echo $this->getIndexer() ?>";
if(host.value)
cmd += " -h=" + host.value;
if(port.value)
cmd += " -P=" + port.value;
if(base.value)
cmd += " -b=" + base.value;
if(user.value)
cmd += " -u=" + user.value;
if(password.value)
cmd += " -p=xxxxxx"; // + password.value;
if(socket.value)
cmd += " --socket=" + socket.value;
if(charset.value)
cmd += " --default-character-set=" + charset.value;
cmd += " -o";
if(nolog.checked)
cmd += " -n";
if(clng.value)
cmd += " -c=" + clng.value;
if(stem.value)
cmd += " --stem=" + stem.value;
if(sortempty.value)
cmd += " --sort-empty=" + sortempty.value;
if(debugmask.value)
cmd += " -d=" + debugmask.value;
if(winsvc_run.checked)
cmd += " --run";
}
$('#cmd').html(cmd);
}
$(document).ready(function(){
$("#graphicForm *").change(function(){
taskFillGraphic_<?php echo(get_class($this));?>(null);
});
});
</script>
<?php
return;
}
/**
*
* @return return
*/
public function getInterfaceHTML()
{
ob_start();
?>
<form id="graphicForm" name="graphicForm" class="form-horizontal" onsubmit="return(false);" method="post">
<div class="control-group">
<label class="control-label"><?php echo _('MySQL Host') ?></label>
<div class="controls">
<input type="text" name="host" value="">
</div>
<label class="control-label"><?php echo _('MySQL Port') ?></label>
<div class="controls">
<input type="text" name="port" value="">
</div>
<label class="control-label"><?php echo _('MySQL Database') ?></label>
<div class="controls">
<input type="text" name="base" value="">
</div>
<label class="control-label"><?php echo _('MySQL Login') ?></label>
<div class="controls">
<input type="text" name="user" value="">
</div>
<label class="control-label"><?php echo _('MySQL password') ?></label>
<div class="controls">
<input type="password" name="password" value="">
</div>
<label class="control-label"><?php echo _('MySQL connection charset') ?></label>
<div class="controls">
<input type="text" name="charset" class="input-small" value="">
</div>
</div>
<div class="control-group">
<label class="control-label"><?php echo _('Socket port') ?></label>
<div class="controls">
<input type="text" name="socket" class="input-small" value="">
</div>
</div>
<div class="control-group">
<label class="control-label"><?php echo _('Debug binary mask') ?></label>
<div class="controls">
<input type="text" name="debugmask" class="input-small" value="">
</div>
</div>
<div class="control-group">
<label class="control-label"><?php echo _('Default language for thesaurus candidates') ?></label>
<div class="controls">
<input type="text" name="clng" class="input-small" value="">
</div>
</div>
<div class="control-group">
<label class="control-label"><?php echo _('Enable stemming languages') ?></label>
<div class="controls">
<input type="text" name="stem" class="input-small" value="">
<span class="help-inline"><?php echo _('example : fr,en') ?></span>
</div>
</div>
<div class="control-group">
<label class="control-label"><?php echo _('Sort records with an empty field') ?></label>
<div class="controls">
<select name="sortempty">
<option value=""><?php echo _('Hide records') ?></option>
<option value="A"><?php echo _('At the beginning') ?></option>
<option value="Z"><?php echo _('At the end') ?></option>
</select>
</div>
</div>
<div class="control-group">
<div class="controls">
<label class="checkbox">
<input type="checkbox" name="nolog">
<?php echo _('Do not log, output to console') ?>
</label>
</div>
</div>
<div class="control-group">
<div class="controls">
<label class="checkbox">
<input type="checkbox" name="winsvc_run">
<?php echo _('Run as application, not as service') ?>
<span class="help-inline">(<?php echo _('Windows specific') ?>)</span>
</label>
</div>
</div>
</form>
<center>
<div style="margin:10px; padding:5px; border:1px #000000 solid; font-family:monospace; font-size:14px; text-align:left; color:#00e000; background-color:#404040" id="cmd">cmd</div>
</center>
<?php
return ob_get_clean();
}
/**
*
* @param SimpleXMLElement $sx_task_settings
* @return task_cindexer
*/
protected function loadSettings(SimpleXMLElement $sx_task_settings)
{
$this->host = trim($sx_task_settings->host);
$this->port = trim($sx_task_settings->port);
$this->base = trim($sx_task_settings->base);
$this->user = trim($sx_task_settings->user);
$this->password = trim($sx_task_settings->password);
$this->socket = trim($sx_task_settings->socket);
$this->charset = trim($sx_task_settings->charset);
$this->stem = trim($sx_task_settings->stem);
$this->sortempty = trim($sx_task_settings->sortempty);
$this->debugmask = (int) (trim($sx_task_settings->debugmask));
$this->nolog = p4field::isyes(trim($sx_task_settings->nolog));
$this->winsvc_run = p4field::isyes(trim($sx_task_settings->winsvc_run));
parent::loadSettings($sx_task_settings);
}
private function getIndexer()
{
$binaries = $this->dependencyContainer['phraseanet.configuration']['binaries'];
if (isset($binaries['phraseanet_indexer'])) {
$cmd = $binaries['phraseanet_indexer'];
} else {
$finder = new ExecutableFinder();
$cmd = $finder->find('phraseanet_indexer');
}
return $cmd;
}
/**
*
* @return void
*/
protected function run2()
{
$cmd = $this->getIndexer();
$this->method = self::METHOD_PROC_OPEN;
if ( ! file_exists($cmd) || ! is_executable($cmd)) {
$this->setState(self::STATE_STOPPED);
$this->log(sprintf('File \'%s\' does not exists', $cmd));
throw new Exception('cindexer executable not found', self::ERR_EXECUTABLE_NOT_FOUND);
return;
}
$args = array();
$args_nopwd = array();
if ($this->host) {
$args[] = '-h=' . $this->host;
$args_nopwd[] = '-h=' . $this->host;
}
if ($this->port) {
$args[] = '-P=' . $this->port;
$args_nopwd[] = '-P=' . $this->port;
}
if ($this->base) {
$args[] = '-b=' . $this->base;
$args_nopwd[] = '-b=' . $this->base;
}
if ($this->user) {
$args[] = '-u=' . $this->user;
$args_nopwd[] = '-u=' . $this->user;
}
if ($this->password) {
$args[] = '-p=' . $this->password;
$args_nopwd[] = '-p=xxxxxxx';
}
if ($this->socket) {
$args[] = '--socket=' . $this->socket;
$args_nopwd[] = '--socket=' . $this->socket;
}
$args[] = '-o';
$args_nopwd[] = '-o';
if ($this->charset) {
$args[] = '--default-character-set=' . $this->charset;
$args_nopwd[] = '--default-character-set=' . $this->charset;
}
if ($this->stem) {
$args[] = '--stem=' . $this->stem;
$args_nopwd[] = '--stem=' . $this->stem;
}
if ($this->sortempty) {
$args[] = '--sort-empty=' . $this->sortempty;
$args_nopwd[] = '--sort-empty=' . $this->sortempty;
}
if ($this->debugmask > 0) {
$args[] = '-d=' . $this->debugmask;
$args_nopwd[] = '-d=' . $this->debugmask;
}
if ($this->nolog) {
$args[] = '-n';
$args_nopwd[] = '-n';
}
if ($this->winsvc_run) {
$args[] = '--run';
$args_nopwd[] = '--run';
}
$this->new_status = NULL; // new status to set at the end
$this->exception = NULL; // exception to throw at the end
$this->log(sprintf("running cindexer with method %s", $this->method));
switch ($this->method) {
case self::METHOD_PROC_OPEN:
$this->run_with_proc_open($cmd, $args, $args_nopwd);
break;
case self::METHOD_FORK:
$this->run_with_fork($cmd, $args, $args_nopwd);
break;
case self::METHOD_EXEC:
$this->run_with_exec($cmd, $args, $args_nopwd);
break;
}
if ($this->new_status !== NULL) {
$this->setState($this->new_status);
}
if ($this->exception) {
throw $this->exception;
}
}
private function run_with_proc_open($cmd, $args, $args_nopwd)
{
$nullfile = defined('PHP_WINDOWS_VERSION_BUILD') ? 'NUL' : '/dev/null';
$descriptors = array();
$descriptors[1] = array("file", $nullfile, "a+");
$descriptors[2] = array("file", $nullfile, "a+");
$pipes = array();
$logcmd = escapeshellarg($cmd).' '.implode(' ', array_map('escapeshellarg', $args_nopwd));
$execmd = escapeshellarg($cmd).' '.implode(' ', array_map('escapeshellarg', $args));
$this->log(sprintf('cmd=\'%s\'', $logcmd));
$process = proc_open($execmd, $descriptors, $pipes, dirname($cmd), null, array('bypass_shell' => true));
$qsent = '';
$timetokill = NULL;
$sock = NULL;
$this->running = true;
while ($this->running) {
if ($this->getState() == self::STATE_TOSTOP && $this->socket > 0) {
// must quit task, so send 'Q' to port 127.0.0.1:XXXX to cindexer
if ( ! $qsent && (($sock = socket_create(AF_INET, SOCK_STREAM, 0)) !== false)) {
if (socket_connect($sock, '127.0.0.1', $this->socket) === true) {
socket_write($sock, 'Q', 1);
socket_write($sock, "\r\n", strlen("\r\n"));
for ($i = 0; $this->running && $i < 5; $i ++) {
$this->sleep(1);
}
$qsent = 'Q';
$timetokill = time() + 10;
} else {
socket_close($sock);
$sock = NULL;
}
}
}
$proc_status = proc_get_status($process);
if (! $proc_status['running']) {
// the cindexer died
if ($qsent == 'Q') {
$this->log('Phrasea indexer stopped');
$this->new_status = self::STATE_STOPPED;
} elseif ($qsent == 'K') {
$this->log('Phrasea indexer has been killed');
$this->new_status = self::STATE_STOPPED;
} else {
$this->log('Phrasea indexer crashed');
$this->exception = new Exception('cindexer crashed', self::ERR_CRASHED);
// do not change the status so scheduler may restart it
}
$this->running = false;
} else {
// the cindexer is still alive
if ($qsent == 'Q') {
if (time() > $timetokill) {
// must kill cindexer
$this->log('Sending kill signal to Phrasea indexer');
$qsent = 'K';
proc_terminate($process); // sigint
}
}
}
for ($i = 0; $this->running && $i < 5; $i ++) {
$this->sleep(1);
}
}
if ($sock) {
socket_close($sock);
$sock = NULL;
}
foreach (array_keys($pipes) as $offset) {
if (is_resource($pipes[$offset])) {
fclose($pipes[$offset]);
}
}
proc_terminate($process); // sigint
proc_close($process);
}
private function run_with_fork($cmd, $args, $args_nopwd)
{
$pid = pcntl_fork();
if ($pid == -1) {
$this->exception = new Exception('cindexer can\'t fork', self::ERR_CANT_FORK);
} elseif ($pid == 0) {
// child
umask(0);
if (($err = posix_setsid()) < 0) {
$this->exception = new Exception('cindexer can\'t detach from terminal', $err);
} else {
chdir(dirname(__FILE__));
pcntl_exec($cmd, $args);
$this->sleep(2);
}
} else {
// parent
$this->running = true;
$sigsent = NULL;
while ($this->running) {
// is the cindexer alive ?
if ( ! posix_kill($pid, 0)) {
// dead...
if ($sigsent === NULL) {
// but it's not my fault
$this->log('Phrasea indexer crashed');
$this->exception = new Exception('cindexer crashed', self::ERR_CRASHED);
// do not change the status so scheduler may restart it
break;
}
}
if ($this->getState() == self::STATE_TOSTOP) {
posix_kill($pid, ($sigsent = SIGINT));
$timetokill = time() + 10;
$this->sleep(2);
}
$status = NULL;
if (pcntl_wait($status, WNOHANG) == $pid) {
// child (indexer) has exited
if ($sigsent == SIGINT) {
$this->log('Phrasea indexer stopped');
$this->new_status = self::STATE_STOPPED;
} elseif ($sigsent == SIGKILL) {
$this->log('Phrasea indexer has been killed');
$this->new_status = self::STATE_STOPPED;
} else {
$this->log('Phrasea indexer crashed');
$this->exception = new Exception('cindexer crashed', self::ERR_CRASHED);
// do not change the status so scheduler may restart it
}
$this->running = false;
} else {
if ($sigsent == SIGINT && time() > $timetokill) {
// must kill cindexer
$this->log('Kill signal sent to Phrasea indexer');
posix_kill($pid, ($sigsent = SIGKILL));
}
$this->sleep(2);
}
} // while running
}
}
private function run_with_exec($cmd, $args, $args_nopwd)
{
pcntl_exec($cmd, $args);
$this->sleep(2);
}
/**
* @param array $params
*/
public static function getDefaultSettings(Configuration $config, array $params = array())
{
$database = $config['main']['database'];
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<tasksettings>\n"
."<host>" . $database['host'] . "</host><port>"
. $database['port'] . "</port><base>"
. $database['dbname'] . "</base><user>"
. $database['user'] . "</user><password>"
. $database['password'] . "</password><socket>25200</socket>"
. "<use_sbas>1</use_sbas><nolog>0</nolog><clng></clng>"
. "<winsvc_run>0</winsvc_run><charset>utf8</charset></tasksettings>";
}
}

View File

@@ -1,80 +0,0 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2013 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Alchemy\Phrasea\Core\Configuration\Configuration;
class task_period_emptyColl extends task_appboxAbstract
{
protected $base_id;
protected $suicidable = true;
protected $total_records = 0;
public static function getName()
{
return(_("Vidage de collection"));
}
public static function interfaceAvailable()
{
return false;
}
public static function help()
{
return("Vide une collection");
}
/**
*
* @param array $params
*/
public static function getDefaultSettings(Configuration $config, array $params = array())
{
return '<?xml version="1.0" encoding="UTF-8"?><tasksettings><bas_id>' . (isset($params['bas_id']) ? $params['bas_id'] : '' ) . '</bas_id></tasksettings>';
}
protected function loadSettings(SimpleXMLElement $sx_task_settings)
{
$this->base_id = (int) $sx_task_settings->base_id;
parent::loadSettings($sx_task_settings);
}
protected function retrieveContent(appbox $appbox)
{
if (! $this->base_id) {
$this->setState(self::STATE_STOPPED);
return array();
}
$collection = collection::get_from_base_id($this->dependencyContainer, $this->base_id);
$this->total_records = $collection->get_record_amount();
$collection->empty_collection(200);
$this->records_done += $this->total_records;
$this->setProgress($this->records_done, $this->total_records);
if ($this->total_records == 0) {
$this->setState(self::STATE_STOPPED);
$this->log('Job finished');
}
return array();
}
protected function processOneContent(appbox $appbox, $row)
{
return $this;
}
protected function postProcessOneContent(appbox $appbox, $row)
{
return $this;
}
}

View File

@@ -1,618 +0,0 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2013 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Alchemy\Phrasea\Core\Configuration\Configuration;
use Alchemy\Phrasea\Exception\InvalidArgumentException;
use Alchemy\Phrasea\Notification\Mail\MailSuccessFTPSender;
use Alchemy\Phrasea\Notification\Receiver;
use Alchemy\Phrasea\Model\Entities\FtpExport;
use Alchemy\Phrasea\Model\Entities\FtpExportElement;
class task_period_ftp extends task_appboxAbstract
{
protected $proxy;
protected $proxyport;
/**
*
* @return string
*/
public static function getName()
{
return(_("task::ftp:FTP Push"));
}
/**
*
* @return string
*/
public static function help()
{
return '';
}
/**
*
* @param string $oldxml
* @return string
*/
public function graphic2xml($oldxml)
{
$request = http_request::getInstance();
$parm2 = $request->get_parms('proxy', 'proxyport', 'period', 'syslog');
$dom = new DOMDocument();
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
if ((@$dom->loadXML($oldxml)) != FALSE) {
foreach (array('str:proxy', 'str:proxyport', 'str:period', 'pop:syslog') as $pname) {
$ptype = substr($pname, 0, 3);
$pname = substr($pname, 4);
$pvalue = $parm2[$pname];
if (($ns = $dom->getElementsByTagName($pname)->item(0)) != NULL) {
// 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":
case "pop":
$ns->appendChild($dom->createTextNode($pvalue));
break;
case "boo":
$ns->appendChild($dom->createTextNode($pvalue ? '1' : '0'));
break;
}
}
}
return($dom->saveXML());
}
/**
*
* @return void
*/
public function printInterfaceJS()
{
?>
<script type="text/javascript">
function taskFillGraphic_<?php echo(get_class($this));?>(xml)
{
if (xml) {
xml = $.parseXML(xml);
xml = $(xml);
with(document.forms['graphicForm'])
{
proxy.value = xml.find("proxy").text();
proxyport.value = xml.find("proxyport").text();
period.value = xml.find("period").text();
}
}
}
$(document).ready(function(){
var limits = {
'period' :{'min':<?php echo self::MINPERIOD; ?>, 'max':<?php echo self::MAXPERIOD; ?>}
} ;
$(".formElem").change(function(){
fieldname = $(this).attr("name");
switch ((this.nodeName+$(this).attr("type")).toLowerCase()) {
case "inputtext":
if (typeof(limits[fieldname])!='undefined') {
var v = 0|this.value;
if(v < limits[fieldname].min)
v = limits[fieldname].min;
else if(v > limits[fieldname].max)
v = limits[fieldname].max;
this.value = v;
}
break;
}
setDirty();
});
});
</script>
<?php
return;
}
/**
*
* @return string
*/
public function getInterfaceHTML()
{
ob_start();
?>
<form id="graphicForm" name="graphicForm" class="form-horizontal" onsubmit="return(false);" method="post">
<div class="control-group">
<label class="control-label"><?php echo _('task::ftp:proxy') ?></label>
<div class="controls">
<input class="formElem" type="text" name="proxy" />
</div>
</div>
<div class="control-group">
<label class="control-label"><?php echo _('task::ftp:proxy port') ?></label>
<div class="controls">
<input class="formElem" type="text" name="proxyport" />
</div>
</div>
<div class="control-group">
<label class="control-label"><?php echo _('task::_common_:periodicite de la tache') ?></label>
<div class="controls">
<input class="formElem input-small" type="text" name="period" />
<span class="help-inline"><?php echo _('task::_common_:secondes (unite temporelle)') ?></span>
</div>
</div>
</form>
<?php
return ob_get_clean();
}
public function saveChanges(connection_pdo $conn, $taskid, &$taskrow)
{
$request = http_request::getInstance();
$parm = $request->get_parms(
'xml'
, 'name'
, 'active'
, 'proxy'
, 'proxyport'
, 'period'
);
if ($parm["xml"] === null) {
// pas de xml 'raw' : on accepte les champs 'graphic view'
$domTaskSettings = new DOMDocument();
if ((@$domTaskSettings->loadXML($taskrow["settings"])) != FALSE) {
$xmlchanged = false;
foreach (array(
'proxy'
, 'proxyport'
, 'period'
) as $f) {
if ($parm[$f] !== NULL) {
if (($ns = $domTaskSettings->getElementsByTagName($f)->item(0)) != NULL) {
// 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 = $domTaskSettings->documentElement->appendChild($domTaskSettings->createElement($f));
}
// on fixe sa valeur
$ns->appendChild($domTaskSettings->createTextNode($parm[$f]));
$xmlchanged = true;
}
}
if ($xmlchanged) {
$parm["xml"] = $domTaskSettings->saveXML();
}
}
}
// si on doit changer le xml, on verifie qu'il est valide
$domdoc = new DOMDocument();
if ($parm["xml"] && ! @$domdoc->loadXML($parm["xml"])) {
return(false);
}
$sql = "";
$params = array(':task_id' => $taskid);
if ($parm["xml"] !== NULL) {
$sql .= ( $sql ? " ," : "") . "settings = :settings";
$params[':settings'] = $parm['xml'];
}
if ($parm["name"] !== NULL) {
$sql .= ( $sql ? " ," : "") . "name = :name";
$params[':name'] = $parm['name'];
}
if ($parm["active"] !== NULL) {
$sql .= ( $sql ? " ," : "") . "active = :active";
$params[':active'] = $parm['active'];
}
if ($sql) {
try {
$sql = "UPDATE task2 SET $sql WHERE task_id = :task_id";
$stmt = $conn->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
return true;
} catch (Exception $e) {
return false;
}
} else {
return true;
}
}
protected function loadSettings(SimpleXMLElement $sx_task_settings)
{
$this->proxy = (string) $sx_task_settings->proxy;
$this->proxyport = (string) $sx_task_settings->proxyport;
parent::loadSettings($sx_task_settings);
}
protected function retrieveContent(appbox $appbox)
{
foreach ($this->dependencyContainer['EM']
->getRepository('Alchemy\Phrasea\Model\Entities\FtpExport')
->findCrashedExports(new \DateTime('-1 month')) as $export) {
$this->dependencyContainer['EM']->remove($export);
}
$this->dependencyContainer['EM']->flush();
return $this->dependencyContainer['EM']
->getRepository('Alchemy\Phrasea\Model\Entities\FtpExport')
->findDoableExports();
}
protected function processOneContent(appbox $appbox, $export)
{
$state = "";
$ftp_server = $export->getAddr();
$ftp_user_name = $export->getLogin();
$ftp_user_pass = $export->getPwd();
$ftpLog = $ftp_user_name . "@" . p4string::addEndSlash($ftp_server) . $export->getDestfolder();
if ($export->getCrash() == 0) {
$line = sprintf(
_('task::ftp:Etat d\'envoi FTP vers le serveur' .
' "%1$s" avec le compte "%2$s" et pour destination le dossier : "%3$s"') . PHP_EOL
, $ftp_server
, $ftp_user_name
, $export->getDestfolder()
);
$state .= $line;
$this->logger->addDebug($line);
}
$state .= $line = sprintf(
_("task::ftp:TENTATIVE no %s, %s")
, $export->getCrash() + 1
, " (" . date('r') . ")"
) . PHP_EOL;
$this->logger->addDebug($line);
try {
$ssl = $export->isSsl();
$ftp_client = $this->dependencyContainer['phraseanet.ftp.client']($ftp_server, 21, 300, $ssl, $this->proxy, $this->proxyport);
$ftp_client->login($ftp_user_name, $ftp_user_pass);
if ($export->isPassif()) {
try {
$ftp_client->passive(true);
} catch (Exception $e) {
$this->logger->addDebug($e->getMessage());
}
}
if (trim($export->getDestfolder()) != '') {
try {
$ftp_client->chdir($export->getDestFolder());
$export->setDestfolder('/' . $export->getDestfolder());
} catch (Exception $e) {
$this->logger->addDebug($e->getMessage());
}
} else {
$export->setDestfolder('/');
}
if (trim($export->getFoldertocreate()) != '') {
try {
$ftp_client->mkdir($export->getFoldertocreate());
} catch (Exception $e) {
$this->logger->addDebug($e->getMessage());
}
try {
$new_dir = $ftp_client->add_end_slash($export->getDestfolder())
. $export->getFoldertocreate();
$ftp_client->chdir($new_dir);
} catch (Exception $e) {
$this->logger->addDebug($e->getMessage());
}
}
$obj = array();
$basefolder = '';
if (!in_array(trim($export->getDestfolder()), array('.', './', ''))) {
$basefolder = p4string::addEndSlash($export->getDestfolder());
}
$basefolder .= $export->getFoldertocreate();
if (in_array(trim($basefolder), array('.', './', ''))) {
$basefolder = '/';
}
foreach ($export->getElements() as $exportElement) {
if ($exportElement->isDone()) {
continue;
}
$base_id = $exportElement->getBaseId();
$record_id = $exportElement->getRecordId();
$subdef = $exportElement->getSubdef();
$localfile = null;
try {
$sbas_id = phrasea::sbasFromBas($this->dependencyContainer, $base_id);
$record = new record_adapter($this->dependencyContainer, $sbas_id, $record_id);
$sdcaption = $record->get_caption()->serialize(caption_record::SERIALIZE_XML, $exportElement->isBusinessfields());
$remotefile = $exportElement->getFilename();
if ($subdef == 'caption') {
$desc = $record->get_caption()->serialize(\caption_record::SERIALIZE_XML, $exportElement->isBusinessfields());
$localfile = $this->dependencyContainer['root.path'] . '/tmp/' . md5($desc . time() . mt_rand());
if (file_put_contents($localfile, $desc) === false) {
throw new Exception('Impossible de creer un fichier temporaire');
}
} elseif ($subdef == 'caption-yaml') {
$desc = $record->get_caption()->serialize(\caption_record::SERIALIZE_YAML, $exportElement->isBusinessfields());
$localfile = $this->dependencyContainer['root.path'] . '/tmp/' . md5($desc . time() . mt_rand());
if (file_put_contents($localfile, $desc) === false) {
throw new Exception('Impossible de creer un fichier temporaire');
}
} else {
$sd = $record->get_subdefs();
if (!$sd || !isset($sd[$subdef])) {
continue;
}
$localfile = $sd[$subdef]->get_pathfile();
if (!file_exists($localfile)) {
throw new Exception('Le fichier local n\'existe pas');
}
}
$current_folder = p4string::delEndSlash(str_replace('//', '/', $basefolder . $exportElement->getFolder()));
if ($ftp_client->pwd() != $current_folder) {
try {
$ftp_client->chdir($current_folder);
} catch (Exception $e) {
$this->logger->addDebug($e->getMessage());
}
}
$ftp_client->put($remotefile, $localfile);
$obj[] = array(
"name" => $subdef, "size" => filesize($localfile),
"shortXml" => ($sdcaption ? $sdcaption : '')
);
if ($subdef == 'caption') {
unlink($localfile);
}
$exportElement
->setDone(true)
->setError(false);
$this->dependencyContainer['EM']->persist($exportElement);
$this->dependencyContainer['EM']->flush();
$this->logexport($record, $obj, $ftpLog);
} catch (Exception $e) {
$state .= $line = sprintf(_('task::ftp:File "%1$s" (record %2$s) de la base "%3$s"' .
' (Export du Document) : Transfert cancelled (le document n\'existe plus)')
, basename($localfile), $record_id
, phrasea::sbas_labels(phrasea::sbasFromBas($this->dependencyContainer, $base_id), $this->dependencyContainer)) . "\n<br/>";
$this->logger->addDebug($line);
// One failure max
$exportElement
->setDone($exportElement->isError())
->setError(true);
$this->dependencyContainer['EM']->persist($exportElement);
$this->dependencyContainer['EM']->flush();
}
}
if ($export->isLogfile()) {
$this->logger->addDebug("logfile ");
$date = new DateTime();
$buffer = '#transfert finished ' . $date->format(DATE_ATOM) . "\n\n";
foreach ($export->getElements() as $exportElement) {
if (!$exportElement->isDone() || $exportElement->isError()) {
continue;
}
$filename = $exportElement->getFilename();
$folder = $exportElement->getFilename();
$root = $export->getFoldertocreate();
$buffer .= $root . '/' . $folder . $filename . "\n";
}
$tmpfile = $this->dependencyContainer['root.path'] . '/tmp/tmpftpbuffer' . $date->format('U') . '.txt';
file_put_contents($tmpfile, $buffer);
$remotefile = $date->format('U') . '-transfert.log';
$ftp_client->chdir($export->getDestFolder());
$ftp_client->put($remotefile, $tmpfile);
unlink($tmpfile);
}
$ftp_client->close();
unset($ftp_client);
} catch (Exception $e) {
$state .= $line = $e . "\n";
$this->logger->addDebug($line);
$export->incrementCrash();
$this->dependencyContainer['EM']->persist($export);
$this->dependencyContainer['EM']->flush();
unset($ftp_client);
}
$this->finalize($appbox, $export);
}
protected function postProcessOneContent(appbox $appbox, $row)
{
return $this;
}
public function finalize(appbox $appbox, FtpExport $export)
{
if ($export->getCrash() >= $export->getNbretry()) {
$this->send_mails($appbox, $export);
return $this;
}
$total = count($export->getElements());
$done = count($export->getElements()->filter(function (FtpExportElement $element) {
return $element->isDone();
}));
$error = count($export->getElements()->filter(function (FtpExportElement $element) {
return $element->isError();
}));
if ($done === $total) {
$this->send_mails($appbox, $export);
if ((int) $error === 0) {
$this->dependencyContainer['EM']->remove($export);
$this->dependencyContainer['EM']->flush();
} else {
$export->setCrash($export->getNbretry());
foreach ($export->getElements() as $element) {
if (!$element->isError()) {
$this->dependencyContainer['EM']->remove($export);
}
}
$this->dependencyContainer['EM']->flush();
}
return $this;
}
}
public function send_mails(appbox $appbox, FtpExport $export)
{
$transferts = array();
$transfert_status = _('task::ftp:Tous les documents ont ete transferes avec succes');
foreach ($export->getElements() as $element) {
if (!$element->isError() && $element->isDone()) {
$transferts[] =
'<li>' . sprintf(_('task::ftp:Record %1$s - %2$s de la base (%3$s - %4$s) - %5$s')
, $element->getRecordId(), $element->getFilename()
, phrasea::sbas_labels(phrasea::sbasFromBas($this->dependencyContainer, $element->getBaseId()), $this->dependencyContainer)
, phrasea::bas_labels($element->getBaseId(), $this->dependencyContainer), $element->getSubdef()) . ' : ' . _('Transfert OK') . '</li>';
} else {
$transferts[] =
'<li>' . sprintf(_('task::ftp:Record %1$s - %2$s de la base (%3$s - %4$s) - %5$s')
, $element->getRecordId(), $element->getFilename()
, phrasea::sbas_labels(phrasea::sbasFromBas($this->dependencyContainer, $element->getBaseId()), $this->dependencyContainer), phrasea::bas_labels($element->getBaseId(), $this->dependencyContainer)
, $element->getSubdef()) . ' : ' . _('Transfert Annule') . '</li>';
$transfert_status = _('task::ftp:Certains documents n\'ont pas pu etre tranferes');
}
}
if ($export->getCrash() >= $export->getNbretry()) {
$connection_status = _('Des difficultes ont ete rencontres a la connection au serveur distant');
} else {
$connection_status = _('La connection vers le serveur distant est OK');
}
$text_mail_sender = $export->getTextMailSender();
$text_mail_receiver = $export->getTextMailReceiver();
$mail = $export->getMail();
$sendermail = $export->getSendermail();
$ftp_server = $export->getAddr();
$message = "\n\n----------------------------------------\n\n";
$message = $connection_status . "\n";
$message .= $transfert_status . "\n";
$message .= _("task::ftp:Details des fichiers") . "\n\n";
$message .= implode("\n", $transferts);
$sender_message = $text_mail_sender . $message;
$receiver_message = $text_mail_receiver . $message;
try {
$receiver = new Receiver(null, $sendermail);
} catch (InvalidArgumentException $e) {
$receiver = null;
}
if ($receiver) {
$mail = MailSuccessFTPSender::create($this->dependencyContainer, $receiver, null, $sender_message);
$mail->setServer($ftp_server);
$this->dependencyContainer['notification.deliverer']->deliver($mail);
}
try {
$receiver = new Receiver(null, $mail);
$mail = MailSuccessFTPSender::create($this->dependencyContainer, $receiver, null, $receiver_message);
$mail->setServer($ftp_server);
$this->dependencyContainer['notification.deliverer']->deliver($mail);
} catch (\Exception $e) {
$this->log('Unable to deliver success message');
}
}
public function logexport(record_adapter $record, $obj, $ftpLog)
{
foreach ($obj as $oneObj) {
$this->dependencyContainer['phraseanet.logger']($record->get_databox())
->log($record, Session_Logger::EVENT_EXPORTFTP, $ftpLog, '');
}
return $this;
}
/**
* @param array $params
*/
public static function getDefaultSettings(Configuration $config, array $params = array())
{
$period = isset($params['period']) ? $params['period'] : self::MINPERIOD;
return sprintf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<tasksettings>
<proxy></proxy>
<proxyport></proxyport>
<period>%s</period>
<syslog></syslog>
</tasksettings>", min(max($period, self::MINPERIOD), self::MAXPERIOD));
}
}

View File

@@ -1,473 +0,0 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2013 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Alchemy\Phrasea\Core\Configuration\Configuration;
class task_period_ftpPull extends task_appboxAbstract
{
protected $proxy;
protected $proxyport;
protected $host;
protected $port;
protected $user;
protected $password;
protected $ssl;
protected $passive;
protected $ftppath;
protected $localpath;
public static function getName()
{
return(_("task::ftp:FTP Pull"));
}
public static function help()
{
return '';
}
public function graphic2xml($oldxml)
{
$request = http_request::getInstance();
$parm2 = $request->get_parms(
'proxy'
, 'proxyport'
, 'host'
, 'port'
, 'user'
, 'password'
, 'ssl'
, 'ftppath'
, 'localpath'
, 'passive'
, 'period'
);
$dom = new DOMDocument();
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
if (@$dom->loadXML($oldxml)) {
$xmlchanged = false;
foreach (array(
'str:proxy'
, 'str:proxyport'
, 'str:period'
, 'boo:passive'
, 'boo:ssl'
, 'str:password'
, 'str:user'
, 'str:ftppath'
, 'str:localpath'
, 'str:port'
, 'str:host'
) 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":
case "pop":
$ns->appendChild($dom->createTextNode($pvalue));
break;
case "boo":
$ns->appendChild($dom->createTextNode($pvalue ? '1' : '0'));
break;
}
$xmlchanged = true;
}
}
return($dom->saveXML());
}
public function printInterfaceJS()
{
?>
<script type="text/javascript">
function taskFillGraphic_<?php echo(get_class($this));?>(xml)
{
if (xml) {
xml = $.parseXML(xml);
xml = $(xml);
with(document.forms['graphicForm'])
{
proxy.value = xml.find("proxy").text();
proxyport.value = xml.find("proxyport").text();
period.value = xml.find("period").text();
localpath.value = xml.find("localpath").text();
ftppath.value = xml.find("ftppath").text();
host.value = xml.find("host").text();
port.value = xml.find("port").text();
user.value = xml.find("user").text();
password.value = xml.find("password").text();
ssl.checked = Number(xml.find("ssl").text()) > 0;
passive.checked = Number(xml.find("passive").text()) > 0;
}
}
}
$(document).ready(function(){
var limits = {
'period' :{'min':<?php echo self::MINPERIOD; ?>, 'max':<?php echo self::MAXPERIOD; ?>}
} ;
$(".formElem").change(function(){
fieldname = $(this).attr("name");
switch ((this.nodeName+$(this).attr("type")).toLowerCase()) {
case "inputtext":
if (typeof(limits[fieldname])!='undefined') {
var v = 0|this.value;
if(v < limits[fieldname].min)
v = limits[fieldname].min;
else if(v > limits[fieldname].max)
v = limits[fieldname].max;
this.value = v;
}
break;
}
setDirty();
});
});
</script>
<?php
}
public function getInterfaceHTML()
{
ob_start();
?>
<form name="graphicForm" onsubmit="return(false);" method="post">
<div class="control-group">
<label class="control-label"><?php echo _('task::ftp:proxy') ?></label>
<div class="controls">
<input class="formElem" type="text" name="proxy" />
</div>
</div>
<div class="control-group">
<label class="control-label"><?php echo _('task::ftp:proxy port') ?></label>
<div class="controls">
<input class="formElem" type="text" name="proxyport" />
</div>
</div>
<div class="control-group">
<label class="control-label"><?php echo('task::ftp:host') ?></label>
<div class="controls">
<input class="formElem" type="text" name="host" />
</div>
</div>
<div class="control-group">
<label class="control-label"><?php echo('task::ftp:port') ?></label>
<div class="controls">
<input class="formElem" type="text" name="port" />
</div>
</div>
<div class="control-group">
<label class="control-label"><?php echo('task::ftp:user') ?></label>
<div class="controls">
<input class="formElem" type="text" name="user" />
</div>
</div>
<div class="control-group">
<label class="control-label"><?php echo('task::ftp:password') ?></label>
<div class="controls">
<input class="formElem" type="password" name="password" />
</div>
</div>
<div class="control-group">
<label class="control-label"><?php echo('task::ftp:chemin distant') ?></label>
<div class="controls">
<input class="formElem" type="text" name="ftppath" />
</div>
</div>
<div class="control-group">
<label class="control-label"><?php echo('task::ftp:localpath') ?></label>
<div class="controls">
<input class="formElem" type="text" name="localpath" />
</div>
</div>
<div class="control-group">
<label class="control-label"><?php echo _('task::ftp:mode passif') ?></label>
<div class="controls">
<input class="formElem" type="checkbox" name="passive" />
</div>
</div>
<div class="control-group">
<div class="controls">
<label class="checkbox">
<input class="formElem" type="checkbox" name="ssl" />
<?php echo _('task::ftp:utiliser SSL') ?>
</label>
</div>
</div>
<div class="control-group">
<label class="control-label"><?php echo _('task::_common_:periodicite de la tache') ?></label>
<div class="controls">
<input class="formElem input-small" type="text" name="period" value="">
<span class="help-inline"><?php echo _('task::_common_:secondes (unite temporelle)') ?></span>
</div>
</div>
</form>
<?php
return ob_get_clean();
}
public function saveChanges(connection_pdo $conn, $taskid, &$taskrow)
{
$request = http_request::getInstance();
$parm = $request->get_parms(
'xml'
, 'name'
, 'active'
, 'proxy'
, 'proxyport'
, 'period'
, 'localpath'
, 'ftppath'
, 'port'
, 'host'
, 'user'
, 'password'
, 'passive'
, 'ssl'
);
if ($parm["xml"] === null) {
// pas de xml 'raw' : on accepte les champs 'graphic view'
$domdoc = new DOMDocument();
if (($domTaskSettings = $domdoc->loadXML($taskrow["settings"])) != FALSE) {
$xmlchanged = false;
foreach (array(
'proxy'
, 'proxyport'
, 'period'
, 'localpath'
, 'ftppath'
, 'host'
, 'port'
, 'user'
, 'password'
, 'passive'
, 'ssl'
) as $f) {
if ($parm[$f] !== NULL) {
if (($ns = $domTaskSettings->getElementsByTagName($f)->item(0)) != NULL) {
// 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 = $domTaskSettings->documentElement->appendChild($domTaskSettings->createElement($f));
}
// on fixe sa valeur
$ns->appendChild($domTaskSettings->createTextNode($parm[$f]));
$xmlchanged = true;
}
}
if ($xmlchanged) {
$parm["xml"] = $domTaskSettings->saveXML();
}
}
}
// si on doit changer le xml, on verifie qu'il est valide
$domdoc = new DOMDocument();
if ($parm["xml"] && ! $domdoc->loadXML($parm["xml"])) {
return(false);
}
$sql = "";
$params = array(':task_id' => $taskid);
if ($parm["xml"] !== NULL) {
$sql .= ( $sql ? " ," : "") . "settings = :settings";
$params[':settings'] = $parm['xml'];
}
if ($parm["name"] !== NULL) {
$sql .= ( $sql ? " ," : "") . "name = :name";
$params[':name'] = $parm['name'];
}
if ($parm["active"] !== NULL) {
$sql .= ( $sql ? " ," : "") . "active = :active";
$params[':active'] = $parm['active'];
}
if ($sql) {
try {
$sql = "UPDATE task2 SET $sql WHERE task_id = :task_id";
$stmt = $conn->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
return true;
} catch (Exception $e) {
return false;
}
} else {
return true;
}
}
protected function loadSettings(SimpleXMLElement $sx_task_settings)
{
$this->proxy = (string) $sx_task_settings->proxy;
$this->proxyport = (string) $sx_task_settings->proxyport;
$this->host = (string) ($sx_task_settings->host);
$this->port = (string) ($sx_task_settings->port);
$this->user = (string) ($sx_task_settings->user);
$this->password = (string) ($sx_task_settings->password);
$this->ssl = !!((string) ($sx_task_settings->ssl));
$this->passive = !!((string) ($sx_task_settings->passive));
$this->ftppath = (string) ($sx_task_settings->ftppath);
$this->localpath = (string) ($sx_task_settings->localpath);
parent::loadSettings($sx_task_settings);
}
protected function retrieveContent(appbox $appbox)
{
foreach (array('localpath', 'host', 'port', 'user', 'password', 'ftppath') as $f) {
if (trim((string) ($this->{$f})) === '') {
$this->log(sprintf('setting \'%s\' must be set', $f), self::LOG_ERROR);
$this->running = FALSE;
}
}
$this->dependencyContainer['filesystem']->mkdir($this->localpath, 0750);
if (!is_dir($this->localpath)) {
$this->log(sprintf('\'%s\' does not exists', $this->localpath), self::LOG_ERROR);
$this->running = FALSE;
}
if (!is_writeable($this->localpath)) {
$this->log(sprintf('\'%s\' is not writeable', $this->localpath), self::LOG_ERROR);
$this->running = FALSE;
}
if (!$this->running) {
$this->set_status(self::STATE_STOPPED);
return array();
}
try {
$ftp = $this->dependencyContainer['phraseanet.ftp.client']($this->host, $this->port, 90, $this->ssl, $this->proxy, $this->proxyport);
$ftp->login($this->user, $this->password);
$ftp->chdir($this->ftppath);
$list_1 = $ftp->list_directory(true);
$done = 0;
$todo = count($list_1);
$this->setProgress($done, $todo);
$this->logger->addDebug("attente de 25sec pour avoir les fichiers froids...");
$this->sleep(25);
if (!$this->running) {
if (isset($ftp) && $ftp instanceof ftpclient) {
$ftp->close();
}
return array();
}
$list_2 = $ftp->list_directory(true);
foreach ($list_1 as $filepath => $timestamp) {
$done++;
$this->setProgress($done, $todo);
if (!isset($list_2[$filepath])) {
$this->logger->addDebug("le fichier $filepath a disparu...\n");
continue;
}
if ($list_2[$filepath] !== $timestamp) {
$this->logger->addDebug("le fichier $filepath a ete modifie depuis le dernier passage...");
continue;
}
$finalpath = p4string::addEndSlash($this->localpath) . ($filepath[0] == '/' ? mb_substr($filepath, 1) : $filepath);
$this->logger->addDebug("Ok pour rappatriement de $filepath vers $finalpath\n");
try {
if (file_exists($finalpath)) {
throw new Exception("Un fichier du meme nom ($finalpath) existe deja...");
}
$this->dependencyContainer['filesystem']->mkdir(dirname($finalpath), 0750);
$ftp->get($finalpath, $filepath);
$ftp->delete($filepath);
} catch (Exception $e) {
$this->logger->addDebug("Erreur lors du rappatriement de $filepath : " . $e->getMessage());
}
}
$ftp->close();
$this->setProgress(0, 0);
} catch (Exception $e) {
if (isset($ftp) && $ftp instanceof ftpclient) {
$ftp->close();
}
$this->log($e->getMessage(), self::LOG_ERROR);
return array();
}
}
protected function processOneContent(appbox $appbox, $row)
{
}
protected function postProcessOneContent(appbox $appbox, $row)
{
}
/**
* @param array $params
*/
public static function getDefaultSettings(Configuration $config, array $params = array())
{
$period = isset($params['period']) ? $params['period'] : self::MINPERIOD;
return sprintf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<tasksettings>
<proxy></proxy>
<proxyport></proxyport>
<period>%s</period>
<passive>0</passive>
<ssl>0</ssl>
<password></password>
<user></user>
<ftppath></ftppath>
<localpath></localpath>
<port>21</port>
<host></host>
</tasksettings>", min(max($period, self::MINPERIOD), self::MAXPERIOD));
}
}

View File

@@ -1,372 +0,0 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2013 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Alchemy\Phrasea\Core\Configuration\Configuration;
use MediaAlchemyst\Transmuter\Image2Image;
class task_period_subdef extends task_databoxAbstract
{
const MINFLUSH = 10;
/**
* Record buffer for writing meta datas after building subdefs
*
* @var array
*/
protected $recs_to_write = array();
/**
* Maximum buffer size before flushing records
*
* @var <type>
*/
protected $record_buffer_size;
protected $thumbnailExtraction;
/**
* Return about text
*
* @return <type>
*/
public static function help()
{
return(
_("task::subdef:creation des sous definitions des documents d'origine")
);
}
/**
* Returns task name
*
* @return string
*/
public static function getName()
{
return(_('task::subdef:creation des sous definitions'));
}
protected function loadSettings(SimpleXMLElement $sx_task_settings)
{
$this->thumbnailExtraction = (Boolean) trim($sx_task_settings->embedded);
parent::loadSettings($sx_task_settings);
}
/**
* must return the xml (text) version of the form
*
* @param string $oldxml
* @return string
*/
public function graphic2xml($oldxml)
{
$request = http_request::getInstance();
$parm2 = $request->get_parms('period', 'flush', 'maxrecs', 'maxmegs', 'embedded');
$dom = new DOMDocument();
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
if (@$dom->loadXML($oldxml)) {
foreach (array('str:period', 'str:flush', 'str:maxrecs', 'str:maxmegs', 'boo:embedded') as $pname) {
$ptype = substr($pname, 0, 3);
$pname = substr($pname, 4);
$pvalue = $parm2[$pname];
if (($ns = $dom->getElementsByTagName($pname)->item(0)) != NULL) {
while (($n = $ns->firstChild)) {
$ns->removeChild($n);
}
} else {
$ns = $dom->documentElement->appendChild($dom->createElement($pname));
}
switch ($ptype) {
case "str":
case "pop":
$ns->appendChild($dom->createTextNode($pvalue));
break;
case "boo":
$ns->appendChild($dom->createTextNode($pvalue ? '1' : '0'));
break;
}
}
}
return($dom->saveXML());
}
/**
* must fill the graphic form (using js) from xml
*
* @param string $xml
* @param string $form
* @return string
*/
public function xml2graphic($xml, $form)
{
if (false !== $sxml = simplexml_load_string($xml)) {
if ((int) ($sxml->period) < self::MINPERIOD) {
$sxml->period = self::MINPERIOD;
} elseif ((int) ($sxml->period) > self::MAXPERIOD) {
$sxml->period = self::MAXPERIOD;
}
if ((int) ($sxml->flush) < self::MINFLUSH) {
$sxml->flush = self::MINFLUSH;
} elseif ((int) ($sxml->flush) > self::MAXFLUSH) {
$sxml->flush = self::MAXFLUSH;
}
if ((int) ($sxml->maxrecs) < self::MINRECS) {
$sxml->maxrecs = self::MINRECS;
} elseif (self::MAXRECS != -1 && (int) ($sxml->maxrecs) > self::MAXRECS) {
$sxml->maxrecs = self::MAXRECS;
}
if ((int) ($sxml->maxmegs) < self::MINMEGS) {
$sxml->maxmegs = self::MINMEGS;
} elseif (self::MAXMEGS != -1 && (int) ($sxml->maxmegs) > self::MAXMEGS) {
$sxml->maxmegs = self::MAXMEGS;
}
?>
<script type="text/javascript">
<?php echo $form ?>.period.value = "<?php echo p4string::MakeString($sxml->period, "js", '"') ?>";
<?php echo $form ?>.flush.value = "<?php echo p4string::MakeString($sxml->flush, "js", '"') ?>";
<?php echo $form ?>.maxrecs.value = "<?php echo p4string::MakeString($sxml->maxrecs, "js", '"') ?>";
<?php echo $form ?>.maxmegs.value = "<?php echo p4string::MakeString($sxml->maxmegs, "js", '"') ?>";
<?php echo $form ?>.embedded.value = <?php echo (Boolean) trim($sxml->embedded); ?>;
</script>
<?php
return("");
} else {
return("BAD XML");
}
}
/**
*
* generates le code js de l'interface 'graphic view'
*
*/
public function printInterfaceJS()
{
?>
<script type="text/javascript">
function taskFillGraphic_<?php echo(get_class($this));?>(xml)
{
if (xml) {
xml = $.parseXML(xml);
xml = $(xml);
with(document.forms['graphicForm'])
{
period.value = xml.find("period").text();
flush.value = xml.find("flush").text();
maxrecs.value = xml.find("maxrecs").text();
maxmegs.value = xml.find("maxmegs").text();
embedded.checked = !!parseInt(xml.find("embedded").text());
}
}
}
$(document).ready(function(){
var limits = {
'period' :{'min':<?php echo self::MINPERIOD; ?>, 'max':<?php echo self::MAXPERIOD; ?>},
'flush' :{'min':<?php echo self::MINFLUSH; ?>, 'max':<?php echo self::MAXFLUSH; ?>},
'maxrecs':{'min':<?php echo self::MINRECS; ?>, 'max':<?php echo self::MAXRECS; ?>},
'maxmegs':{'min':<?php echo self::MINMEGS; ?>, 'max':<?php echo self::MAXMEGS; ?>}
} ;
$(".formElem").change(function(){
fieldname = $(this).attr("name");
switch ((this.nodeName+$(this).attr("type")).toLowerCase()) {
case "inputtext":
if (typeof(limits[fieldname])!='undefined') {
var v = 0|this.value;
if(v < limits[fieldname].min)
v = limits[fieldname].min;
else if(v > limits[fieldname].max)
v = limits[fieldname].max;
this.value = v;
}
break;
}
setDirty();
});
});
</script>
<?php
}
/**
* return interface 'graphic view'
*
*/
public function getInterfaceHTML()
{
ob_start();
?>
<form id="graphicForm" class="form-horizontal" name="graphicForm" onsubmit="return(false);" method="post">
<div class="control-group">
<label class="control-label"><?php echo _('task::_common_:periodicite de la tache') ?></label>
<div class="controls">
<input class="formElem input-small" type="text" name="period" />
<span class="help-inline"><?php echo _('task::_common_:secondes (unite temporelle)') ?></span>
</div>
</div>
<div class="control-group">
<label class="control-label"> <?php echo sprintf(_("Number of records to process per batch")) ?></label>
<div class="controls">
<input class="formElem input-mini" type="text" name="flush" value="">
</div>
</div>
<div class="control-group">
<label class="control-label"><?php echo _('Restart the task every X records') ?></label>
<div class="controls">
<input class="formElem input-mini" type="text" name="maxrecs" value="">
</div>
</div>
<div class="control-group">
<label class="control-label"><?php echo _('Restart the task if memory reaches') ?></label>
<div class="controls">
<input class="formElem input-mini" type="text" name="maxmegs" value="">
<span class="help-inline">Mo</span>
</div>
</div>
<div class="control-group">
<label class="control-label"><?php echo _('Try to extract embedded thumbnails') ?></label>
<div class="controls">
<input class="formElem input-mini" type="checkbox" name="embedded" value="1">
</div>
</div>
</form>
<?php
return ob_get_clean();
}
public function retrieveSbasContent(databox $databox)
{
Image2Image::$lookForEmbeddedPreview = $this->thumbnailExtraction;
$connbas = $databox->get_connection();
$sql = 'SELECT coll_id, record_id
FROM record
WHERE jeton & ' . JETON_MAKE_SUBDEF . ' > 0
ORDER BY record_id DESC LIMIT 0, '.$this->maxrecs;
$stmt = $connbas->prepare($sql);
$stmt->execute();
$rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
return $rs;
}
public function processOneContent(databox $databox, $row)
{
$record_id = $row['record_id'];
$this->log(sprintf(
"Generate subdefs for : sbasid=%s / databox=%s / recordid=%s "
, $databox->get_sbas_id(), $databox->get_dbname() , $record_id)
, self::LOG_INFO
);
try {
$record = new record_adapter($this->dependencyContainer, $this->sbas_id, $record_id);
$record->generate_subdefs($databox, $this->dependencyContainer);
} catch (\Exception $e) {
$this->log(
sprintf(
"Generate subdefs failed for : sbasid=%s / databox=%s / recordid=%s : %s"
, $databox->get_sbas_id(), $databox->get_dbname() , $record_id, $e->getMessage())
, self::LOG_WARNING
);
}
$this->recs_to_write[] = $record->get_record_id();
if (count($this->recs_to_write) >= $this->record_buffer_size) {
$this->flushRecordsSbas();
}
unset($record);
return $this;
}
protected function postProcessOneContent(databox $databox, $row)
{
$connbas = $databox->get_connection();
$sql = 'UPDATE record
SET jeton=(jeton & ~' . JETON_MAKE_SUBDEF . '), moddate=NOW()
WHERE record_id=:record_id';
$stmt = $connbas->prepare($sql);
$stmt->execute(array(':record_id' => $row['record_id']));
$stmt->closeCursor();
return $this;
}
protected function flushRecordsSbas()
{
$sql = implode(', ', $this->recs_to_write);
if ($sql != '') {
$this->log(sprintf(
'setting %d record(s) to subdef meta writing'
, count($this->recs_to_write)
), self::LOG_INFO);
try {
$connbas = connection::getPDOConnection($this->dependencyContainer, $this->sbas_id);
$sql = 'UPDATE record
SET status=(status & ~0x03),
jeton=(jeton | ' . JETON_WRITE_META_SUBDEF . ')
WHERE record_id IN (' . $sql . ')';
$stmt = $connbas->prepare($sql);
$stmt->execute();
$stmt->closeCursor();
} catch (\Exception $e) {
$this->log($e->getMessage(), self::LOG_CRITICAL);
}
}
$this->recs_to_write = array();
return $this;
}
/**
* @param array $params
*/
public static function getDefaultSettings(Configuration $config, array $params = array())
{
$period = isset($params['period']) ? $params['period'] : self::MINPERIOD;
$flush = isset($params['flush']) ? $params['flush'] : self::MINFLUSH;
$maxrecs = isset($params['maxrecs']) ? $params['maxrecs'] : self::MINRECS;
$maxmegs = isset($params['maxmegs']) ? $params['maxmegs'] : self::DEFMEGS;
return sprintf('<?xml version="1.0" encoding="UTF-8"?>
<tasksettings>
<period>%s</period>
<flush>%s</flush>
<maxrecs>%s</maxrecs>
<maxmegs>%s</maxmegs>
<embedded>0</embedded>
</tasksettings>',
min(max($period, self::MINPERIOD), self::MAXPERIOD),
min(max($flush, self::MINFLUSH), self::MAXFLUSH),
min(max($maxrecs, self::MINRECS), self::MAXRECS),
min(max($maxmegs, self::MINMEGS), self::MAXMEGS)
);
}
}

View File

@@ -1,45 +0,0 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2013 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class task_period_test extends task_appboxAbstract
{
public static function getName()
{
return "Test";
}
public static function help()
{
return "just saying what i'm doing";
}
protected function retrieveContent(appbox $appbox)
{
$this->log('test class, retrieve content');
return array(array('hello'), array('world'));
}
protected function processOneContent(appbox $appbox, $row)
{
$this->log(sprintf("test class, process content : `%s`", implode(' ', $row)));
return $this;
}
protected function postProcessOneContent(appbox $appbox, $row)
{
$this->log(sprintf("test class, post process content, they were %s", count($row)));
return $this;
}
}

View File

@@ -1,360 +0,0 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2013 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Alchemy\Phrasea\Core\Configuration\Configuration;
use PHPExiftool\Driver\Metadata;
use PHPExiftool\Driver\Value;
use PHPExiftool\Driver\Tag;
use PHPExiftool\Writer;
class task_period_writemeta extends task_databoxAbstract
{
protected $clear_doc;
protected $metasubdefs = array();
public static function help()
{
return(_("task::writemeta:(re)ecriture des metadatas dans les documents (et subdefs concernees)"));
}
protected function loadSettings(SimpleXMLElement $sx_task_settings)
{
$this->clear_doc = p4field::isyes($sx_task_settings->cleardoc);
parent::loadSettings($sx_task_settings);
}
public static function getName()
{
return(_('task::writemeta:ecriture des metadatas'));
}
public function graphic2xml($oldxml)
{
$request = http_request::getInstance();
$parm2 = $request->get_parms('period', 'cleardoc', 'maxrecs', 'maxmegs');
$dom = new DOMDocument();
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
if ($dom->loadXML($oldxml)) {
foreach (array('str:period', 'str:maxrecs', 'str:maxmegs', 'boo:cleardoc') as $pname) {
$ptype = substr($pname, 0, 3);
$pname = substr($pname, 4);
$pvalue = $parm2[$pname];
if (($ns = $dom->getElementsByTagName($pname)->item(0)) != NULL) {
// 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":
case "pop":
$ns->appendChild($dom->createTextNode($pvalue));
break;
case "boo":
$ns->appendChild($dom->createTextNode($pvalue ? '1' : '0'));
break;
}
}
}
return($dom->saveXML());
}
public function xml2graphic($xml, $form)
{
if (false !== $sxml = simplexml_load_string($xml)) {
if ((int) ($sxml->period) < self::MINPERIOD) {
$sxml->period = self::MINPERIOD;
} elseif ((int) ($sxml->period) > self::MAXPERIOD) {
$sxml->period = self::MAXPERIOD;
}
if ((int) ($sxml->maxrecs) < self::MINRECS) {
$sxml->maxrecs = self::MINRECS;
} elseif (self::MAXRECS != -1 && (int) ($sxml->maxrecs) > self::MAXRECS) {
$sxml->maxrecs = self::MAXRECS;
}
if ((int) ($sxml->maxmegs) < self::MINMEGS) {
$sxml->maxmegs = self::MINMEGS;
} elseif (self::MAXMEGS != -1 && (int) ($sxml->maxmegs) > self::MAXMEGS) {
$sxml->maxmegs = self::MAXMEGS;
}
?>
<script type="text/javascript">
<?php echo $form ?>.period.value = "<?php echo p4string::MakeString($sxml->period, "js", '"') ?>";
<?php echo $form ?>.cleardoc.checked = <?php echo p4field::isyes($sxml->cleardoc) ? "true" : 'false' ?>;
<?php echo $form ?>.maxrecs.value = "<?php echo p4string::MakeString($sxml->maxrecs, "js", '"') ?>";
<?php echo $form ?>.maxmegs.value = "<?php echo p4string::MakeString($sxml->maxmegs, "js", '"') ?>";
</script>
<?php
return("");
} else { // ... so we NEVER come here
// bad xml
return("BAD XML");
}
}
public function printInterfaceJS()
{
?>
<script type="text/javascript">
function taskFillGraphic_<?php echo(get_class($this));?>(xml)
{
if (xml) {
xml = $.parseXML(xml);
xml = $(xml);
with(document.forms['graphicForm'])
{
period.value = xml.find("period").text();
cleardoc.checked = Number(xml.find("cleardoc").text()) > 0;
maxrecs.value = xml.find("maxrecs").text();
maxmegs.value = xml.find("maxmegs").text();
}
}
}
$(document).ready(function(){
var limits = {
'period':{'min':<?php echo self::MINPERIOD; ?>, 'max':<?php echo self::MAXPERIOD; ?>},
'maxrecs':{'min':<?php echo self::MINRECS; ?>, 'max':<?php echo self::MAXRECS; ?>},
'maxmegs':{'min':<?php echo self::MINMEGS; ?>, 'max':<?php echo self::MAXMEGS; ?>}
} ;
$(".formElem").change(function(){
fieldname = $(this).attr("name");
switch ((this.nodeName+$(this).attr("type")).toLowerCase()) {
case "inputtext":
if (typeof(limits[fieldname])!='undefined') {
var v = 0|this.value;
if(v < limits[fieldname].min)
v = limits[fieldname].min;
else if(v > limits[fieldname].max)
v = limits[fieldname].max;
this.value = v;
}
break;
}
setDirty();
});
});
</script>
<?php
}
public function getInterfaceHTML()
{
$sbas_ids = $this->dependencyContainer['authentication']->getUser()->ACL()->get_granted_sbas(array('bas_manage'));
ob_start();
if (count($sbas_ids) > 0) {
?>
<form name="graphicForm" onsubmit="return(false);" method="post">
<div class="control-group">
<label class="control-label"><?php echo _('task::_common_:periodicite de la tache') ?></label>
<div class="controls">
<input class="formElem input-small" type="text" name="period" value="">
<span class="help-inline"><?php echo _('task::_common_:secondes (unite temporelle)') ?></span>
</div>
</div>
<div class="control-group">
<div class="controls">
<label class="checkbox">
<input class="formElem" type="checkbox" name="cleardoc">
<?php echo _('task::writemeta:effacer les metadatas non presentes dans la structure') ?>
</label>
</div>
</div>
<div class="control-group">
<label class="control-label"><?php echo _('Restart the task every X records') ?></label>
<div class="controls">
<input class="formElem input-small" type="text" name="maxrecs" value="">
</div>
</div>
<div class="control-group">
<label class="control-label"><?php echo _('task::_common_:records, ou si la memoire depasse') ?></label>
<div class="controls">
<input class="formElem input-small" type="text" name="maxmegs" value="">
<span class="help-inline">Mo</span>
</div>
</div>
</form>
<?php
}
return ob_get_clean();
}
protected function retrieveSbasContent(databox $databox)
{
$this->dependencyContainer['exiftool.writer']->setModule(Writer::MODULE_MWG, true);
$connbas = $databox->get_connection();
$subdefgroups = $databox->get_subdef_structure();
$metasubdefs = array();
foreach ($subdefgroups as $type => $subdefs) {
foreach ($subdefs as $sub) {
$name = $sub->get_name();
if ($sub->meta_writeable()) {
$metasubdefs[$name . '_' . $type] = true;
}
}
}
$this->metasubdefs = $metasubdefs;
$sql = 'SELECT record_id, coll_id, jeton
FROM record WHERE (jeton & ' . JETON_WRITE_META . ' > 0)';
$stmt = $connbas->prepare($sql);
$stmt->execute();
$rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
return $rs;
}
protected function processOneContent(databox $databox, $row)
{
$record_id = $row['record_id'];
$jeton = $row['jeton'];
$record = new record_adapter($this->dependencyContainer, $this->sbas_id, $record_id);
$type = $record->get_type();
$subdefs = $record->get_subdefs();
$tsub = array();
foreach ($subdefs as $name => $subdef) {
$write_document = (($jeton & JETON_WRITE_META_DOC) && $name == 'document');
$write_subdef = (($jeton & JETON_WRITE_META_SUBDEF) && isset($this->metasubdefs[$name . '_' . $type]));
if (($write_document || $write_subdef) && $subdef->is_physically_present()) {
$tsub[$name] = $subdef->get_pathfile();
}
}
$metadatas = new Metadata\MetadataBag();
if ($record->get_uuid()) {
$metadatas->add(
new Metadata\Metadata(
new Tag\XMPExif\ImageUniqueID(),
new Value\Mono($record->get_uuid())
)
);
$metadatas->add(
new Metadata\Metadata(
new Tag\ExifIFD\ImageUniqueID(),
new Value\Mono($record->get_uuid())
)
);
$metadatas->add(
new Metadata\Metadata(
new Tag\IPTC\UniqueDocumentID(),
new Value\Mono($record->get_uuid())
)
);
}
foreach ($record->get_caption()->get_fields() as $field) {
$meta = $field->get_databox_field();
/* @var $meta \databox_field */
$datas = $field->get_values();
if ($meta->is_multi()) {
$values = array();
foreach ($datas as $data) {
$values[] = $data->getValue();
}
$value = new Value\Multi($values);
} else {
$data = array_pop($datas);
$value = $data->getValue();
$value = new Value\Mono($value);
}
$metadatas->add(
new Metadata\Metadata($meta->get_tag(), $value)
);
}
$this->dependencyContainer['exiftool.writer']->reset();
foreach ($tsub as $name => $file) {
$this->dependencyContainer['exiftool.writer']->erase($name != 'document' || $this->clear_doc, true);
try {
$this->dependencyContainer['exiftool.writer']->write($file, $metadatas);
$this->log(sprintf('meta written for sbasid=%1$d - recordid=%2$d (%3$s)', $this->sbas_id, $record_id, $name), self::LOG_INFO);
} catch (\PHPExiftool\Exception\Exception $e) {
$this->log(sprintf('meta NOT written for sbasid=%1$d - recordid=%2$d (%3$s) because "%s"', $this->sbas_id, $record_id, $name, $e->getMessage()), self::LOG_ERROR);
}
}
return $this;
}
protected function flushRecordsSbas()
{
return $this;
}
protected function postProcessOneContent(databox $databox, $row)
{
$connbas = $databox->get_connection();
$sql = 'UPDATE record SET jeton=jeton & ~' . JETON_WRITE_META . '
WHERE record_id = :record_id';
$stmt = $connbas->prepare($sql);
$stmt->execute(array(':record_id' => $row['record_id']));
$stmt->closeCursor();
return $this;
}
/**
* @param array $params
*/
public static function getDefaultSettings(Configuration $config, array $params = array())
{
$period = isset($params['period']) ? $params['period'] : self::MINPERIOD;
$maxrecs = isset($params['maxrecs']) ? $params['maxrecs'] : self::MINRECS;
$maxmegs = isset($params['maxmegs']) ? $params['maxmegs'] : self::DEFMEGS;
return sprintf('<?xml version="1.0" encoding="UTF-8"?>
<tasksettings>
<period>%s</period>
<maxrecs>%s</maxrecs>
<maxmegs>%s</maxmegs>
<cleardoc>0</cleardoc>
</tasksettings>',
min(max($period, self::MINPERIOD), self::MAXPERIOD),
min(max($maxrecs, self::MINRECS), self::MAXRECS),
min(max($maxmegs, self::MINMEGS), self::MAXMEGS)
);
}
}