From a5d27916ff25d117d24cbc34b54f107b7914343d Mon Sep 17 00:00:00 2001 From: jygaulier Date: Wed, 20 Jun 2012 14:52:01 +0200 Subject: [PATCH] workflow02 renames to recordmover --- lib/classes/task/period/recordmover.class.php | 820 ++++++++++++++++++ 1 file changed, 820 insertions(+) create mode 100644 lib/classes/task/period/recordmover.class.php diff --git a/lib/classes/task/period/recordmover.class.php b/lib/classes/task/period/recordmover.class.php new file mode 100644 index 0000000000..f0c02d5bc2 --- /dev/null +++ b/lib/classes/task/period/recordmover.class.php @@ -0,0 +1,820 @@ +get_parms( + "period", "logsql" + ); + $dom = new DOMDocument(); + $dom->preserveWhiteSpace = false; + $dom->formatOutput = true; + if ($dom->loadXML($oldxml)) { + $xmlchanged = false; + // foreach($parm2 as $pname=>$pvalue) + 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 + $dom->documentElement->appendChild($dom->createTextNode("\t")); + $ns = $dom->documentElement->appendChild($dom->createElement($pname)); + $dom->documentElement->appendChild($dom->createTextNode("\n")); + } + // set the value + switch ($ptype) { + case "str": + $ns->appendChild($dom->createTextNode($pvalue)); + break; + case "boo": + $ns->appendChild($dom->createTextNode($pvalue ? '1' : '0')); + break; + } + $xmlchanged = true; + } + } + + return $dom->saveXML(); + } + + /** + * must fill the gui (using js) from xml + * + * @param string $xml + * @param form-object $form + * @return string "" or error message + */ + public function xml2graphic($xml, $form) + { + if (($sxml = simplexml_load_string($xml))) { // in fact XML IS always valid here... + // ... but we could check for safe values + if ((int) ($sxml->period) < 10) + $sxml->period = 10; + elseif ((int) ($sxml->period) > 1440) // 1 jour + $sxml->period = 1440; + + if ((string) ($sxml->delay) == '') + $sxml->delay = 0; + ?> + + + + + + +
+ + + +           +  log changes +
+
+
+
+ maxrecs = 1000; + $this->sxTaskSettings = @simplexml_load_string($this->getSettings()); + if ( ! $this->sxTaskSettings) { + 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'] : '')); + } + + try { + $connbas = connection::getPDOConnection($task['sbas_id']); + } catch (Exception $e) { + $this->log(sprintf("can't connect sbas %s", $task['sbas_id'])); + 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($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_workflow02 + */ + protected function processOneContent(appbox $appbox, Array $row) + { + $logsql = (int) ($this->sxTaskSettings->logsql) > 0; + $dbox = databox::get_instance($row['sbas_id']); + $rec = new record_adapter($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($dbox, $row['coll']); + $rec->move_to_collection($coll, $appbox); + 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[63 - $bit] = $val; + } + } + $status = implode('', $status); + $rec->set_binary_status($status); + 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(); + if ($logsql) { + $this->log(sprintf("on sbas %s delete (grp child) rid %s \n", $row['sbas_id'], $child->get_record_id())); + } + } + } + $rec->delete(); + 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_workflow02 + */ + protected function postProcessOneContent(appbox $appbox, Array $row) + { + return $this; + } + + /** + * compute sql for a task ( entry in settings) + * + * @param simplexml $sxtask + * @param boolean $playTest + * @return array + */ + private function calcSQL($sxtask, $playTest = false) + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $sbas_id = (int) ($sxtask['sbas_id']); + + $ret = array( + 'name' => $sxtask['name'] ? (string) $sxtask['name'] : 'sans nom', + 'name_htmlencoded' => htmlentities($sxtask['name'] ? $sxtask['name'] : 'sans nom'), + 'active' => trim($sxtask['active']) === '1', + 'sbas_id' => $sbas_id, + 'basename' => '', + 'basename_htmlencoded' => '', + 'action' => strtoupper($sxtask['action']), + 'sql' => NULL, + 'err' => '', + 'err_htmlencoded' => '', + ); + + try { + $dbox = $appbox->get_databox($sbas_id); + + $ret['basename'] = $dbox->get_viewname(); + $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($sbas_id); + + $tw = array(); + $join = ''; + + $ijoin = 0; + + // criteria + 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 + 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 + 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 + 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 + $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($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']); + foreach ($sxml->tasks->task as $sxtask) { + $ret['tasks'][] = $this->calcSQL($sxtask, false); + } + break; + case 'PLAYTEST': + $sxml = simplexml_load_string($parm2['xml']); + foreach ($sxml->tasks->task as $sxtask) { + $ret['tasks'][] = $this->calcSQL($sxtask, true); + } + break; + case 'CALCSQL': + $xml = $this->graphic2xml(''); + $sxml = simplexml_load_string($xml); + foreach ($sxml->tasks->task as $sxtask) { + $ret['tasks'][] = $this->calcSQL($sxtask, false); + } + break; + } + + return json_encode($ret); + } +}