PageRenderTime 57ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/api/Model.class.php

https://github.com/Hildy/cerb5
PHP | 4404 lines | 3227 code | 715 blank | 462 comment | 503 complexity | ace8680c2cbe2b4f2bb02556c858870a MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /***********************************************************************
  3. | Cerberus Helpdesk(tm) developed by WebGroup Media, LLC.
  4. |-----------------------------------------------------------------------
  5. | All source code & content (c) Copyright 2007, WebGroup Media LLC
  6. | unless specifically noted otherwise.
  7. |
  8. | This source code is released under the Cerberus Public License.
  9. | The latest version of this license can be found here:
  10. | http://www.cerberusweb.com/license.php
  11. |
  12. | By using this software, you acknowledge having read this license
  13. | and agree to be bound thereby.
  14. | ______________________________________________________________________
  15. | http://www.cerberusweb.com http://www.webgroupmedia.com/
  16. ***********************************************************************/
  17. /*
  18. * IMPORTANT LICENSING NOTE from your friends on the Cerberus Helpdesk Team
  19. *
  20. * Sure, it would be so easy to just cheat and edit this file to use the
  21. * software without paying for it. But we trust you anyway. In fact, we're
  22. * writing this software for you!
  23. *
  24. * Quality software backed by a dedicated team takes money to develop. We
  25. * don't want to be out of the office bagging groceries when you call up
  26. * needing a helping hand. We'd rather spend our free time coding your
  27. * feature requests than mowing the neighbors' lawns for rent money.
  28. *
  29. * We've never believed in encoding our source code out of paranoia over not
  30. * getting paid. We want you to have the full source code and be able to
  31. * make the tweaks your organization requires to get more done -- despite
  32. * having less of everything than you might need (time, people, money,
  33. * energy). We shouldn't be your bottleneck.
  34. *
  35. * We've been building our expertise with this project since January 2002. We
  36. * promise spending a couple bucks [Euro, Yuan, Rupees, Galactic Credits] to
  37. * let us take over your shared e-mail headache is a worthwhile investment.
  38. * It will give you a sense of control over your in-box that you probably
  39. * haven't had since spammers found you in a game of "E-mail Address
  40. * Battleship". Miss. Miss. You sunk my in-box!
  41. *
  42. * A legitimate license entitles you to support, access to the developer
  43. * mailing list, the ability to participate in betas and the warm fuzzy
  44. * feeling of feeding a couple obsessed developers who want to help you get
  45. * more done than 'the other guy'.
  46. *
  47. * - Jeff Standen, Mike Fogg, Brenan Cavish, Darren Sugita, Dan Hildebrandt
  48. * and Joe Geck.
  49. * WEBGROUP MEDIA LLC. - Developers of Cerberus Helpdesk
  50. */
  51. class Model_PreParseRule {
  52. public $id;
  53. public $created;
  54. public $name;
  55. public $criteria;
  56. public $actions;
  57. public $pos;
  58. public $is_sticky = 0;
  59. public $sticky_order = 0;
  60. /**
  61. * Returns a Model_PreParserRule on a match, or NULL
  62. *
  63. * @param boolean $is_new
  64. * @param string $from
  65. * @param string $to
  66. * @param CerberusParserMessage $message
  67. * @return Model_PreParserRule[]
  68. */
  69. static function getMatches(CerberusParserMessage $message) {
  70. $filters = DAO_PreParseRule::getAll();
  71. $headers = $message->headers;
  72. // New or reply?
  73. $is_new = (isset($message->headers['in-reply-to']) || isset($message->headers['references'])) ? false : true;
  74. // From address
  75. $fromInst = CerberusParser::getAddressFromHeaders($headers);
  76. // Stackable
  77. $matches = array();
  78. // Custom fields
  79. $custom_fields = DAO_CustomField::getAll();
  80. // Criteria extensions
  81. $filter_criteria_exts = DevblocksPlatform::getExtensions('cerberusweb.mail_filter.criteria', false);
  82. // Lazy load when needed on criteria basis
  83. $address_field_values = null;
  84. $org_field_values = null;
  85. // check filters
  86. if(is_array($filters))
  87. foreach($filters as $filter) {
  88. $passed = 0;
  89. // check criteria
  90. foreach($filter->criteria as $rule_key => $rule) {
  91. @$value = $rule['value'];
  92. switch($rule_key) {
  93. case 'dayofweek':
  94. $current_day = strftime('%w');
  95. // $current_day = 1;
  96. // Forced to English abbrevs as indexes
  97. $days = array('sun','mon','tue','wed','thu','fri','sat');
  98. // Is the current day enabled?
  99. if(isset($rule[$days[$current_day]])) {
  100. $passed++;
  101. }
  102. break;
  103. case 'timeofday':
  104. $current_hour = strftime('%H');
  105. $current_min = strftime('%M');
  106. // $current_hour = 17;
  107. // $current_min = 5;
  108. if(null != ($from_time = @$rule['from']))
  109. list($from_hour, $from_min) = explode(':', $from_time);
  110. if(null != ($to_time = @$rule['to']))
  111. if(list($to_hour, $to_min) = explode(':', $to_time));
  112. // Do we need to wrap around to the next day's hours?
  113. if($from_hour > $to_hour) { // yes
  114. $to_hour += 24; // add 24 hrs to the destination (1am = 25th hour)
  115. }
  116. // Are we in the right 24 hourly range?
  117. if((integer)$current_hour >= $from_hour && (integer)$current_hour <= $to_hour) {
  118. // If we're in the first hour, are we minutes early?
  119. if($current_hour==$from_hour && (integer)$current_min < $from_min)
  120. break;
  121. // If we're in the last hour, are we minutes late?
  122. if($current_hour==$to_hour && (integer)$current_min > $to_min)
  123. break;
  124. $passed++;
  125. }
  126. break;
  127. case 'type':
  128. if(($is_new && 0 == strcasecmp($value,'new'))
  129. || (!$is_new && 0 == strcasecmp($value,'reply')))
  130. $passed++;
  131. break;
  132. case 'from':
  133. $regexp_from = DevblocksPlatform::strToRegExp($value);
  134. if(preg_match($regexp_from, $fromInst->email)) {
  135. $passed++;
  136. }
  137. break;
  138. case 'tocc':
  139. $tocc = array();
  140. $destinations = DevblocksPlatform::parseCsvString($value);
  141. // Build a list of To/Cc addresses on this message
  142. @$to_list = imap_rfc822_parse_adrlist($headers['to'],'localhost');
  143. @$cc_list = imap_rfc822_parse_adrlist($headers['cc'],'localhost');
  144. if(is_array($to_list))
  145. foreach($to_list as $addy) {
  146. $tocc[] = $addy->mailbox . '@' . $addy->host;
  147. }
  148. if(is_array($cc_list))
  149. foreach($cc_list as $addy) {
  150. $tocc[] = $addy->mailbox . '@' . $addy->host;
  151. }
  152. $dest_flag = false; // bail out when true
  153. if(is_array($destinations) && is_array($tocc))
  154. foreach($destinations as $dest) {
  155. if($dest_flag) break;
  156. $regexp_dest = DevblocksPlatform::strToRegExp($dest);
  157. foreach($tocc as $addy) {
  158. if(@preg_match($regexp_dest, $addy)) {
  159. $passed++;
  160. $dest_flag = false;
  161. break;
  162. }
  163. }
  164. }
  165. break;
  166. case 'header1':
  167. case 'header2':
  168. case 'header3':
  169. case 'header4':
  170. case 'header5':
  171. $header = strtolower($rule['header']);
  172. if(empty($value)) { // we're checking for null/blanks
  173. if(!isset($headers[$header]) || empty($headers[$header])) {
  174. $passed++;
  175. }
  176. } elseif(isset($headers[$header]) && !empty($headers[$header])) {
  177. $regexp_header = DevblocksPlatform::strToRegExp($value);
  178. // handle arrays like Received: and (broken)Content-Type headers (farking spammers)
  179. if(is_array($headers[$header])) {
  180. foreach($headers[$header] as $array_header) {
  181. if(preg_match($regexp_header, str_replace(array("\r","\n"),' ',$array_header))) {
  182. $passed++;
  183. break;
  184. }
  185. }
  186. } else {
  187. // Flatten CRLF
  188. if(preg_match($regexp_header, str_replace(array("\r","\n"),' ',$headers[$header]))) {
  189. $passed++;
  190. }
  191. }
  192. }
  193. break;
  194. case 'body':
  195. // Line-by-line body scanning (sed-like)
  196. $lines = preg_split("/[\r\n]/", $message->body);
  197. if(is_array($lines))
  198. foreach($lines as $line) {
  199. if(@preg_match($value, $line)) {
  200. $passed++;
  201. break;
  202. }
  203. }
  204. break;
  205. case 'body_encoding':
  206. $regexp_bodyenc = DevblocksPlatform::strToRegExp($value);
  207. if(preg_match($regexp_bodyenc, $message->body_encoding))
  208. $passed++;
  209. break;
  210. case 'attachment':
  211. $regexp_file = DevblocksPlatform::strToRegExp($value);
  212. // check the files in the raw message
  213. foreach($message->files as $file_name => $file) { /* @var $file ParserFile */
  214. if(preg_match($regexp_file, $file_name)) {
  215. $passed++;
  216. break;
  217. }
  218. }
  219. break;
  220. default: // ignore invalids
  221. // Custom Fields
  222. if(0==strcasecmp('cf_',substr($rule_key,0,3))) {
  223. $field_id = substr($rule_key,3);
  224. // Make sure it exists
  225. if(null == (@$field = $custom_fields[$field_id]))
  226. continue;
  227. // Lazy values loader
  228. $field_values = array();
  229. switch($field->source_extension) {
  230. case ChCustomFieldSource_Address::ID:
  231. if(null == $address_field_values)
  232. $address_field_values = array_shift(DAO_CustomFieldValue::getValuesBySourceIds(ChCustomFieldSource_Address::ID, $fromInst->id));
  233. $field_values =& $address_field_values;
  234. break;
  235. case ChCustomFieldSource_Org::ID:
  236. if(null == $org_field_values)
  237. $org_field_values = array_shift(DAO_CustomFieldValue::getValuesBySourceIds(ChCustomFieldSource_Org::ID, $fromInst->contact_org_id));
  238. $field_values =& $org_field_values;
  239. break;
  240. }
  241. // Type sensitive value comparisons
  242. // [TODO] Operators
  243. // [TODO] Highly redundant
  244. switch($field->type) {
  245. case 'S': // string
  246. case 'T': // clob
  247. case 'U': // URL
  248. $field_val = isset($field_values[$field_id]) ? $field_values[$field_id] : '';
  249. $oper = isset($rule['oper']) ? $rule['oper'] : "=";
  250. if($oper == "=" && @preg_match(DevblocksPlatform::strToRegExp($value, true), $field_val))
  251. $passed++;
  252. elseif($oper == "!=" && @!preg_match(DevblocksPlatform::strToRegExp($value, true), $field_val))
  253. $passed++;
  254. break;
  255. case 'N': // number
  256. if(!isset($field_values[$field_id]))
  257. break;
  258. $field_val = isset($field_values[$field_id]) ? $field_values[$field_id] : 0;
  259. $oper = isset($rule['oper']) ? $rule['oper'] : "=";
  260. if($oper=="=" && intval($field_val)==intval($value))
  261. $passed++;
  262. elseif($oper=="!=" && intval($field_val)!=intval($value))
  263. $passed++;
  264. elseif($oper==">" && intval($field_val) > intval($value))
  265. $passed++;
  266. elseif($oper=="<" && intval($field_val) < intval($value))
  267. $passed++;
  268. break;
  269. case 'E': // date
  270. $field_val = isset($field_values[$field_id]) ? intval($field_values[$field_id]) : 0;
  271. $from = isset($rule['from']) ? $rule['from'] : "0";
  272. $to = isset($rule['to']) ? $rule['to'] : "now";
  273. if(intval(@strtotime($from)) <= $field_val && intval(@strtotime($to)) >= $field_val) {
  274. $passed++;
  275. }
  276. break;
  277. case 'C': // checkbox
  278. $field_val = isset($field_values[$field_id]) ? $field_values[$field_id] : 0;
  279. if(intval($value)==intval($field_val))
  280. $passed++;
  281. break;
  282. case 'D': // dropdown
  283. case 'M': // multi-picklist
  284. case 'X': // multi-checkbox
  285. case 'W': // worker
  286. $field_val = isset($field_values[$field_id]) ? $field_values[$field_id] : array();
  287. if(!is_array($value)) $value = array($value);
  288. if(is_array($field_val)) { // if multiple things set
  289. foreach($field_val as $v) { // loop through possible
  290. if(isset($value[$v])) { // is any possible set?
  291. $passed++;
  292. break;
  293. }
  294. }
  295. } else { // single
  296. if(isset($value[$field_val])) { // is our set field in possibles?
  297. $passed++;
  298. break;
  299. }
  300. }
  301. break;
  302. }
  303. } elseif(isset($filter_criteria_exts[$rule_key])) { // criteria extensions
  304. try {
  305. $crit_ext = $filter_criteria_exts[$rule_key]->createInstance();
  306. if($crit_ext->matches($filter, $message)) {
  307. $passed++;
  308. break;
  309. }
  310. } catch(Exception $e) {
  311. // Oops!
  312. //print_r($e);
  313. }
  314. }
  315. break;
  316. }
  317. }
  318. // If our rule matched every criteria, stop and return the filter
  319. if($passed == count($filter->criteria)) {
  320. DAO_PreParseRule::increment($filter->id); // ++ the times we've matched
  321. $matches[] = $filter;
  322. // Check our actions and see if we should bail out early
  323. if(isset($filter->actions) && !empty($filter->actions))
  324. foreach($filter->actions as $action_key => $action) {
  325. switch($action_key) {
  326. case 'nothing':
  327. case 'blackhole':
  328. case 'redirect':
  329. case 'bounce':
  330. return $matches;
  331. break;
  332. }
  333. }
  334. }
  335. }
  336. return $matches;
  337. }
  338. }
  339. class Model_GroupInboxFilter {
  340. public $id = 0;
  341. public $name = '';
  342. public $group_id = 0;
  343. public $criteria = array();
  344. public $actions = array();
  345. public $pos = 0;
  346. public $is_sticky = 0;
  347. public $sticky_order = 0;
  348. public $is_stackable = 0;
  349. /**
  350. * @return Model_GroupInboxFilter|false
  351. */
  352. static function getMatches($group_id, $ticket_id, $only_rule_id=0) {
  353. $matches = array();
  354. if(empty($group_id))
  355. return false;
  356. if(!empty($only_rule_id)) {
  357. $filters = array(
  358. DAO_GroupInboxFilter::get($only_rule_id)
  359. );
  360. } else {
  361. $filters = DAO_GroupInboxFilter::getByGroupId($group_id);
  362. }
  363. // Check the ticket
  364. if(null === ($ticket = DAO_Ticket::getTicket($ticket_id)))
  365. return false;
  366. // Build our objects
  367. $ticket_from = DAO_Address::get($ticket->last_wrote_address_id);
  368. $ticket_group_id = $ticket->team_id;
  369. // [TODO] These expensive checks should only populate when needed
  370. $messages = DAO_Ticket::getMessagesByTicket($ticket_id);
  371. $message_headers = array();
  372. if(empty($messages))
  373. return false;
  374. if(null != (@$message_last = array_pop($messages))) { /* @var $message_last CerberusMessage */
  375. $message_headers = $message_last->getHeaders();
  376. }
  377. // Clear the rest of the message manifests
  378. unset($messages);
  379. $custom_fields = DAO_CustomField::getAll();
  380. // Lazy load when needed on criteria basis
  381. $ticket_field_values = null;
  382. $address_field_values = null;
  383. $org_field_values = null;
  384. // Check filters
  385. if(is_array($filters))
  386. foreach($filters as $filter) { /* @var $filter Model_GroupInboxFilter */
  387. $passed = 0;
  388. // Skip filters with no criteria
  389. if(!is_array($filter->criteria) || empty($filter->criteria))
  390. continue;
  391. // check criteria
  392. foreach($filter->criteria as $rule_key => $rule) {
  393. @$value = $rule['value'];
  394. switch($rule_key) {
  395. case 'dayofweek':
  396. $current_day = strftime('%w');
  397. // $current_day = 1;
  398. // Forced to English abbrevs as indexes
  399. $days = array('sun','mon','tue','wed','thu','fri','sat');
  400. // Is the current day enabled?
  401. if(isset($rule[$days[$current_day]])) {
  402. $passed++;
  403. }
  404. break;
  405. case 'timeofday':
  406. $current_hour = strftime('%H');
  407. $current_min = strftime('%M');
  408. // $current_hour = 17;
  409. // $current_min = 5;
  410. if(null != ($from_time = @$rule['from']))
  411. list($from_hour, $from_min) = explode(':', $from_time);
  412. if(null != ($to_time = @$rule['to']))
  413. if(list($to_hour, $to_min) = explode(':', $to_time));
  414. // Do we need to wrap around to the next day's hours?
  415. if($from_hour > $to_hour) { // yes
  416. $to_hour += 24; // add 24 hrs to the destination (1am = 25th hour)
  417. }
  418. // Are we in the right 24 hourly range?
  419. if((integer)$current_hour >= $from_hour && (integer)$current_hour <= $to_hour) {
  420. // If we're in the first hour, are we minutes early?
  421. if($current_hour==$from_hour && (integer)$current_min < $from_min)
  422. break;
  423. // If we're in the last hour, are we minutes late?
  424. if($current_hour==$to_hour && (integer)$current_min > $to_min)
  425. break;
  426. $passed++;
  427. }
  428. break;
  429. case 'tocc':
  430. $tocc = array();
  431. $destinations = DevblocksPlatform::parseCsvString($value);
  432. // Build a list of To/Cc addresses on this message
  433. @$to_list = imap_rfc822_parse_adrlist($message_headers['to'],'localhost');
  434. @$cc_list = imap_rfc822_parse_adrlist($message_headers['cc'],'localhost');
  435. if(is_array($to_list))
  436. foreach($to_list as $addy) {
  437. $tocc[] = $addy->mailbox . '@' . $addy->host;
  438. }
  439. if(is_array($cc_list))
  440. foreach($cc_list as $addy) {
  441. $tocc[] = $addy->mailbox . '@' . $addy->host;
  442. }
  443. $dest_flag = false; // bail out when true
  444. if(is_array($destinations) && is_array($tocc))
  445. foreach($destinations as $dest) {
  446. if($dest_flag) break;
  447. $regexp_dest = DevblocksPlatform::strToRegExp($dest);
  448. foreach($tocc as $addy) {
  449. if(@preg_match($regexp_dest, $addy)) {
  450. $passed++;
  451. $dest_flag = false;
  452. break;
  453. }
  454. }
  455. }
  456. break;
  457. case 'from':
  458. $regexp_from = DevblocksPlatform::strToRegExp($value);
  459. if(@preg_match($regexp_from, $ticket_from->email)) {
  460. $passed++;
  461. }
  462. break;
  463. case 'subject':
  464. $regexp_subject = DevblocksPlatform::strToRegExp($value);
  465. if(@preg_match($regexp_subject, $ticket->subject)) {
  466. $passed++;
  467. }
  468. break;
  469. case 'body':
  470. if(null == ($message_body = $message_last->getContent()))
  471. break;
  472. // Line-by-line body scanning (sed-like)
  473. $lines = preg_split("/[\r\n]/", $message_body);
  474. if(is_array($lines))
  475. foreach($lines as $line) {
  476. if(@preg_match($value, $line)) {
  477. $passed++;
  478. break;
  479. }
  480. }
  481. break;
  482. case 'header1':
  483. case 'header2':
  484. case 'header3':
  485. case 'header4':
  486. case 'header5':
  487. @$header = strtolower($rule['header']);
  488. if(empty($header)) {
  489. $passed++;
  490. break;
  491. }
  492. if(empty($value)) { // we're checking for null/blanks
  493. if(!isset($message_headers[$header]) || empty($message_headers[$header])) {
  494. $passed++;
  495. }
  496. } elseif(isset($message_headers[$header]) && !empty($message_headers[$header])) {
  497. $regexp_header = DevblocksPlatform::strToRegExp($value);
  498. // Flatten CRLF
  499. if(@preg_match($regexp_header, str_replace(array("\r","\n"),' ',$message_headers[$header]))) {
  500. $passed++;
  501. }
  502. }
  503. break;
  504. default: // ignore invalids
  505. // Custom Fields
  506. if(0==strcasecmp('cf_',substr($rule_key,0,3))) {
  507. $field_id = substr($rule_key,3);
  508. // Make sure it exists
  509. if(null == (@$field = $custom_fields[$field_id]))
  510. continue;
  511. // Lazy values loader
  512. $field_values = array();
  513. switch($field->source_extension) {
  514. case ChCustomFieldSource_Address::ID:
  515. if(null == $address_field_values)
  516. $address_field_values = array_shift(DAO_CustomFieldValue::getValuesBySourceIds(ChCustomFieldSource_Address::ID, $ticket_from->id));
  517. $field_values =& $address_field_values;
  518. break;
  519. case ChCustomFieldSource_Org::ID:
  520. if(null == $org_field_values)
  521. $org_field_values = array_shift(DAO_CustomFieldValue::getValuesBySourceIds(ChCustomFieldSource_Org::ID, $ticket_from->contact_org_id));
  522. $field_values =& $org_field_values;
  523. break;
  524. case ChCustomFieldSource_Ticket::ID:
  525. if(null == $ticket_field_values)
  526. $ticket_field_values = array_shift(DAO_CustomFieldValue::getValuesBySourceIds(ChCustomFieldSource_Ticket::ID, $ticket->id));
  527. $field_values =& $ticket_field_values;
  528. break;
  529. }
  530. // No values, default.
  531. // if(!isset($field_values[$field_id]))
  532. // continue;
  533. // Type sensitive value comparisons
  534. // [TODO] Operators
  535. switch($field->type) {
  536. case 'S': // string
  537. case 'T': // clob
  538. case 'U': // URL
  539. $field_val = isset($field_values[$field_id]) ? $field_values[$field_id] : '';
  540. $oper = isset($rule['oper']) ? $rule['oper'] : "=";
  541. if($oper == "=" && @preg_match(DevblocksPlatform::strToRegExp($value, true), $field_val))
  542. $passed++;
  543. elseif($oper == "!=" && @!preg_match(DevblocksPlatform::strToRegExp($value, true), $field_val))
  544. $passed++;
  545. break;
  546. case 'N': // number
  547. if(!isset($field_values[$field_id]))
  548. break;
  549. $field_val = intval($field_values[$field_id]);
  550. $oper = isset($rule['oper']) ? $rule['oper'] : "=";
  551. if($oper=="=" && $field_val == intval($value))
  552. $passed++;
  553. elseif($oper=="!=" && $field_val != intval($value))
  554. $passed++;
  555. elseif($oper==">" && $field_val > intval($value))
  556. $passed++;
  557. elseif($oper=="<" && $field_val < intval($value))
  558. $passed++;
  559. break;
  560. case 'E': // date
  561. $field_val = isset($field_values[$field_id]) ? intval($field_values[$field_id]) : 0;
  562. $from = isset($rule['from']) ? $rule['from'] : "0";
  563. $to = isset($rule['to']) ? $rule['to'] : "now";
  564. if(intval(@strtotime($from)) <= $field_val && intval(@strtotime($to)) >= $field_val) {
  565. $passed++;
  566. }
  567. break;
  568. case 'C': // checkbox
  569. $field_val = isset($field_values[$field_id]) ? $field_values[$field_id] : 0;
  570. if(intval($value)==intval($field_val))
  571. $passed++;
  572. break;
  573. case 'D': // dropdown
  574. case 'X': // multi-checkbox
  575. case 'M': // multi-picklist
  576. case 'W': // worker
  577. $field_val = isset($field_values[$field_id]) ? $field_values[$field_id] : array();
  578. if(!is_array($value)) $value = array($value);
  579. if(is_array($field_val)) { // if multiple things set
  580. foreach($field_val as $v) { // loop through possible
  581. if(isset($value[$v])) { // is any possible set?
  582. $passed++;
  583. break;
  584. }
  585. }
  586. } else { // single
  587. if(isset($value[$field_val])) { // is our set field in possibles?
  588. $passed++;
  589. break;
  590. }
  591. }
  592. break;
  593. }
  594. }
  595. break;
  596. }
  597. }
  598. // If our rule matched every criteria, stop and return the filter
  599. if($passed == count($filter->criteria)) {
  600. DAO_GroupInboxFilter::increment($filter->id); // ++ the times we've matched
  601. $matches[$filter->id] = $filter;
  602. // If we're not stackable anymore, bail out.
  603. if(!$filter->is_stackable)
  604. return $matches;
  605. }
  606. }
  607. // If last rule was still stackable...
  608. if(!empty($matches))
  609. return $matches;
  610. // No matches
  611. return false;
  612. }
  613. /**
  614. * @param integer[] $ticket_ids
  615. */
  616. function run($ticket_ids) {
  617. $fields = array();
  618. $field_values = array();
  619. $groups = DAO_Group::getAll();
  620. $buckets = DAO_Bucket::getAll();
  621. $workers = DAO_Worker::getAll();
  622. $custom_fields = DAO_CustomField::getAll();
  623. // actions
  624. if(is_array($this->actions))
  625. foreach($this->actions as $action => $params) {
  626. switch($action) {
  627. case 'status':
  628. if(isset($params['is_waiting']))
  629. $fields[DAO_Ticket::IS_WAITING] = intval($params['is_waiting']);
  630. if(isset($params['is_closed']))
  631. $fields[DAO_Ticket::IS_CLOSED] = intval($params['is_closed']);
  632. if(isset($params['is_deleted']))
  633. $fields[DAO_Ticket::IS_DELETED] = intval($params['is_deleted']);
  634. break;
  635. case 'assign':
  636. if(isset($params['worker_id'])) {
  637. $w_id = intval($params['worker_id']);
  638. if(0 == $w_id || isset($workers[$w_id]))
  639. $fields[DAO_Ticket::NEXT_WORKER_ID] = $w_id;
  640. }
  641. break;
  642. case 'move':
  643. if(isset($params['group_id']) && isset($params['bucket_id'])) {
  644. $g_id = intval($params['group_id']);
  645. $b_id = intval($params['bucket_id']);
  646. if(isset($groups[$g_id]) && (0==$b_id || isset($buckets[$b_id]))) {
  647. $fields[DAO_Ticket::TEAM_ID] = $g_id;
  648. $fields[DAO_Ticket::CATEGORY_ID] = $b_id;
  649. }
  650. }
  651. break;
  652. case 'spam':
  653. if(isset($params['is_spam'])) {
  654. if(intval($params['is_spam'])) {
  655. foreach($ticket_ids as $ticket_id)
  656. CerberusBayes::markTicketAsSpam($ticket_id);
  657. } else {
  658. foreach($ticket_ids as $ticket_id)
  659. CerberusBayes::markTicketAsNotSpam($ticket_id);
  660. }
  661. }
  662. break;
  663. default:
  664. // Custom fields
  665. if(substr($action,0,3)=="cf_") {
  666. $field_id = intval(substr($action,3));
  667. if(!isset($custom_fields[$field_id]) || !isset($params['value']))
  668. break;
  669. $field_values[$field_id] = $params;
  670. }
  671. break;
  672. }
  673. }
  674. if(!empty($ticket_ids)) {
  675. if(!empty($fields))
  676. DAO_Ticket::updateTicket($ticket_ids, $fields);
  677. // Custom Fields
  678. C4_AbstractView::_doBulkSetCustomFields(ChCustomFieldSource_Ticket::ID, $field_values, $ticket_ids);
  679. }
  680. }
  681. };
  682. /**
  683. * Enter description here...
  684. *
  685. */
  686. abstract class C4_AbstractView {
  687. public $id = 0;
  688. public $name = "";
  689. public $view_columns = array();
  690. public $params = array();
  691. public $renderPage = 0;
  692. public $renderLimit = 10;
  693. public $renderSortBy = '';
  694. public $renderSortAsc = 1;
  695. function getData() {
  696. }
  697. function render() {
  698. echo ' '; // Expect Override
  699. }
  700. function renderCriteria($field) {
  701. echo ' '; // Expect Override
  702. }
  703. protected function _renderCriteriaCustomField($tpl, $field_id) {
  704. $field = DAO_CustomField::get($field_id);
  705. $tpl_path = APP_PATH . '/features/cerberusweb.core/templates/';
  706. switch($field->type) {
  707. case Model_CustomField::TYPE_DROPDOWN:
  708. case Model_CustomField::TYPE_MULTI_PICKLIST:
  709. case Model_CustomField::TYPE_MULTI_CHECKBOX:
  710. $tpl->assign('field', $field);
  711. $tpl->display('file:' . $tpl_path . 'internal/views/criteria/__cfield_picklist.tpl');
  712. break;
  713. case Model_CustomField::TYPE_CHECKBOX:
  714. $tpl->display('file:' . $tpl_path . 'internal/views/criteria/__cfield_checkbox.tpl');
  715. break;
  716. case Model_CustomField::TYPE_DATE:
  717. $tpl->display('file:' . $tpl_path . 'internal/views/criteria/__date.tpl');
  718. break;
  719. case Model_CustomField::TYPE_NUMBER:
  720. $tpl->display('file:' . $tpl_path . 'internal/views/criteria/__number.tpl');
  721. break;
  722. case Model_CustomField::TYPE_WORKER:
  723. $tpl->assign('workers', DAO_Worker::getAllActive());
  724. $tpl->display('file:' . $tpl_path . 'internal/views/criteria/__worker.tpl');
  725. break;
  726. default:
  727. $tpl->display('file:' . $tpl_path . 'internal/views/criteria/__string.tpl');
  728. break;
  729. }
  730. }
  731. /**
  732. * Enter description here...
  733. *
  734. * @param string $field
  735. * @param string $oper
  736. * @param string $value
  737. * @abstract
  738. */
  739. function doSetCriteria($field, $oper, $value) {
  740. // Expect Override
  741. }
  742. protected function _doSetCriteriaCustomField($token, $field_id) {
  743. $field = DAO_CustomField::get($field_id);
  744. @$oper = DevblocksPlatform::importGPC($_POST['oper'],'string','');
  745. @$value = DevblocksPlatform::importGPC($_POST['value'],'string','');
  746. $criteria = null;
  747. switch($field->type) {
  748. case Model_CustomField::TYPE_DROPDOWN:
  749. case Model_CustomField::TYPE_MULTI_PICKLIST:
  750. case Model_CustomField::TYPE_MULTI_CHECKBOX:
  751. @$options = DevblocksPlatform::importGPC($_POST['options'],'array',array());
  752. if(!empty($options)) {
  753. $criteria = new DevblocksSearchCriteria($token,$oper,$options);
  754. } else {
  755. $criteria = new DevblocksSearchCriteria($token,DevblocksSearchCriteria::OPER_IS_NULL);
  756. }
  757. break;
  758. case Model_CustomField::TYPE_CHECKBOX:
  759. $criteria = new DevblocksSearchCriteria($token,$oper,!empty($value) ? 1 : 0);
  760. break;
  761. case Model_CustomField::TYPE_NUMBER:
  762. $criteria = new DevblocksSearchCriteria($token,$oper,intval($value));
  763. break;
  764. case Model_CustomField::TYPE_DATE:
  765. @$from = DevblocksPlatform::importGPC($_REQUEST['from'],'string','');
  766. @$to = DevblocksPlatform::importGPC($_REQUEST['to'],'string','');
  767. if(empty($from)) $from = 0;
  768. if(empty($to)) $to = 'today';
  769. $criteria = new DevblocksSearchCriteria($token,$oper,array($from,$to));
  770. break;
  771. case Model_CustomField::TYPE_WORKER:
  772. @$oper = DevblocksPlatform::importGPC($_REQUEST['oper'],'string','eq');
  773. @$worker_ids = DevblocksPlatform::importGPC($_POST['worker_id'],'array',array());
  774. $criteria = new DevblocksSearchCriteria($token,$oper,$worker_ids);
  775. break;
  776. default: // TYPE_SINGLE_LINE || TYPE_MULTI_LINE
  777. if(($oper == DevblocksSearchCriteria::OPER_LIKE || $oper == DevblocksSearchCriteria::OPER_NOT_LIKE)
  778. && false === (strpos($value,'*'))) {
  779. $value = '*'.$value.'*';
  780. }
  781. $criteria = new DevblocksSearchCriteria($token,$oper,$value);
  782. break;
  783. }
  784. return $criteria;
  785. }
  786. /**
  787. * This method automatically fixes any cached strange options, like
  788. * deleted custom fields.
  789. *
  790. */
  791. protected function _sanitize() {
  792. $fields = $this->getColumns();
  793. $custom_fields = DAO_CustomField::getAll();
  794. $needs_save = false;
  795. // Parameter sanity check
  796. if(is_array($this->params))
  797. foreach($this->params as $pidx => $null) {
  798. if(substr($pidx,0,3)!="cf_")
  799. continue;
  800. if(0 != ($cf_id = intval(substr($pidx,3)))) {
  801. // Make sure our custom fields still exist
  802. if(!isset($custom_fields[$cf_id])) {
  803. unset($this->params[$pidx]);
  804. $needs_save = true;
  805. }
  806. }
  807. }
  808. // View column sanity check
  809. if(is_array($this->view_columns))
  810. foreach($this->view_columns as $cidx => $c) {
  811. // Custom fields
  812. if(substr($c,0,3) == "cf_") {
  813. if(0 != ($cf_id = intval(substr($c,3)))) {
  814. // Make sure our custom fields still exist
  815. if(!isset($custom_fields[$cf_id])) {
  816. unset($this->view_columns[$cidx]);
  817. $needs_save = true;
  818. }
  819. }
  820. } else {
  821. // If the column no longer exists (rare but worth checking)
  822. if(!isset($fields[$c])) {
  823. unset($this->view_columns[$cidx]);
  824. $needs_save = true;
  825. }
  826. }
  827. }
  828. // Sort by sanity check
  829. if(substr($this->renderSortBy,0,3)=="cf_") {
  830. if(0 != ($cf_id = intval(substr($this->renderSortBy,3)))) {
  831. if(!isset($custom_fields[$cf_id])) {
  832. $this->renderSortBy = null;
  833. $needs_save = true;
  834. }
  835. }
  836. }
  837. if($needs_save) {
  838. C4_AbstractViewLoader::setView($this->id, $this);
  839. }
  840. }
  841. function renderCriteriaParam($param) {
  842. $field = $param->field;
  843. $vals = $param->value;
  844. if(!is_array($vals))
  845. $vals = array($vals);
  846. // Do we need to do anything special on custom fields?
  847. if('cf_'==substr($field,0,3)) {
  848. $field_id = intval(substr($field,3));
  849. $custom_fields = DAO_CustomField::getAll();
  850. switch($custom_fields[$field_id]->type) {
  851. case Model_CustomField::TYPE_WORKER:
  852. $workers = DAO_worker::getAll();
  853. foreach($vals as $idx => $worker_id) {
  854. if(isset($workers[$worker_id]))
  855. $vals[$idx] = $workers[$worker_id]->getName();
  856. }
  857. break;
  858. }
  859. }
  860. echo implode(', ', $vals);
  861. }
  862. /**
  863. * All the view's available fields
  864. *
  865. * @return array
  866. */
  867. static function getFields() {
  868. // Expect Override
  869. return array();
  870. }
  871. /**
  872. * All searchable fields
  873. *
  874. * @return array
  875. */
  876. static function getSearchFields() {
  877. // Expect Override
  878. return array();
  879. }
  880. /**
  881. * All fields that can be displayed as columns in the view
  882. *
  883. * @return array
  884. */
  885. static function getColumns() {
  886. // Expect Override
  887. return array();
  888. }
  889. function doCustomize($columns, $num_rows=10) {
  890. $this->renderLimit = $num_rows;
  891. $viewColumns = array();
  892. foreach($columns as $col) {
  893. if(empty($col))
  894. continue;
  895. $viewColumns[] = $col;
  896. }
  897. $this->view_columns = $viewColumns;
  898. }
  899. function doSortBy($sortBy) {
  900. $iSortAsc = intval($this->renderSortAsc);
  901. // [JAS]: If clicking the same header, toggle asc/desc.
  902. if(0 == strcasecmp($sortBy,$this->renderSortBy)) {
  903. $iSortAsc = (0 == $iSortAsc) ? 1 : 0;
  904. } else { // [JAS]: If a new header, start with asc.
  905. $iSortAsc = 1;
  906. }
  907. $this->renderSortBy = $sortBy;
  908. $this->renderSortAsc = $iSortAsc;
  909. }
  910. function doPage($page) {
  911. $this->renderPage = $page;
  912. }
  913. function doRemoveCriteria($field) {
  914. unset($this->params[$field]);
  915. $this->renderPage = 0;
  916. }
  917. function doResetCriteria() {
  918. $this->params = array();
  919. $this->renderPage = 0;
  920. }
  921. public static function _doBulkSetCustomFields($source_extension,$custom_fields, $ids) {
  922. $fields = DAO_CustomField::getAll();
  923. if(!empty($custom_fields))
  924. foreach($custom_fields as $cf_id => $params) {
  925. if(!is_array($params) || !isset($params['value']))
  926. continue;
  927. $cf_val = $params['value'];
  928. // Data massaging
  929. switch($fields[$cf_id]->type) {
  930. case Model_CustomField::TYPE_DATE:
  931. $cf_val = intval(@strtotime($cf_val));
  932. break;
  933. case Model_CustomField::TYPE_CHECKBOX:
  934. case Model_CustomField::TYPE_NUMBER:
  935. $cf_val = (0==strlen($cf_val)) ? '' : intval($cf_val);
  936. break;
  937. }
  938. // If multi-selection types, handle delta changes
  939. if(Model_CustomField::TYPE_MULTI_PICKLIST==$fields[$cf_id]->type
  940. || Model_CustomField::TYPE_MULTI_CHECKBOX==$fields[$cf_id]->type) {
  941. if(is_array($cf_val))
  942. foreach($cf_val as $val) {
  943. $op = substr($val,0,1);
  944. $val = substr($val,1);
  945. if(is_array($ids))
  946. foreach($ids as $id) {
  947. if($op=='+')
  948. DAO_CustomFieldValue::setFieldValue($source_extension,$id,$cf_id,$val,true);
  949. elseif($op=='-')
  950. DAO_CustomFieldValue::unsetFieldValue($source_extension,$id,$cf_id,$val);
  951. }
  952. }
  953. // Otherwise, set/unset as a single field
  954. } else {
  955. if(is_array($ids))
  956. foreach($ids as $id) {
  957. if(0 != strlen($cf_val))
  958. DAO_CustomFieldValue::setFieldValue($source_extension,$id,$cf_id,$cf_val);
  959. else
  960. DAO_CustomFieldValue::unsetFieldValue($source_extension,$id,$cf_id);
  961. }
  962. }
  963. }
  964. }
  965. };
  966. /**
  967. * Used to persist a C4_AbstractView instance and not be encumbered by
  968. * classloading issues (out of the session) from plugins that might have
  969. * concrete AbstractView implementations.
  970. */
  971. class C4_AbstractViewModel {
  972. public $class_name = '';
  973. public $id = 0;
  974. public $name = "";
  975. public $view_columns = array();
  976. public $params = array();
  977. public $renderPage = 0;
  978. public $renderLimit = 10;
  979. public $renderSortBy = '';
  980. public $renderSortAsc = 1;
  981. };
  982. /**
  983. * This is essentially an AbstractView Factory
  984. */
  985. class C4_AbstractViewLoader {
  986. static $views = null;
  987. const VISIT_ABSTRACTVIEWS = 'abstractviews_list';
  988. static private function _init() {
  989. $visit = CerberusApplication::getVisit();
  990. self::$views = $visit->get(self::VISIT_ABSTRACTVIEWS,array());
  991. }
  992. /**
  993. * @param string $view_label Abstract view identifier
  994. * @return boolean
  995. */
  996. static function exists($view_label) {
  997. if(is_null(self::$views)) self::_init();
  998. return isset(self::$views[$view_label]);
  999. }
  1000. /**
  1001. * Enter description here...
  1002. *
  1003. * @param string $class C4_AbstractView
  1004. * @param string $view_label ID
  1005. * @return C4_AbstractView instance
  1006. */
  1007. static function getView($view_label, C4_AbstractViewModel $defaults=null) {
  1008. $active_worker = CerberusApplication::getActiveWorker();
  1009. if(is_null(self::$views)) self::_init();
  1010. if(self::exists($view_label)) {
  1011. $model = self::$views[$view_label];
  1012. return self::unserializeAbstractView($model);
  1013. } else {
  1014. // See if the worker has their own saved prefs
  1015. @$prefs = unserialize(DAO_WorkerPref::get($active_worker->id, 'view'.$view_label));
  1016. // If no worker prefsd, check if we're passed defaults
  1017. if((empty($prefs) || !$prefs instanceof C4_AbstractViewModel) && !empty($defaults))
  1018. $prefs = $defaults;
  1019. // Create a default view if it doesn't exist
  1020. if(!empty($prefs) && $prefs instanceof C4_AbstractViewModel) {
  1021. if(!empty($prefs->class_name) || class_exists($prefs->class_name)) {
  1022. $view = new $prefs->class_name;
  1023. $view->id = $view_label;
  1024. if(!empty($prefs->view_columns))
  1025. $view->view_columns = $prefs->view_columns;
  1026. if(!empty($prefs->renderLimit))
  1027. $view->renderLimit = $prefs->renderLimit;
  1028. if(null !== $prefs->renderSortBy)
  1029. $view->renderSortBy = $prefs->renderSortBy;
  1030. if(null !== $prefs->renderSortAsc)
  1031. $view->renderSortAsc = $prefs->renderSortAsc;
  1032. self::setView($view_label, $view);
  1033. return $view;
  1034. }
  1035. }
  1036. }
  1037. return null;
  1038. }
  1039. /**
  1040. * Enter description here...
  1041. *
  1042. * @param string $class C4_AbstractView
  1043. * @param string $view_label ID
  1044. * @param C4_AbstractView $view
  1045. */
  1046. static function setView($view_label, $view) {
  1047. if(is_null(self::$views)) self::_init();
  1048. self::$views[$view_label] = self::serializeAbstractView($view);
  1049. self::_save();
  1050. }
  1051. static function deleteView($view_label) {
  1052. unset(self::$views[$view_label]);
  1053. self::_save();
  1054. }
  1055. static private function _save() {
  1056. // persist
  1057. $visit = CerberusApplication::getVisit();
  1058. $visit->set(self::VISIT_ABSTRACTVIEWS, self::$views);
  1059. }
  1060. static function serializeAbstractView($view) {
  1061. if(!$view instanceof C4_AbstractView) {
  1062. return null;
  1063. }
  1064. $model = new C4_AbstractViewModel();
  1065. $model->class_name = get_class($view);
  1066. $model->id = $view->id;
  1067. $model->name = $view->name;
  1068. $model->view_columns = $view->view_columns;
  1069. $model->params = $view->params;
  1070. $model->renderPage = $view->renderPage;
  1071. $model->renderLimit = $view->renderLimit;
  1072. $model->renderSortBy = $view->renderSortBy;
  1073. $model->renderSortAsc = $view->renderSortAsc;
  1074. return $model;
  1075. }
  1076. static function unserializeAbstractView(C4_AbstractViewModel $model) {
  1077. if(!class_exists($model->class_name, true))
  1078. return null;
  1079. if(null == ($inst = new $model->class_name))
  1080. return null;
  1081. /* @var $inst C4_AbstractView */
  1082. $inst->id = $model->id;
  1083. $inst->name = $model->name;
  1084. $inst->view_columns = $model->view_columns;
  1085. $inst->params = $model->params;
  1086. $inst->renderPage = $model->renderPage;
  1087. $inst->renderLimit = $model->renderLimit;
  1088. $inst->renderSortBy = $model->renderSortBy;
  1089. $inst->renderSortAsc = $model->renderSortAsc;
  1090. return $inst;
  1091. }
  1092. };
  1093. class Model_Address {
  1094. public $id;
  1095. public $email = '';
  1096. public $first_name = '';
  1097. public $last_name = '';
  1098. public $contact_org_id = 0;
  1099. public $num_spam = 0;
  1100. public $num_nonspam = 0;
  1101. public $is_banned = 0;
  1102. public $is_registered = 0;
  1103. public $pass = '';
  1104. public $last_autoreply;
  1105. function Model_Address() {}
  1106. function getName() {
  1107. return sprintf("%s%s%s",
  1108. $this->first_name,
  1109. (!empty($this->first_name) && !empty($this->last_name)) ? " " : "",
  1110. $this->last_name
  1111. );
  1112. }
  1113. };
  1114. class Model_AddressToWorker {
  1115. public $address;
  1116. public $worker_id;
  1117. public $is_confirmed;
  1118. public $code;
  1119. public $code_expire;
  1120. }
  1121. class View_DevblocksTemplate extends C4_AbstractView {
  1122. const DEFAULT_ID = 'templates';
  1123. function __construct() {
  1124. $this->id = self::DEFAULT_ID;
  1125. $this->name = 'Templates';
  1126. $this->renderLimit = 25;
  1127. $this->renderSortBy = SearchFields_DevblocksTemplate::PATH;
  1128. $this->renderSortAsc = true;
  1129. $this->view_columns = array(
  1130. SearchFields_DevblocksTemplate::PLUGIN_ID,
  1131. // SearchFields_DevblocksTemplate::TAG,
  1132. SearchFields_DevblocksTemplate::LAST_UPDATED,
  1133. );
  1134. $this->doResetCriteria();
  1135. }
  1136. function getData() {
  1137. return DAO_DevblocksTemplate::search(
  1138. $this->view_columns,
  1139. $this->params,
  1140. $this->renderLimit,
  1141. $this->renderPage,
  1142. $this->renderSortBy,
  1143. $this->renderSortAsc
  1144. );
  1145. }
  1146. function render() {
  1147. $this->_sanitize();
  1148. $tpl = DevblocksPlatform::getTemplateService();
  1149. $tpl->assign('id', $this->id);
  1150. $tpl->assign('view', $this);
  1151. // $custom_fields = DAO_CustomField::getBySource(ChCustomFieldSource_Worker::ID);
  1152. // $tpl->assign('custom_fields', $custom_fields);
  1153. $tpl->assign('view_fields', $this->getColumns());
  1154. $tpl->display('file:' . APP_PATH . '/features/usermeet.core/templates/community/display/tabs/templates/view.tpl');
  1155. }
  1156. function renderCriteria($field) {
  1157. $tpl = DevblocksPlatform::getTemplateService();
  1158. $tpl->assign('id', $this->id);
  1159. switch($field) {
  1160. case SearchFields_DevblocksTemplate::CONTENT:
  1161. case SearchFields_DevblocksTemplate::PATH:
  1162. case SearchFields_DevblocksTemplate::PLUGIN_ID:
  1163. case SearchFields_DevblocksTemplate::TAG:
  1164. $tpl->display('file:' . APP_PATH . '/features/cerberusweb.core/templates/internal/views/criteria/__string.tpl');
  1165. break;
  1166. case SearchFields_DevblocksTemplate::LAST_UPDATED:
  1167. $tpl->display('file:' . APP_PATH . '/features/cerberusweb.core/templates/internal/views/criteria/__date.tpl');
  1168. break;
  1169. default:
  1170. // Custom Fields
  1171. // if('cf_' == substr($field,0,3)) {
  1172. // $this->_renderCriteriaCustomField($tpl, substr($field,3));
  1173. // } else {
  1174. // echo ' ';
  1175. // }
  1176. break;
  1177. }
  1178. }
  1179. function renderCriteriaParam($param) {
  1180. $field = $param->field;
  1181. $values = !is_array($param->value) ? array($param->value) : $param->value;
  1182. switch($field) {
  1183. // case SearchFields_WorkerEvent::WORKER_ID:
  1184. // $workers = DAO_Worker::getAll();
  1185. // $strings = array();
  1186. //
  1187. // foreach($values as $val) {
  1188. // if(empty($val))
  1189. // $strings[] = "Nobody";
  1190. // elseif(!isset($workers[$val]))
  1191. // continue;
  1192. // else
  1193. // $strings[] = $workers[$val]->getName();
  1194. // }
  1195. // echo implode(", ", $strings);
  1196. // break;
  1197. default:
  1198. parent::renderCriteriaParam($param);
  1199. break;
  1200. }
  1201. }
  1202. static function getFields() {
  1203. return SearchFields_DevblocksTemplate::getFields();
  1204. }
  1205. static function getSearchFields() {
  1206. $fields = self::getFields();
  1207. return $fields;
  1208. }
  1209. static function getColumns() {
  1210. $fields = self::getFields();
  1211. return $fields;
  1212. }
  1213. function doResetCriteria() {
  1214. parent::doResetCriteria();
  1215. // $this->params = array(
  1216. // SearchFields_WorkerEvent::NUM_NONSPAM => new DevblocksSearchCriteria(SearchFields_WorkerEvent::NUM_NONSPAM,'>',0),
  1217. // );
  1218. }
  1219. function doSetCriteria($field, $oper, $value) {
  1220. $criteria = null;
  1221. switch($field) {
  1222. case SearchFields_DevblocksTemplate::CONTENT:
  1223. case SearchFields_DevblocksTemplate::PATH:
  1224. case SearchFields_DevblocksTemplate::PLUGIN_ID:
  1225. case SearchFields_DevblocksTemplate::TAG:
  1226. // force wildcards if none used on a LIKE
  1227. if(($oper == DevblocksSearchCriteria::OPER_LIKE || $oper == DevblocksSearchCriteria::OPER_NOT_LIKE)
  1228. && false === (strpos($value,'*'))) {
  1229. $value = '*'.$value.'*';
  1230. }
  1231. $criteria = new DevblocksSearchCriteria($field, $oper, $value);
  1232. break;
  1233. case SearchFields_DevblocksTemplate::LAST_UPDATED:
  1234. @$from = DevblocksPlatform::importGPC($_REQUEST['from'],'string','');
  1235. @$to = DevblocksPlatform::importGPC($_REQUEST['to'],'string','');
  1236. if(empty($from)) $from = 0;
  1237. if(empty($to)) $to = 'today';
  1238. $criteria = new DevblocksSearchCriteria($field,$oper,array($from,$to));
  1239. break;
  1240. default:
  1241. // Custom Fields
  1242. // if(substr($field,0,3)=='cf_') {
  1243. // $criteria = $this->_doSetCriteriaCustomField($field, substr($field,3));
  1244. // }
  1245. break;
  1246. }
  1247. if(!empty($criteria)) {
  1248. $this->params[$field] = $criteria;
  1249. $this->renderPage = 0;
  1250. }
  1251. }
  1252. function doBulkUpdate($filter, $do, $ids=array()) {
  1253. @set_time_limit(600); // [TODO] Temp!
  1254. $change_fields = array();
  1255. $deleted = false;
  1256. $custom_fields = array();
  1257. if(empty($do))
  1258. return;
  1259. // Make sure we have checked items if we want a checked list
  1260. if(0 == strcasecmp($filter,"checks") && empty($ids))
  1261. return;
  1262. if(is_array($do))
  1263. foreach($do as $k => $v) {
  1264. switch($k) {
  1265. case 'deleted':
  1266. $deleted = true;
  1267. break;
  1268. default:
  1269. // Custom fields
  1270. if(substr($k,0,3)=="cf_") {
  1271. $custom_fields[substr($k,3)] = $v;
  1272. }
  1273. break;
  1274. }
  1275. }
  1276. $pg = 0;
  1277. if(empty($ids))
  1278. do {
  1279. list($objects,$null) = DAO_DevblocksTemplate::search(
  1280. array(),
  1281. $this->params,
  1282. 100,
  1283. $pg++,
  1284. DAO_DevblocksTemplate::ID,
  1285. true,
  1286. false
  1287. );
  1288. $ids = array_merge($ids, array_keys($objects));
  1289. } while(!empty($objects));
  1290. $batch_total = count($ids);
  1291. for($x=0;$x<=$batch_total;$x+=100) {
  1292. $batch_ids = array_slice($ids,$x,100);
  1293. if(!$deleted)
  1294. DAO_DevblocksTemplate::update($batch_ids, $change_fields);
  1295. else
  1296. DAO_DevblocksTemplate::delete($batch_ids);
  1297. // Custom Fields
  1298. // self::_doBulkSetCustomFields(ChCustomFieldSource_Worker::ID, $custom_fields, $batch_ids);
  1299. unset($batch_ids);
  1300. }
  1301. if($deleted) {
  1302. // Clear template cache
  1303. $tpl = DevblocksPlatform::getTemplateService();
  1304. $tpl->clear_compiled_tpl();
  1305. }
  1306. unset($ids);
  1307. }
  1308. };
  1309. class C4_TicketView extends C4_AbstractView {
  1310. const DEFAULT_ID = 'tickets_workspace';
  1311. function __construct() {
  1312. $this->id = self::DEFAULT_ID;
  1313. $this->name = 'Tickets';
  1314. $this->renderLimit = 10;
  1315. $this->renderSortBy = SearchFields_Ticket::TICKET_UPDATED_DATE;
  1316. $this->renderSortAsc = false;
  1317. $this->view_columns = array(
  1318. SearchFields_Ticket::TICKET_LAST_ACTION_CODE,
  1319. SearchFields_Ticket::TICKET_UPDATED_DATE,
  1320. SearchFields_Ticket::TICKET_TEAM_ID,
  1321. SearchFields_Ticket::TICKET_CATEGORY_ID,
  1322. SearchFields_Ticket::TICKET_SPAM_SCORE,
  1323. );
  1324. }
  1325. function getData() {
  1326. $objects = DAO_Ticket::search(
  1327. $this->view_columns,
  1328. $this->params,
  1329. $this->renderLimit,
  1330. $this->renderPage,
  1331. $this->renderSortBy,
  1332. $this->renderSortAsc
  1333. );
  1334. return $objects;
  1335. }
  1336. function render() {
  1337. $this->_sanitize();
  1338. $tpl = DevblocksPlatform::getTemplateService();
  1339. $tpl->assign('id', $this->id);
  1340. $view_path = APP_PATH . '/features/cerberusweb.core/templates/tickets/';
  1341. $tpl->assign('view_path',$view_path);
  1342. $tpl->assign('view', $this);
  1343. $visit = CerberusApplication::getVisit();
  1344. $results = self::getData();
  1345. $tpl->assign('results', $results);
  1346. @$ids = array_keys($results[0]);
  1347. $workers = DAO_Worker::getAll();
  1348. $tpl->assign('workers', $workers);
  1349. $teams = DAO_Group::getAll();
  1350. $tpl->assign('teams', $teams);
  1351. $buckets = DAO_Bucket::getAll();
  1352. $tpl->assign('buckets', $buckets);
  1353. $team_categories = DAO_Bucket::getTeams();
  1354. $tpl->assign('team_categories', $team_categories);
  1355. $custom_fields = DAO_CustomField::getBySource(ChCustomFieldSource_Ticket::ID);
  1356. $tpl->assign('custom_fields', $custom_fields);
  1357. // Undo?
  1358. $last_action = C4_TicketView::getLastAction($this->id);
  1359. $tpl->assign('last_action', $last_action);
  1360. if(!empty($last_action) && !is_null($last_action->ticket_ids)) {
  1361. $tpl->assign('last_action_count', count($last_action->ticket_ids));
  1362. }
  1363. $tpl->assign('timestamp_now', time());
  1364. $tpl->assign('view_fields', $this->getColumns());
  1365. $tpl->display('file:' . $view_path . 'ticket_view.tpl');
  1366. }
  1367. function doResetCriteria() {
  1368. $active_worker = CerberusApplication::getActiveWorker(); /* @var $active_worker CerberusWorker */
  1369. $active_worker_memberships = $active_worker->getMemberships();
  1370. $this->params = array(
  1371. SearchFields_Ticket::TICKET_CLOSED => new DevblocksSearchCriteria(SearchFields_Ticket::TICKET_CLOSED,'=',0),
  1372. SearchFields_Ticket::TICKET_TEAM_ID => new DevblocksSearchCriteria(SearchFields_Ticket::TICKET_TEAM_ID,'in',array_keys($active_worker_memberships)), // censor
  1373. );
  1374. $this->renderPage = 0;
  1375. }
  1376. function renderCriteria($field) {
  1377. $tpl = DevblocksPlatform::getTemplateService();
  1378. $tpl->assign('id', $this->id);
  1379. $tpl_path = APP_PATH . '/features/cerberusweb.core/templates/';
  1380. switch($field) {
  1381. case SearchFields_Ticket::TICKET_ID:
  1382. case SearchFields_Ticket::TICKET_MASK:
  1383. case SearchFields_Ticket::TICKET_SUBJECT:
  1384. case SearchFields_Ticket::TICKET_FIRST_WROTE:
  1385. case SearchFields_Ticket::TICKET_LAST_WROTE:
  1386. case SearchFields_Ticket::REQUESTER_ADDRESS:
  1387. case SearchFields_Ticket::TICKET_INTERESTING_WORDS:
  1388. case SearchFields_Ticket::ORG_NAME:
  1389. $tpl->display('file:' . $tpl_path . 'internal/views/criteria/__string.tpl');
  1390. break;
  1391. case SearchFields_Ticket::TICKET_MESSAGE_CONTENT:
  1392. $tpl->display('file:' . $tpl_path . 'internal/views/criteria/__fulltext.tpl');
  1393. break;
  1394. case SearchFields_Ticket::TICKET_FIRST_WROTE_SPAM:
  1395. case SearchFields_Ticket::TICKET_FIRST_WROTE_NONSPAM:
  1396. $tpl->display('file:' . $tpl_path . 'internal/views/criteria/__number.tpl');
  1397. break;
  1398. case SearchFields_Ticket::TICKET_WAITING:
  1399. case SearchFields_Ticket::TICKET_DELETED:
  1400. case SearchFields_Ticket::TICKET_CLOSED:
  1401. $tpl->display('file:' . $tpl_path . 'internal/views/criteria/__bool.tpl');
  1402. break;
  1403. case SearchFields_Ticket::TICKET_CREATED_DATE:
  1404. case SearchFields_Ticket::TICKET_UPDATED_DATE:
  1405. case SearchFields_Ticket::TICKET_DUE_DATE:
  1406. $tpl->display('file:' . $tpl_path . 'internal/views/criteria/__date.tpl');
  1407. break;
  1408. case SearchFields_Ticket::TICKET_SPAM_TRAINING:
  1409. $tpl->display('file:' . $tpl_path . 'tickets/search/criteria/ticket_spam_training.tpl');
  1410. break;
  1411. case SearchFields_Ticket::TICKET_SPAM_SCORE:
  1412. $tpl->display('file:' . $tpl_path . 'tickets/search/criteria/ticket_spam_score.tpl');
  1413. break;
  1414. case SearchFields_Ticket::TICKET_LAST_ACTION_CODE:
  1415. $tpl->display('file:' . $tpl_path . 'tickets/search/criteria/ticket_last_action.tpl');
  1416. break;
  1417. case SearchFields_Ticket::TICKET_NEXT_WORKER_ID:
  1418. case SearchFields_Ticket::TICKET_LAST_WORKER_ID:
  1419. $workers = DAO_Worker::getAll();
  1420. $tpl->assign('workers', $workers);
  1421. $tpl->display('file:' . $tpl_path . 'internal/views/criteria/__worker.tpl');
  1422. break;
  1423. case SearchFields_Ticket::TICKET_TEAM_ID:
  1424. $teams = DAO_Group::getAll();
  1425. $tpl->assign('teams', $teams);
  1426. $team_categories = DAO_Bucket::getTeams();
  1427. $tpl->assign('team_categories', $team_categories);
  1428. $tpl->display('file:' . $tpl_path . 'tickets/search/criteria/ticket_team.tpl');
  1429. break;
  1430. default:
  1431. // Custom Fields
  1432. if('cf_' == substr($field,0,3)) {
  1433. $this->_renderCriteria

Large files files are truncated, but you can click here to view the full file