source: branches/1.2/workflow/inc/engine/src/ProcessManager/JobManager.php @ 1349

Revision 1349, 17.0 KB checked in by niltonneto, 15 years ago (diff)

Ticket #561 - Inclusão do módulo Workflow faltante nessa versão.

Line 
1<?php
2/**************************************************************************\
3* eGroupWare                                                               *
4* http://www.egroupware.org                                                *
5* --------------------------------------------                             *
6*  This program is free software; you can redistribute it and/or modify it *
7*  under the terms of the GNU General Public License as published by the   *
8*  Free Software Foundation; either version 2 of the License, or (at your  *
9*  option) any later version.                                              *
10\**************************************************************************/
11
12require_once GALAXIA_LIBRARY . '/src/ProcessManager/BaseManager.php';
13require_once GALAXIA_LIBRARY . '/src/ProcessManager/ProcessManager.php';
14require_once GALAXIA_LIBRARY . '/../jobs/class.JobEnum.inc.php';
15
16/**
17 * Classe para gerenciamento de Jobs
18 * @package Workflow
19 * @author Sidnei Augusto Drovetto Jr. - drovetto@gmail.com
20 * @license http://www.gnu.org/copyleft/gpl.html GPL
21 * @subpackage Job
22 */
23class JobManager extends BaseManager
24{
25        /**
26         * @var string $jobTable O nome da tabela de Jobs (normalmente: egw_wf_jobs)
27         * @access private
28         */
29        private $jobTable;
30
31        /**
32         * @var string $logTable O nome da tabela de Log dos Jobs (normalmente: egw_wf_job_logs)
33         * @access private
34         */
35        private $logTable;
36
37        /**
38         * @var object $processManager Objeto da classe ProcessManager
39         * @access private
40         */
41        private $processManager;
42
43        /**
44         * Status de Job executado com sucesso (o usuário chama o método informando o sucesso)
45         * @name STATUS_JOB_SUCCESS
46         */
47        const STATUS_JOB_SUCCESS = 0;
48
49        /**
50         * Status de Job executado com falha (quando o usuário chama o método de falha)
51         * @name STATUS_JOB_FAIL
52         */
53        const STATUS_JOB_FAIL = 1;
54
55        /**
56         * Status de Job que apresentou falha. A classe JobRunner faz algumas verificações e, pode detectar alguns problemas e impedir a execução do Job (atribuindo este Status ao log do Job)
57         * @name STATUS_FAIL
58         */
59        const STATUS_FAIL = 2;
60
61        /**
62         * Status de Job quando ocorre algum erro Fatal do PHP na execução do Job. A descrição do Job é a o erro gerado pelo PHP
63         * @name STATUS_ERROR
64         */
65        const STATUS_ERROR = 3;
66
67        /**
68         * Status de Job que é aplicado quando não há erros e o código do usuário não informa se a execução foi bem sucedida ou não
69         * @name STATUS_UNKNOWN
70         */
71        const STATUS_UNKNOWN = 4;
72
73        /**
74         * Construtor da classe JobManager
75         * @return object
76         * @access public
77         */
78        public function JobManager(&$db)
79        {
80                parent::BaseManager($db);
81                $this->child_name = 'JobManager';
82
83                $this->jobTable = GALAXIA_TABLE_PREFIX . 'jobs';
84                $this->logTable = GALAXIA_TABLE_PREFIX . 'job_logs';
85                $this->processManager = new ProcessManager($this->db);
86        }
87
88        /**
89         * Gera um nome normalizado a partir de um nome de Job
90         * @param string $name O nome do Job
91         * @return string O valor de $name normalizado
92         * @access private
93         */
94        private function normalize($name)
95        {
96                $search = array('/[À-Å]/', '/Æ/', '/Ç/', '/[È-Ë]/', '/[Ì-Ï]/', '/Ð/', '/Ñ/', '/[Ò-ÖØ]/', '/×/', '/[Ù-Ü]/', '/Ý/', '/ß/', '/[à-å]/', '/æ/', '/ç/', '/[è-ë]/', '/[ì-ï]/', '/ð/', '/ñ/', '/[ò-öø]/', '/÷/', '/[ù-ü]/', '/[ý-ÿ]/');
97                $replace = array('A', 'AE', 'C', 'E', 'I', 'D', 'N', 'O', 'X', 'U', 'Y', 'ss', 'a', 'ae', 'c', 'e', 'i', 'd', 'n', 'o', 'x', 'u', 'y');
98                $output = str_replace(' ', '', ucwords(preg_replace($search, $replace, $name)));
99                $output = preg_replace("/[^0-9A-Za-z\_\-]/", '', $output);
100
101                return $output;
102        }
103
104        /**
105         * Valida os parâmetros para atualização e criação de jobs
106         * @param string $name O nome do Job
107         * @param string $timeStart Uma string cujo conteúdo é um data e horário devidamente formatados
108         * @param int $intervalValue O intervalo de repetição
109         * @param int $intervalUnity A unidade de repetição (dia, mês, etc.)
110         * @param int $dateType O tipo que define quando o Job é executado
111         * @param int $weekDays Inteiro que representa os dias da semana em que o Job será executado
112         * @param int $monthOffset Intervalo de execução relativa a mês
113         * @param bool $active Indica se o job está ativo (true) ou não (false)
114         * @param int $processID O ID do processo a qual o Job pertence
115         * @param int $jobID O ID do Job (no caso de atualização)
116         * @return bool Indica se os parâmetros foram aceitos (true) ou não (false)
117         * @access private
118         */
119        private function validateJobParameters($name, $timeStart, $intervalValue, $intervalUnity, $dateType, $weekDays, $monthOffset, $active, $processID, $jobID = null)
120        {
121                $normalizedName = $this->normalize($name);
122                if (strlen($normalizedName) < 1)
123                        $this->error[] = 'Este processo produz um nome normalizado que é vazio. Troque o nome do job.';
124
125                list($date, $time) = explode(' ', $timeStart, 2);
126                if (strlen($date) != 10)
127                        $this->error[] = 'A data está num formato inválido. Utilize: dd/mm/aaaa.';
128
129                list($year, $month, $day) = explode('-', $date, 3);
130                if (!checkdate($month, $day, $year))
131                        $this->error[] = 'A data fornecida não é válida.';
132
133                list($hour, $minute, $second) = explode(':', $time, 3);
134                if (($hour < 0) || ($hour > 23) || ($minute < 0) || ($minute > 59))
135                        $this->error[] = 'A horário informado não é válido.';
136
137                if ((!is_numeric($intervalValue)) || (strpos($intervalValue, '.') !== false) || (strpos($intervalValue, ',') !== false) || (($intervalValue < 1) && ($intervalUnity != DateUnity::NONE)))
138                        $this->error[] = 'O intervalo de tempo para repetição precisa ser um número inteiro positivo.';
139
140                switch ($dateType)
141                {
142                        case DateType::ABSOLUTE_DATE:
143                                if (!in_array($intervalUnity, array(DateUnity::YEAR, DateUnity::MONTH, DateUnity::WEEK, DateUnity::DAY, DateUnity::HOUR, DateUnity::MINUTE, DateUnity::NONE)))
144                                        $this->error[] = 'A unidade de intervalo utilizada é inválida.';
145                                break;
146
147                        case DateType::WEEK_DATE:
148                                if (($weekDays < 0) || ($weekDays > 127))
149                                        $this->error[] = 'Os dias da semana selecionados geraram não formam uma possibilidade válida.';
150                                break;
151
152                        case DateType::RELATIVE_DATE:
153                                if ($monthOffset < 1)
154                                        $this->error[] = 'O deslocamento relativo ao mês seguinte não possui um valor válido.';
155                                break;
156                }
157
158                /* a new job will be created */
159                $allJobs = $this->getJobsByProcessID($processID);
160
161                foreach ($allJobs as $currentJob)
162                {
163                        if ($currentJob['job_id'] == $jobID)
164                                continue;
165                        if (strcasecmp($this->normalize($currentJob['name']), $normalizedName) == 0)
166                        {
167                                $this->error[] = 'O nome escolhido entra em conflito com outro job do mesmo processo.';
168                                break;
169                        }
170                }
171
172                return (count($this->error) === 0);
173        }
174
175        /**
176         * Busca informações de todos os Jobs de um processo
177         * @param int $processID O ID do processo
178         * @return array A lista de Jobs de um processo
179         * @access public
180         */
181        public function getJobsByProcessID($processID)
182        {
183                return $this->query("SELECT * FROM {$this->jobTable} WHERE wf_process_id = ?", array($processID))->getArray();
184        }
185
186        /**
187         * Obtém o arquivo de código do Job
188         * @param int $jobID O ID do Job
189         * @return string O arquivo do Job
190         * @access public
191         */
192        public function getJobFile($jobID)
193        {
194                $jobInfo = $this->getJob($jobID);
195                $processInfo = $this->processManager->get_process($jobInfo['wf_process_id']);
196                return GALAXIA_PROCESSES . '/' . $processInfo['wf_normalized_name'] . '/code/jobs/' . 'class.job.' . $this->normalize($jobInfo['name']) . '.php';
197        }
198
199        /**
200         * Obtém o nome da classe de um Job
201         * @param int $jobID O ID do Job
202         * @return string O nome da classe do Job
203         * @access public
204         */
205        public function getClassName($jobID)
206        {
207                $jobInfo = $this->getJob($jobID);
208                return $this->normalize($jobInfo['name']);
209        }
210
211        /**
212         * Obtém informações sobre um Job
213         * @param int $jobID O ID do Job
214         * @return array Informações sobre o Job
215         * @access public
216         */
217        public function getJob($jobID)
218        {
219                return $this->query("SELECT * FROM {$this->jobTable} WHERE job_id = ?", array($jobID))->fetchRow();
220        }
221
222        /**
223         * Define se um Job está ativo ou não
224         * @param int $jobID O ID do Job
225         * @param bool $active Informa se o Job será ativado (true) ou não (false)
226         * @return bool True em caso de sucesso ou false caso contrário
227         * @access public
228         */
229        public function setActive($jobID, $active)
230        {
231                $jobID = (int) $jobID;
232                $active = $active ? 'TRUE' : 'FALSE';
233                return $this->query("UPDATE {$this->jobTable} SET active = ? WHERE job_id = ?", array($active, $jobID));
234        }
235
236        /**
237         * Busca os logs de um Job
238         * @param int $jobID O ID do Job
239         * @return array Os logs do Job
240         * @access public
241         */
242        public function getLogsByJobID($jobID)
243        {
244                return $this->query("SELECT * FROM {$this->logTable} WHERE job_id = ? ORDER BY date_time DESC", array($jobID))->getArray();
245        }
246
247        /**
248         * Grava uma entrada de log para um Job
249         * @param int $jobID O ID do Job
250         * @param object $currentDate Um objeto da classe DateTime contendo a data em que o Job foi executado
251         * @param string $message A mensagem que será salva no Log
252         * @param int $status O status do log
253         * @return void
254         * @access public
255         */
256        public function writeLog($jobID, $currentDate, $message, $status)
257        {
258                $message = str_replace(array(chr(92), chr(0), chr(39)), array('\134', '\000', '\047'), $message);
259                $this->db->StartTrans();
260
261                /* remove any previous log for the same time */
262                $query = "DELETE FROM {$this->logTable} WHERE (job_id = ?) AND (date_time = TO_TIMESTAMP(?, 'YYYY-MM-DD HH24:MI:SS'))";
263                $values = array($jobID, $currentDate->format('Y-m-d H:i:00'));
264                $this->query($query, $values);
265
266                /* insert the new log */
267                $query = "INSERT INTO {$this->logTable}(job_id, date_time, result, status) VALUES(?, TO_TIMESTAMP(?, 'YYYY-MM-DD HH24:MI:SS'), ?, ?)";
268                $values = array($jobID, $currentDate->format('Y-m-d H:i:00'), $message, $status);
269                $this->query($query, $values);
270
271                $this->db->CompleteTrans();
272        }
273
274        /**
275         * Cria e grava um arquivo modelo do Job
276         * @param int $jobID O ID do Job
277         * @return void
278         * @access public
279         */
280        private function createModelFile($jobID)
281        {
282                $className = $this->getClassName($jobID);
283                $contents = "<?php\nclass {$className} extends JobBase\n{\n\tpublic function run()\n\t{\n\t\t/* código que será executado */\n\t}\n}\n?>";
284                $this->setJobFileContent($jobID, $contents);
285        }
286
287        /**
288         * Define o conteúdo de um arquivo de Job
289         * @param int $jobID O ID do Job
290         * @param string $contents O conteúdo do arquivo
291         * @return bool True se o arquivo foi criado corretamente ou false caso contrário
292         * @access public
293         */
294        public function setJobFileContent($jobID, $contents)
295        {
296                $jobFile = $this->getJobFile($jobID);
297                if (!is_dir(($jobDirectory = dirname($jobFile))))
298                        mkdir($jobDirectory, 0770);
299
300                $success = @file_put_contents($jobFile, $contents);
301                if ($success === false)
302                        $this->error[] = "Não foi possível definir o conteúdo do arquivo de Job. Crie manualmente o arquivo:\n{$jobFile}";
303
304                return $success;
305        }
306
307        /**
308         * Cria um novo Job
309         * @param int $processID O ID do processo a qual o Job pertence
310         * @param string $name O nome do Job
311         * @param string $description A descrição do Job
312         * @param string $timeStart Uma string cujo conteúdo é um data e horário devidamente formatados
313         * @param int $intervalValue O intervalo de repetição
314         * @param int $intervalUnity A unidade de repetição (dia, mês, etc.)
315         * @param int $dateType O tipo que define quando o Job é executado
316         * @param int $weekDays Inteiro que representa os dias da semana em que o Job será executado
317         * @param int $monthOffset Intervalo de execução relativa a mês
318         * @param bool $active Indica se o job está ativo (true) ou não (false)
319         * @return bool True em caso de sucesso ou false caso os parâmetros não sejam válidos
320         * @access public
321         */
322        public function createJob($processID, $name, $description, $timeStart, $intervalValue, $intervalUnity, $dateType, $weekDays, $monthOffset, $active)
323        {
324                if ($this->validateJobParameters($name, $timeStart, $intervalValue, $intervalUnity, $dateType, $weekDays, $monthOffset, $active, $processID) === false)
325                        return false;
326
327                switch ($dateType)
328                {
329                        case DateType::ABSOLUTE_DATE:
330                                $weekDays = $monthOffset = null;
331                                break;
332
333                        case DateType::WEEK_DATE:
334                                $monthOffset = null;
335                                break;
336
337                        case DateType::RELATIVE_DATE:
338                                $weekDays = null;
339                                break;
340                }
341
342                $query = "INSERT INTO {$this->jobTable}(wf_process_id, name, description, time_start, interval_value, interval_unity, date_type, week_days, month_offset, active) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
343                $values = array($processID, $name, $description, $timeStart, $intervalValue, $intervalUnity, $dateType, $weekDays, $monthOffset, $active);
344                $this->query($query, $values);
345                $row = $this->query("SELECT job_id FROM {$this->jobTable} WHERE wf_process_id = ? AND name = ?", array($processID, $name))->fetchRow();
346                $this->createModelFile($row['job_id']);
347
348                return true;
349        }
350
351        /**
352         * Atualiza um Job
353         * @param int $jobID O ID do Job que será atualizado
354         * @param int $processID O ID do processo a qual o Job pertence
355         * @param string $name O nome do Job
356         * @param string $description A descrição do Job
357         * @param string $timeStart Uma string cujo conteúdo é um data e horário devidamente formatados
358         * @param int $intervalValue O intervalo de repetição
359         * @param int $intervalUnity A unidade de repetição (dia, mês, etc.)
360         * @param int $dateType O tipo que define quando o Job é executado
361         * @param int $weekDays Inteiro que representa os dias da semana em que o Job será executado
362         * @param int $monthOffset Intervalo de execução relativa a mês
363         * @param bool $active Indica se o job está ativo (true) ou não (false)
364         * @return bool True em caso de sucesso ou false caso os parâmetros não sejam válidos
365         * @access public
366         */
367        public function updateJob($jobID, $processID, $name, $description, $timeStart, $intervalValue, $intervalUnity, $dateType, $weekDays, $monthOffset, $active)
368        {
369                if ($this->validateJobParameters($name, $timeStart, $intervalValue, $intervalUnity, $dateType, $weekDays, $monthOffset, $active, $processID, $jobID) === false)
370                        return false;
371
372                $oldJob = $this->getJob($jobID);
373                $oldJobFile = $this->getJobFile($jobID);
374                $oldJobClassName = $this->getClassName($jobID);
375
376                switch ($dateType)
377                {
378                        case DateType::ABSOLUTE_DATE:
379                                $weekDays = $monthOffset = null;
380                                break;
381
382                        case DateType::WEEK_DATE:
383                                $monthOffset = null;
384                                break;
385
386                        case DateType::RELATIVE_DATE:
387                                $weekDays = null;
388                                break;
389                }
390                $query = "UPDATE {$this->jobTable} SET name = ?, description = ?, time_start = ?, interval_value = ?, interval_unity = ?, date_type = ?, week_days = ?, month_offset = ?, active = ? WHERE job_id = ?";
391                $values = array($name, $description, $timeStart, $intervalValue, $intervalUnity, $dateType, $weekDays, $monthOffset, $active, $jobID);
392
393                $this->query($query, $values);
394
395                if (!file_exists($oldJobFile))
396                {
397                        $this->createModelFile($jobID);
398                }
399                else
400                {
401                        if ($oldJob['name'] != $name)
402                        {
403                                $oldFileContents = file_get_contents($oldJobFile);
404                                $newFileContents = str_replace("class {$oldJobClassName}", 'class ' . $this->getClassName($jobID), $oldFileContents, $count);
405                                if ($count == 0)
406                                        $this->error[] = 'É necessário trocar manualmente o nome da classe. O nome que deve ser utilizado é: ' . $this->getClassName($jobID) . '.';
407                                file_put_contents($this->getJobFile($jobID), $newFileContents);
408                                unlink($oldJobFile);
409                        }
410                }
411
412                return true;
413        }
414
415        /**
416         * Cria ou atualiza um Job. Este método é utilizado na criação de Jobs ocorrida na importação de um processo
417         * @param int $processID O ID do processo
418         * @param int $jobID O ID do Job. Se for 0 (zero) indica que deve ser criado um novo processo
419         * @param array $params Os parâmetros para criação/atualização de um processo
420         * @return int O ID do Job criado/atualizado
421         * @access public
422         */
423        public function replaceJob($processID, $jobID, $params)
424        {
425                $name = $params['name'];
426                $description = $params['description'];
427                $timeStart = $params['timeStart'];
428                $intervalValue = $params['intervalValue'];
429                $intervalUnity = $params['intervalUnity'];
430                $dateType = $params['dateType'];
431                $weekDays = $params['weekDays'];
432                $monthOffset = $params['monthOffset'];
433                $active = 'f';
434                $fileContents = $params['fileContents'];
435
436                if ($jobID === 0)
437                {
438                        $this->createJob($processID, $name, $description, $timeStart, $intervalValue, $intervalUnity, $dateType, $weekDays, $monthOffset, $active);
439                        $jobID = $this->getOne("SELECT MAX(job_id) FROM {$this->jobTable} WHERE (wf_process_id = ?)", array($processID));
440                }
441                else
442                {
443                        $this->updateJob($jobID, $processID, $name, $description, $timeStart, $intervalValue, $intervalUnity, $dateType, $weekDays, $monthOffset, $active);
444                }
445                $this->setJobFileContent($jobID, $fileContents);
446
447                return $jobID;
448        }
449
450        /**
451         * Remove um Job e seus logs
452         * @param int $jobID O ID do Job
453         * @return void
454         * @access public
455         */
456        public function removeJob($jobID)
457        {
458                $jobID = (int) $jobID;
459                @unlink($this->getJobFile($jobID));
460                $this->query("DELETE FROM {$this->jobTable} WHERE job_id = ?", array($jobID));
461                $this->query("DELETE FROM {$this->logTable} WHERE job_id = ?", array($jobID));
462        }
463
464        /**
465         * Remove os Jobs e logs de um processo
466         * @param int $processID O ID do processo
467         * @return void
468         * @access public
469         */
470        public function removeJobsByProcessID($processID)
471        {
472                $jobs = $this->getJobsByProcessID($processID);
473                foreach ($jobs as $job)
474                        $this->removeJob($job['job_id']);
475        }
476}
477?>
Note: See TracBrowser for help on using the repository browser.