PageRenderTime 79ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/api/Model.class.php

https://github.com/stolf/cerb4
PHP | 4177 lines | 3084 code | 672 blank | 421 comment | 482 complexity | 77b9835f1e57b833e6ee1210d1493ebc MD5 | raw 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 = DEVBLOCKS_PLUGIN_PATH . '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 C4_TicketView extends C4_AbstractView {
  1122. const DEFAULT_ID = 'tickets_workspace';
  1123. function __construct() {
  1124. $this->id = self::DEFAULT_ID;
  1125. $this->name = 'Tickets';
  1126. $this->renderLimit = 10;
  1127. $this->renderSortBy = SearchFields_Ticket::TICKET_UPDATED_DATE;
  1128. $this->renderSortAsc = false;
  1129. $this->view_columns = array(
  1130. SearchFields_Ticket::TICKET_LAST_ACTION_CODE,
  1131. SearchFields_Ticket::TICKET_UPDATED_DATE,
  1132. SearchFields_Ticket::TICKET_TEAM_ID,
  1133. SearchFields_Ticket::TICKET_CATEGORY_ID,
  1134. SearchFields_Ticket::TICKET_SPAM_SCORE,
  1135. );
  1136. }
  1137. function getData() {
  1138. $objects = DAO_Ticket::search(
  1139. $this->view_columns,
  1140. $this->params,
  1141. $this->renderLimit,
  1142. $this->renderPage,
  1143. $this->renderSortBy,
  1144. $this->renderSortAsc
  1145. );
  1146. return $objects;
  1147. }
  1148. function render() {
  1149. $this->_sanitize();
  1150. $tpl = DevblocksPlatform::getTemplateService();
  1151. $tpl->assign('id', $this->id);
  1152. $view_path = DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/tickets/';
  1153. $tpl->assign('view_path',$view_path);
  1154. $tpl->assign('view', $this);
  1155. $visit = CerberusApplication::getVisit();
  1156. $results = self::getData();
  1157. $tpl->assign('results', $results);
  1158. @$ids = array_keys($results[0]);
  1159. $workers = DAO_Worker::getAll();
  1160. $tpl->assign('workers', $workers);
  1161. $teams = DAO_Group::getAll();
  1162. $tpl->assign('teams', $teams);
  1163. $buckets = DAO_Bucket::getAll();
  1164. $tpl->assign('buckets', $buckets);
  1165. $team_categories = DAO_Bucket::getTeams();
  1166. $tpl->assign('team_categories', $team_categories);
  1167. $custom_fields = DAO_CustomField::getBySource(ChCustomFieldSource_Ticket::ID);
  1168. $tpl->assign('custom_fields', $custom_fields);
  1169. // Undo?
  1170. $last_action = C4_TicketView::getLastAction($this->id);
  1171. $tpl->assign('last_action', $last_action);
  1172. if(!empty($last_action) && !is_null($last_action->ticket_ids)) {
  1173. $tpl->assign('last_action_count', count($last_action->ticket_ids));
  1174. }
  1175. $tpl->assign('timestamp_now', time());
  1176. $tpl->cache_lifetime = "0";
  1177. $tpl->assign('view_fields', $this->getColumns());
  1178. $tpl->display('file:' . $view_path . 'ticket_view.tpl');
  1179. }
  1180. function doResetCriteria() {
  1181. $active_worker = CerberusApplication::getActiveWorker(); /* @var $active_worker CerberusWorker */
  1182. $active_worker_memberships = $active_worker->getMemberships();
  1183. $this->params = array(
  1184. SearchFields_Ticket::TICKET_CLOSED => new DevblocksSearchCriteria(SearchFields_Ticket::TICKET_CLOSED,'=',0),
  1185. SearchFields_Ticket::TICKET_TEAM_ID => new DevblocksSearchCriteria(SearchFields_Ticket::TICKET_TEAM_ID,'in',array_keys($active_worker_memberships)), // censor
  1186. );
  1187. $this->renderPage = 0;
  1188. }
  1189. function renderCriteria($field) {
  1190. $tpl = DevblocksPlatform::getTemplateService();
  1191. $tpl->assign('id', $this->id);
  1192. $tpl_path = DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/';
  1193. switch($field) {
  1194. case SearchFields_Ticket::TICKET_ID:
  1195. case SearchFields_Ticket::TICKET_MASK:
  1196. case SearchFields_Ticket::TICKET_SUBJECT:
  1197. case SearchFields_Ticket::TICKET_FIRST_WROTE:
  1198. case SearchFields_Ticket::TICKET_LAST_WROTE:
  1199. case SearchFields_Ticket::REQUESTER_ADDRESS:
  1200. case SearchFields_Ticket::TICKET_INTERESTING_WORDS:
  1201. case SearchFields_Ticket::ORG_NAME:
  1202. $tpl->display('file:' . $tpl_path . 'internal/views/criteria/__string.tpl');
  1203. break;
  1204. case SearchFields_Ticket::TICKET_MESSAGE_CONTENT:
  1205. $tpl->display('file:' . $tpl_path . 'internal/views/criteria/__fulltext.tpl');
  1206. break;
  1207. case SearchFields_Ticket::TICKET_FIRST_WROTE_SPAM:
  1208. case SearchFields_Ticket::TICKET_FIRST_WROTE_NONSPAM:
  1209. $tpl->display('file:' . $tpl_path . 'internal/views/criteria/__number.tpl');
  1210. break;
  1211. case SearchFields_Ticket::TICKET_WAITING:
  1212. case SearchFields_Ticket::TICKET_DELETED:
  1213. case SearchFields_Ticket::TICKET_CLOSED:
  1214. $tpl->display('file:' . $tpl_path . 'internal/views/criteria/__bool.tpl');
  1215. break;
  1216. case SearchFields_Ticket::TICKET_CREATED_DATE:
  1217. case SearchFields_Ticket::TICKET_UPDATED_DATE:
  1218. case SearchFields_Ticket::TICKET_DUE_DATE:
  1219. $tpl->display('file:' . $tpl_path . 'internal/views/criteria/__date.tpl');
  1220. break;
  1221. case SearchFields_Ticket::TICKET_SPAM_TRAINING:
  1222. $tpl->display('file:' . $tpl_path . 'tickets/search/criteria/ticket_spam_training.tpl');
  1223. break;
  1224. case SearchFields_Ticket::TICKET_SPAM_SCORE:
  1225. $tpl->display('file:' . $tpl_path . 'tickets/search/criteria/ticket_spam_score.tpl');
  1226. break;
  1227. case SearchFields_Ticket::TICKET_LAST_ACTION_CODE:
  1228. $tpl->display('file:' . $tpl_path . 'tickets/search/criteria/ticket_last_action.tpl');
  1229. break;
  1230. case SearchFields_Ticket::TICKET_NEXT_WORKER_ID:
  1231. case SearchFields_Ticket::TICKET_LAST_WORKER_ID:
  1232. $workers = DAO_Worker::getAll();
  1233. $tpl->assign('workers', $workers);
  1234. $tpl->display('file:' . $tpl_path . 'internal/views/criteria/__worker.tpl');
  1235. break;
  1236. case SearchFields_Ticket::TICKET_TEAM_ID:
  1237. $teams = DAO_Group::getAll();
  1238. $tpl->assign('teams', $teams);
  1239. $team_categories = DAO_Bucket::getTeams();
  1240. $tpl->assign('team_categories', $team_categories);
  1241. $tpl->display('file:' . $tpl_path . 'tickets/search/criteria/ticket_team.tpl');
  1242. break;
  1243. default:
  1244. // Custom Fields
  1245. if('cf_' == substr($field,0,3)) {
  1246. $this->_renderCriteriaCustomField($tpl, substr($field,3));
  1247. } else {
  1248. echo ' ';
  1249. }
  1250. break;
  1251. }
  1252. }
  1253. function renderCriteriaParam($param) {
  1254. $field = $param->field;
  1255. $values = !is_array($param->value) ? array($param->value) : $param->value;
  1256. switch($field) {
  1257. case SearchFields_Ticket::TICKET_LAST_WORKER_ID:
  1258. case SearchFields_Ticket::TICKET_NEXT_WORKER_ID:
  1259. $workers = DAO_Worker::getAll();
  1260. $strings = array();
  1261. foreach($values as $val) {
  1262. if(empty($val))
  1263. $strings[] = "Nobody";
  1264. elseif(!isset($workers[$val]))
  1265. continue;
  1266. else
  1267. $strings[] = $workers[$val]->getName();
  1268. }
  1269. echo implode(", ", $strings);
  1270. break;
  1271. case SearchFields_Ticket::TICKET_TEAM_ID:
  1272. $teams = DAO_Group::getAll();
  1273. $strings = array();
  1274. foreach($values as $val) {
  1275. if(!isset($teams[$val]))
  1276. continue;
  1277. $strings[] = $teams[$val]->name;
  1278. }
  1279. echo implode(", ", $strings);
  1280. break;
  1281. case SearchFields_Ticket::TICKET_CATEGORY_ID:
  1282. $buckets = DAO_Bucket::getAll();
  1283. $strings = array();
  1284. foreach($values as $val) {
  1285. if(0==$val) {
  1286. $strings[] = "Inbox";
  1287. } elseif(!isset($buckets[$val])) {
  1288. continue;
  1289. } else {
  1290. $strings[] = $buckets[$val]->name;
  1291. }
  1292. }
  1293. echo implode(", ", $strings);
  1294. break;
  1295. case SearchFields_Ticket::TICKET_LAST_ACTION_CODE:
  1296. $strings = array();
  1297. foreach($values as $val) {
  1298. switch($val) {
  1299. case 'O':
  1300. $strings[] = "New Ticket";
  1301. break;
  1302. case 'R':
  1303. $strings[] = "Customer Reply";
  1304. break;
  1305. case 'W':
  1306. $strings[] = "Worker Reply";
  1307. break;
  1308. }
  1309. }
  1310. echo implode(", ", $strings);
  1311. break;
  1312. case SearchFields_Ticket::TICKET_SPAM_TRAINING:
  1313. $strings = array();
  1314. foreach($values as $val) {
  1315. switch($val) {
  1316. case 'S':
  1317. $strings[] = "Spam";
  1318. break;
  1319. case 'N':
  1320. $strings[] = "Not Spam";
  1321. break;
  1322. default:
  1323. $strings[] = "Not Trained";
  1324. break;
  1325. }
  1326. }
  1327. echo implode(", ", $strings);
  1328. break;
  1329. default:
  1330. parent::renderCriteriaParam($param);
  1331. break;
  1332. }
  1333. }
  1334. static function getFields() {
  1335. return SearchFields_Ticket::getFields();
  1336. }
  1337. static function getSearchFields() {
  1338. $fields = self::getFields();
  1339. unset($fields[SearchFields_Ticket::TICKET_CATEGORY_ID]);
  1340. unset($fields[SearchFields_Ticket::TICKET_UNLOCK_DATE]);
  1341. return $fields;
  1342. }
  1343. static function getColumns() {
  1344. $fields = self::getFields();
  1345. unset($fields[SearchFields_Ticket::TICKET_MESSAGE_CONTENT]);
  1346. unset($fields[SearchFields_Ticket::REQUESTER_ID]);
  1347. unset($fields[SearchFields_Ticket::REQUESTER_ADDRESS]);
  1348. unset($fields[SearchFields_Ticket::TICKET_UNLOCK_DATE]);
  1349. unset($fields[SearchFields_Ticket::TICKET_INTERESTING_WORDS]);
  1350. return $fields;
  1351. }
  1352. function doSetCriteria($field, $oper, $value) {
  1353. $criteria = null;
  1354. switch($field) {
  1355. case SearchFields_Ticket::TICKET_ID:
  1356. case SearchFields_Ticket::TICKET_MASK:
  1357. case SearchFields_Ticket::TICKET_SUBJECT:
  1358. case SearchFields_Ticket::TICKET_FIRST_WROTE:
  1359. case SearchFields_Ticket::TICKET_LAST_WROTE:
  1360. case SearchFields_Ticket::REQUESTER_ADDRESS:
  1361. case SearchFields_Ticket::TICKET_INTERESTING_WORDS:
  1362. case SearchFields_Ticket::ORG_NAME:
  1363. // force wildcards if none used on a LIKE
  1364. if(($oper == DevblocksSearchCriteria::OPER_LIKE || $oper == DevblocksSearchCriteria::OPER_NOT_LIKE)
  1365. && false === (strpos($value,'*'))) {
  1366. $value = '*'.$value.'*';
  1367. }
  1368. $criteria = new DevblocksSearchCriteria($field, $oper, $value);
  1369. break;
  1370. case SearchFields_Ticket::TICKET_MESSAGE_CONTENT:
  1371. $criteria = new DevblocksSearchCriteria($field, $oper, $value);
  1372. break;
  1373. case SearchFields_Ticket::TICKET_WAITING:
  1374. case SearchFields_Ticket::TICKET_DELETED:
  1375. case SearchFields_Ticket::TICKET_CLOSED:
  1376. @$bool = DevblocksPlatform::importGPC($_REQUEST['bool'],'integer',1);
  1377. $criteria = new DevblocksSearchCriteria($field,$oper,$bool);
  1378. break;
  1379. case SearchFields_Ticket::TICKET_FIRST_WROTE_SPAM:
  1380. case SearchFields_Ticket::TICKET_FIRST_WROTE_NONSPAM:
  1381. $criteria = new DevblocksSearchCriteria($field,$oper,$value);
  1382. break;
  1383. case SearchFields_Ticket::TICKET_CREATED_DATE:
  1384. case SearchFields_Ticket::TICKET_UPDATED_DATE:
  1385. case SearchFields_Ticket::TICKET_DUE_DATE:
  1386. @$from = DevblocksPlatform::importGPC($_REQUEST['from'],'string','');
  1387. @$to = DevblocksPlatform::importGPC($_REQUEST['to'],'string','');
  1388. if(empty($from) || (!is_numeric($from) && @false === strtotime(str_replace('.','-',$from))))
  1389. $from = 0;
  1390. if(empty($to) || (!is_numeric($to) && @false === strtotime(str_replace('.','-',$to))))
  1391. $to = 'now';
  1392. $criteria = new DevblocksSearchCriteria($field,$oper,array($from,$to));
  1393. break;
  1394. case SearchFields_Ticket::TICKET_SPAM_SCORE:
  1395. @$score = DevblocksPlatform::importGPC($_REQUEST['score'],'integer',null);
  1396. if(!is_null($score) && is_numeric($score)) {
  1397. $criteria = new DevblocksSearchCriteria($field,$oper,intval($score)/100);
  1398. }
  1399. break;
  1400. case SearchFields_Ticket::TICKET_SPAM_TRAINING:
  1401. $criteria = new DevblocksSearchCriteria($field,$oper,$value);
  1402. break;
  1403. case SearchFields_Ticket::TICKET_LAST_ACTION_CODE:
  1404. @$last_action_code = DevblocksPlatform::importGPC($_REQUEST['last_action'],'array',array());
  1405. $criteria = new DevblocksSearchCriteria($field,$oper,$last_action_code);
  1406. break;
  1407. case SearchFields_Ticket::TICKET_LAST_WORKER_ID:
  1408. case SearchFields_Ticket::TICKET_NEXT_WORKER_ID:
  1409. @$worker_id = DevblocksPlatform::importGPC($_REQUEST['worker_id'],'array',array());
  1410. $criteria = new DevblocksSearchCriteria($field,$oper,$worker_id);
  1411. break;
  1412. case SearchFields_Ticket::TICKET_TEAM_ID:
  1413. @$team_ids = DevblocksPlatform::importGPC($_REQUEST['team_id'],'array');
  1414. @$bucket_ids = DevblocksPlatform::importGPC($_REQUEST['bucket_id'],'array');
  1415. if(!empty($team_ids))
  1416. $this->params[SearchFields_Ticket::TICKET_TEAM_ID] = new DevblocksSearchCriteria(SearchFields_Ticket::TICKET_TEAM_ID,$oper,$team_ids);
  1417. if(!empty($bucket_ids))
  1418. $this->params[SearchFields_Ticket::TICKET_CATEGORY_ID] = new DevblocksSearchCriteria(SearchFields_Ticket::TICKET_CATEGORY_ID,$oper,$bucket_ids);
  1419. break;
  1420. default:
  1421. // Custom Fields
  1422. if(substr($field,0,3)=='cf_') {
  1423. $criteria = $this->_doSetCriteriaCustomField($field, substr($field,3));
  1424. }
  1425. break;
  1426. }
  1427. if(!empty($criteria)) {
  1428. $this->params[$field] = $criteria;
  1429. $this->renderPage = 0;
  1430. }
  1431. }
  1432. /**
  1433. * @param array
  1434. * @param array
  1435. * @return boolean
  1436. * [TODO] Find a better home for this?
  1437. */
  1438. function doBulkUpdate($filter, $filter_param, $data, $do, $ticket_ids=array()) {
  1439. @set_time_limit(600);
  1440. // Make sure we have checked items if we want a checked list
  1441. if(0 == strcasecmp($filter,"checks") && empty($ticket_ids))
  1442. return;
  1443. $rule = new Model_GroupInboxFilter();
  1444. $rule->actions = $do;
  1445. $params = $this->params;
  1446. if(empty($filter)) {
  1447. $data[] = '*'; // All, just to permit a loop in foreach($data ...)
  1448. }
  1449. switch($filter) {
  1450. default:
  1451. case 'subject':
  1452. case 'sender':
  1453. case 'header':
  1454. if(is_array($data))
  1455. foreach($data as $v) {
  1456. $new_params = array();
  1457. $do_header = null;
  1458. switch($filter) {
  1459. case 'subject':
  1460. $new_params = array(
  1461. new DevblocksSearchCriteria(SearchFields_Ticket::TICKET_SUBJECT,DevblocksSearchCriteria::OPER_LIKE,$v)
  1462. );
  1463. $do_header = 'subject';
  1464. $ticket_ids = array();
  1465. break;
  1466. case 'sender':
  1467. $new_params = array(
  1468. new DevblocksSearchCriteria(SearchFields_Ticket::SENDER_ADDRESS,DevblocksSearchCriteria::OPER_LIKE,$v)
  1469. );
  1470. $do_header = 'from';
  1471. $ticket_ids = array();
  1472. break;
  1473. case 'header':
  1474. $new_params = array(
  1475. // [TODO] It will eventually come up that we need multiple header matches (which need to be pair grouped as OR)
  1476. new DevblocksSearchCriteria(SearchFields_Ticket::TICKET_MESSAGE_HEADER,DevblocksSearchCriteria::OPER_EQ,$filter_param),
  1477. new DevblocksSearchCriteria(SearchFields_Ticket::TICKET_MESSAGE_HEADER_VALUE,DevblocksSearchCriteria::OPER_EQ,$v)
  1478. );
  1479. $ticket_ids = array();
  1480. break;
  1481. }
  1482. $new_params = array_merge($new_params, $params);
  1483. $pg = 0;
  1484. if(empty($ticket_ids)) {
  1485. do {
  1486. list($tickets,$null) = DAO_Ticket::search(
  1487. array(),
  1488. $new_params,
  1489. 100,
  1490. $pg++,
  1491. SearchFields_Ticket::TICKET_ID,
  1492. true,
  1493. false
  1494. );
  1495. $ticket_ids = array_merge($ticket_ids, array_keys($tickets));
  1496. } while(!empty($tickets));
  1497. }
  1498. $batch_total = count($ticket_ids);
  1499. for($x=0;$x<=$batch_total;$x+=200) {
  1500. $batch_ids = array_slice($ticket_ids,$x,200);
  1501. $rule->run($batch_ids);
  1502. unset($batch_ids);
  1503. }
  1504. }
  1505. break;
  1506. }
  1507. unset($ticket_ids);
  1508. }
  1509. static function createSearchView() {
  1510. $active_worker = CerberusApplication::getActiveWorker();
  1511. $memberships = $active_worker->getMemberships();
  1512. $translate = DevblocksPlatform::getTranslationService();
  1513. $view = new C4_TicketView();
  1514. $view->id = CerberusApplication::VIEW_SEARCH;
  1515. $view->name = $translate->_('common.search_results');
  1516. $view->view_columns = array(
  1517. SearchFields_Ticket::TICKET_LAST_ACTION_CODE,
  1518. SearchFields_Ticket::TICKET_UPDATED_DATE,
  1519. SearchFields_Ticket::TICKET_TEAM_ID,
  1520. SearchFields_Ticket::TICKET_CATEGORY_ID,
  1521. SearchFields_Ticket::TICKET_SPAM_SCORE,
  1522. );
  1523. $view->params = array(
  1524. SearchFields_Ticket::TICKET_CLOSED => new DevblocksSearchCriteria(SearchFields_Ticket::TICKET_CLOSED,DevblocksSearchCriteria::OPER_EQ,0),
  1525. SearchFields_Ticket::TICKET_TEAM_ID => new DevblocksSearchCriteria(SearchFields_Ticket::TICKET_TEAM_ID,'in',array_keys($memberships)), // censor
  1526. );
  1527. $view->renderLimit = 100;
  1528. $view->renderPage = 0;
  1529. $view->renderSortBy = null; // SearchFields_Ticket::TICKET_UPDATED_DATE
  1530. $view->renderSortAsc = 0;
  1531. return $view;
  1532. }
  1533. static public function setLastAction($view_id, Model_TicketViewLastAction $last_action=null) {
  1534. $visit = CerberusApplication::getVisit(); /* @var $visit CerberusVisit */
  1535. $view_last_actions = $visit->get(CerberusVisit::KEY_VIEW_LAST_ACTION,array());
  1536. if(!is_null($last_action) && !empty($last_action->ticket_ids)) {
  1537. $view_last_actions[$view_id] = $last_action;
  1538. } else {
  1539. if(isset($view_last_actions[$view_id])) {
  1540. unset($view_last_actions[$view_id]);
  1541. }
  1542. }
  1543. $visit->set(CerberusVisit::KEY_VIEW_LAST_ACTION,$view_last_actions);
  1544. }
  1545. /**
  1546. * @param string $view_id
  1547. * @return Model_TicketViewLastAction
  1548. */
  1549. static public function getLastAction($view_id) {
  1550. $visit = CerberusApplication::getVisit(); /* @var $visit CerberusVisit */
  1551. $view_last_actions = $visit->get(CerberusVisit::KEY_VIEW_LAST_ACTION,array());
  1552. return (isset($view_last_actions[$view_id]) ? $view_last_actions[$view_id] : null);
  1553. }
  1554. static public function clearLastActions() {
  1555. $visit = CerberusApplication::getVisit(); /* @var $visit CerberusVisit */
  1556. $visit->set(CerberusVisit::KEY_VIEW_LAST_ACTION,array());
  1557. }
  1558. };
  1559. class C4_AddressView extends C4_AbstractView {
  1560. const DEFAULT_ID = 'addresses';
  1561. function __construct() {
  1562. $translate = DevblocksPlatform::getTranslationService();
  1563. $this->id = self::DEFAULT_ID;
  1564. $this->name = $translate->_('addy_book.tab.addresses');
  1565. $this->renderLimit = 10;
  1566. $this->renderSortBy = 'a_email';
  1567. $this->renderSortAsc = true;
  1568. $this->view_columns = array(
  1569. SearchFields_Address::FIRST_NAME,
  1570. SearchFields_Address::LAST_NAME,
  1571. SearchFields_Address::ORG_NAME,
  1572. SearchFields_Address::IS_REGISTERED,
  1573. SearchFields_Address::NUM_NONSPAM,
  1574. SearchFields_Address::NUM_SPAM,
  1575. );
  1576. $this->params = array(
  1577. SearchFields_Address::IS_REGISTERED => new DevblocksSearchCriteria(SearchFields_Address::IS_REGISTERED,'=',1),
  1578. );
  1579. }
  1580. function getData() {
  1581. $objects = DAO_Address::search(
  1582. $this->view_columns,
  1583. $this->params,
  1584. $this->renderLimit,
  1585. $this->renderPage,
  1586. $this->renderSortBy,
  1587. $this->renderSortAsc
  1588. );
  1589. return $objects;
  1590. }
  1591. function render() {
  1592. $this->_sanitize();
  1593. $tpl = DevblocksPlatform::getTemplateService();
  1594. $tpl->assign('id', $this->id);
  1595. $tpl->assign('view', $this);
  1596. $address_fields = DAO_CustomField::getBySource(ChCustomFieldSource_Address::ID);
  1597. $tpl->assign('custom_fields', $address_fields);
  1598. $tpl->cache_lifetime = "0";
  1599. $tpl->assign('view_fields', $this->getColumns());
  1600. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/contacts/addresses/address_view.tpl');
  1601. }
  1602. function renderCriteria($field) {
  1603. $tpl = DevblocksPlatform::getTemplateService();
  1604. $tpl->assign('id', $this->id);
  1605. $tpl_path = DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/';
  1606. switch($field) {
  1607. case SearchFields_Address::EMAIL:
  1608. case SearchFields_Address::FIRST_NAME:
  1609. case SearchFields_Address::LAST_NAME:
  1610. case SearchFields_Address::ORG_NAME:
  1611. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/internal/views/criteria/__string.tpl');
  1612. break;
  1613. case SearchFields_Address::NUM_SPAM:
  1614. case SearchFields_Address::NUM_NONSPAM:
  1615. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/internal/views/criteria/__number.tpl');
  1616. break;
  1617. case SearchFields_Address::IS_BANNED:
  1618. case SearchFields_Address::IS_REGISTERED:
  1619. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/internal/views/criteria/__bool.tpl');
  1620. break;
  1621. default:
  1622. // Custom Fields
  1623. if('cf_' == substr($field,0,3)) {
  1624. $this->_renderCriteriaCustomField($tpl, substr($field,3));
  1625. } else {
  1626. echo ' ';
  1627. }
  1628. break;
  1629. }
  1630. }
  1631. function renderCriteriaParam($param) {
  1632. $field = $param->field;
  1633. $values = !is_array($param->value) ? array($param->value) : $param->value;
  1634. switch($field) {
  1635. default:
  1636. parent::renderCriteriaParam($param);
  1637. break;
  1638. }
  1639. }
  1640. static function getFields() {
  1641. return SearchFields_Address::getFields();
  1642. }
  1643. static function getSearchFields() {
  1644. $fields = self::getFields();
  1645. unset($fields[SearchFields_Address::ID]);
  1646. unset($fields[SearchFields_Address::CONTACT_ORG_ID]);
  1647. unset($fields[SearchFields_Address::PASS]);
  1648. return $fields;
  1649. }
  1650. static function getColumns() {
  1651. $fields = self::getFields();
  1652. unset($fields[SearchFields_Address::PASS]);
  1653. return $fields;
  1654. }
  1655. function doResetCriteria() {
  1656. parent::doResetCriteria();
  1657. $this->params = array(
  1658. SearchFields_Address::IS_REGISTERED => new DevblocksSearchCriteria(SearchFields_Address::IS_REGISTERED,'=',1),
  1659. );
  1660. }
  1661. function doSetCriteria($field, $oper, $value) {
  1662. $criteria = null;
  1663. switch($field) {
  1664. case SearchFields_Address::EMAIL:
  1665. case SearchFields_Address::FIRST_NAME:
  1666. case SearchFields_Address::LAST_NAME:
  1667. case SearchFields_Address::ORG_NAME:
  1668. // force wildcards if none used on a LIKE
  1669. if(($oper == DevblocksSearchCriteria::OPER_LIKE || $oper == DevblocksSearchCriteria::OPER_NOT_LIKE)
  1670. && false === (strpos($value,'*'))) {
  1671. $value = '*'.$value.'*';
  1672. }
  1673. $criteria = new DevblocksSearchCriteria($field, $oper, $value);
  1674. break;
  1675. case SearchFields_Address::NUM_SPAM:
  1676. case SearchFields_Address::NUM_NONSPAM:
  1677. $criteria = new DevblocksSearchCriteria($field,$oper,$value);
  1678. break;
  1679. case SearchFields_Address::IS_BANNED:
  1680. @$bool = DevblocksPlatform::importGPC($_REQUEST['bool'],'integer',1);
  1681. $criteria = new DevblocksSearchCriteria($field,$oper,$bool);
  1682. break;
  1683. case SearchFields_Address::IS_REGISTERED:
  1684. @$bool = DevblocksPlatform::importGPC($_REQUEST['bool'],'integer',1);
  1685. $criteria = new DevblocksSearchCriteria($field,$oper,$bool);
  1686. break;
  1687. default:
  1688. // Custom Fields
  1689. if(substr($field,0,3)=='cf_') {
  1690. $criteria = $this->_doSetCriteriaCustomField($field, substr($field,3));
  1691. }
  1692. break;
  1693. }
  1694. if(!empty($criteria)) {
  1695. $this->params[$field] = $criteria;
  1696. $this->renderPage = 0;
  1697. }
  1698. }
  1699. function doBulkUpdate($filter, $do, $ids=array()) {
  1700. @set_time_limit(600); // [TODO] Temp!
  1701. $change_fields = array();
  1702. $custom_fields = array();
  1703. // Make sure we have actions
  1704. if(empty($do))
  1705. return;
  1706. // Make sure we have checked items if we want a checked list
  1707. if(0 == strcasecmp($filter,"checks") && empty($ids))
  1708. return;
  1709. if(is_array($do))
  1710. foreach($do as $k => $v) {
  1711. switch($k) {
  1712. case 'org_id':
  1713. $change_fields[DAO_Address::CONTACT_ORG_ID] = intval($v);
  1714. break;
  1715. case 'banned':
  1716. $change_fields[DAO_Address::IS_BANNED] = intval($v);
  1717. break;
  1718. default:
  1719. // Custom fields
  1720. if(substr($k,0,3)=="cf_") {
  1721. $custom_fields[substr($k,3)] = $v;
  1722. }
  1723. }
  1724. }
  1725. $pg = 0;
  1726. if(empty($ids))
  1727. do {
  1728. list($objects,$null) = DAO_Address::search(
  1729. array(),
  1730. $this->params,
  1731. 100,
  1732. $pg++,
  1733. SearchFields_Address::ID,
  1734. true,
  1735. false
  1736. );
  1737. $ids = array_merge($ids, array_keys($objects));
  1738. } while(!empty($objects));
  1739. $batch_total = count($ids);
  1740. for($x=0;$x<=$batch_total;$x+=100) {
  1741. $batch_ids = array_slice($ids,$x,100);
  1742. DAO_Address::update($batch_ids, $change_fields);
  1743. // Custom Fields
  1744. self::_doBulkSetCustomFields(ChCustomFieldSource_Address::ID, $custom_fields, $batch_ids);
  1745. unset($batch_ids);
  1746. }
  1747. unset($ids);
  1748. }
  1749. };
  1750. class C4_AttachmentView extends C4_AbstractView {
  1751. const DEFAULT_ID = 'attachments';
  1752. function __construct() {
  1753. $this->id = self::DEFAULT_ID;
  1754. $this->name = 'Attachments';
  1755. $this->renderLimit = 100;
  1756. $this->renderSortBy = SearchFields_Attachment::FILE_SIZE;
  1757. $this->renderSortAsc = false;
  1758. $this->view_columns = array(
  1759. SearchFields_Attachment::MIME_TYPE,
  1760. SearchFields_Attachment::FILE_SIZE,
  1761. SearchFields_Attachment::MESSAGE_CREATED_DATE,
  1762. SearchFields_Attachment::ADDRESS_EMAIL,
  1763. SearchFields_Attachment::TICKET_MASK,
  1764. );
  1765. // $this->params = array(
  1766. // SearchFields_Address::NUM_NONSPAM => new DevblocksSearchCriteria(SearchFields_Address::NUM_NONSPAM,'>',0),
  1767. // );
  1768. }
  1769. function getData() {
  1770. $objects = DAO_Attachment::search(
  1771. $this->params,
  1772. $this->renderLimit,
  1773. $this->renderPage,
  1774. $this->renderSortBy,
  1775. $this->renderSortAsc
  1776. );
  1777. return $objects;
  1778. }
  1779. function render() {
  1780. $this->_sanitize();
  1781. $tpl = DevblocksPlatform::getTemplateService();
  1782. $tpl->assign('id', $this->id);
  1783. $tpl->assign('view', $this);
  1784. $tpl->cache_lifetime = "0";
  1785. $tpl->assign('view_fields', $this->getColumns());
  1786. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/configuration/tabs/attachments/view.tpl');
  1787. }
  1788. function renderCriteria($field) {
  1789. $tpl = DevblocksPlatform::getTemplateService();
  1790. $tpl->assign('id', $this->id);
  1791. switch($field) {
  1792. case SearchFields_Attachment::DISPLAY_NAME:
  1793. case SearchFields_Attachment::FILEPATH:
  1794. case SearchFields_Attachment::MIME_TYPE:
  1795. case SearchFields_Attachment::TICKET_MASK:
  1796. case SearchFields_Attachment::TICKET_SUBJECT:
  1797. case SearchFields_Attachment::ADDRESS_EMAIL:
  1798. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/internal/views/criteria/__string.tpl');
  1799. break;
  1800. // case SearchFields_Attachment::ID:
  1801. // case SearchFields_Attachment::MESSAGE_ID:
  1802. case SearchFields_Attachment::TICKET_ID:
  1803. case SearchFields_Attachment::FILE_SIZE:
  1804. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/internal/views/criteria/__number.tpl');
  1805. break;
  1806. case SearchFields_Attachment::MESSAGE_IS_OUTGOING:
  1807. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/internal/views/criteria/__bool.tpl');
  1808. break;
  1809. case SearchFields_Attachment::MESSAGE_CREATED_DATE:
  1810. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/internal/views/criteria/__date.tpl');
  1811. break;
  1812. default:
  1813. echo '';
  1814. break;
  1815. }
  1816. }
  1817. function renderCriteriaParam($param) {
  1818. $field = $param->field;
  1819. $values = !is_array($param->value) ? array($param->value) : $param->value;
  1820. switch($field) {
  1821. default:
  1822. parent::renderCriteriaParam($param);
  1823. break;
  1824. }
  1825. }
  1826. static function getFields() {
  1827. return SearchFields_Attachment::getFields();
  1828. }
  1829. static function getSearchFields() {
  1830. $fields = self::getFields();
  1831. unset($fields[SearchFields_Attachment::ID]);
  1832. unset($fields[SearchFields_Attachment::MESSAGE_ID]);
  1833. return $fields;
  1834. }
  1835. static function getColumns() {
  1836. $fields = self::getFields();
  1837. return $fields;
  1838. }
  1839. function doResetCriteria() {
  1840. parent::doResetCriteria();
  1841. // $this->params = array(
  1842. // SearchFields_Address::NUM_NONSPAM => new DevblocksSearchCriteria(SearchFields_Address::NUM_NONSPAM,'>',0),
  1843. // );
  1844. }
  1845. function doSetCriteria($field, $oper, $value) {
  1846. $criteria = null;
  1847. switch($field) {
  1848. case SearchFields_Attachment::DISPLAY_NAME:
  1849. case SearchFields_Attachment::MIME_TYPE:
  1850. case SearchFields_Attachment::FILEPATH:
  1851. case SearchFields_Attachment::TICKET_MASK:
  1852. case SearchFields_Attachment::TICKET_SUBJECT:
  1853. case SearchFields_Attachment::ADDRESS_EMAIL:
  1854. // force wildcards if none used on a LIKE
  1855. if(($oper == DevblocksSearchCriteria::OPER_LIKE || $oper == DevblocksSearchCriteria::OPER_NOT_LIKE)
  1856. && false === (strpos($value,'*'))) {
  1857. $value = '*'.$value.'*';
  1858. }
  1859. $criteria = new DevblocksSearchCriteria($field, $oper, $value);
  1860. break;
  1861. case SearchFields_Attachment::ID:
  1862. case SearchFields_Attachment::MESSAGE_ID:
  1863. case SearchFields_Attachment::TICKET_ID:
  1864. case SearchFields_Attachment::FILE_SIZE:
  1865. $criteria = new DevblocksSearchCriteria($field,$oper,$value);
  1866. break;
  1867. case SearchFields_Attachment::MESSAGE_CREATED_DATE:
  1868. @$from = DevblocksPlatform::importGPC($_REQUEST['from'],'string','');
  1869. @$to = DevblocksPlatform::importGPC($_REQUEST['to'],'string','');
  1870. if(empty($from)) $from = 0;
  1871. if(empty($to)) $to = 'today';
  1872. $criteria = new DevblocksSearchCriteria($field,$oper,array($from,$to));
  1873. break;
  1874. case SearchFields_Attachment::MESSAGE_IS_OUTGOING:
  1875. @$bool = DevblocksPlatform::importGPC($_REQUEST['bool'],'integer',1);
  1876. $criteria = new DevblocksSearchCriteria($field,$oper,$bool);
  1877. break;
  1878. }
  1879. if(!empty($criteria)) {
  1880. $this->params[$field] = $criteria;
  1881. $this->renderPage = 0;
  1882. }
  1883. }
  1884. function doBulkUpdate($filter, $do, $ids=array()) {
  1885. @set_time_limit(0);
  1886. $change_fields = array();
  1887. $deleted = false;
  1888. // Make sure we have actions
  1889. if(empty($do))
  1890. return;
  1891. // Make sure we have checked items if we want a checked list
  1892. if(0 == strcasecmp($filter,"checks") && empty($ids))
  1893. return;
  1894. if(is_array($do))
  1895. foreach($do as $k => $v) {
  1896. switch($k) {
  1897. case 'deleted':
  1898. $deleted = true;
  1899. break;
  1900. default:
  1901. break;
  1902. }
  1903. }
  1904. $pg = 0;
  1905. if(empty($ids))
  1906. do {
  1907. list($objects,$null) = DAO_Attachment::search(
  1908. $this->params,
  1909. 100,
  1910. $pg++,
  1911. SearchFields_Attachment::ID,
  1912. true,
  1913. false
  1914. );
  1915. $ids = array_merge($ids, array_keys($objects));
  1916. } while(!empty($objects));
  1917. $batch_total = count($ids);
  1918. for($x=0;$x<=$batch_total;$x+=100) {
  1919. $batch_ids = array_slice($ids,$x,100);
  1920. if(!$deleted) {
  1921. DAO_Attachment::update($batch_ids, $change_fields);
  1922. } else {
  1923. DAO_Attachment::delete($batch_ids);
  1924. }
  1925. unset($batch_ids);
  1926. }
  1927. unset($ids);
  1928. }
  1929. };
  1930. class C4_ContactOrgView extends C4_AbstractView {
  1931. const DEFAULT_ID = 'contact_orgs';
  1932. function __construct() {
  1933. $translate = DevblocksPlatform::getTranslationService();
  1934. $this->id = self::DEFAULT_ID;
  1935. $this->name = $translate->_('addy_book.tab.organizations');
  1936. $this->renderSortBy = 'c_name';
  1937. $this->renderSortAsc = true;
  1938. $this->view_columns = array(
  1939. SearchFields_ContactOrg::COUNTRY,
  1940. SearchFields_ContactOrg::CREATED,
  1941. SearchFields_ContactOrg::PHONE,
  1942. SearchFields_ContactOrg::WEBSITE,
  1943. );
  1944. }
  1945. function getData() {
  1946. $objects = DAO_ContactOrg::search(
  1947. $this->view_columns,
  1948. $this->params,
  1949. $this->renderLimit,
  1950. $this->renderPage,
  1951. $this->renderSortBy,
  1952. $this->renderSortAsc
  1953. );
  1954. return $objects;
  1955. }
  1956. function render() {
  1957. $this->_sanitize();
  1958. $tpl = DevblocksPlatform::getTemplateService();
  1959. $tpl->assign('core_tpl', DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/');
  1960. $tpl->assign('id', $this->id);
  1961. $tpl->assign('view', $this);
  1962. $org_fields = DAO_CustomField::getBySource(ChCustomFieldSource_Org::ID);
  1963. $tpl->assign('custom_fields', $org_fields);
  1964. $tpl->cache_lifetime = "0";
  1965. $tpl->assign('view_fields', $this->getColumns());
  1966. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/contacts/orgs/contact_view.tpl');
  1967. }
  1968. function renderCriteria($field) {
  1969. $tpl = DevblocksPlatform::getTemplateService();
  1970. $tpl->assign('id', $this->id);
  1971. switch($field) {
  1972. case SearchFields_ContactOrg::NAME:
  1973. case SearchFields_ContactOrg::STREET:
  1974. case SearchFields_ContactOrg::CITY:
  1975. case SearchFields_ContactOrg::PROVINCE:
  1976. case SearchFields_ContactOrg::POSTAL:
  1977. case SearchFields_ContactOrg::COUNTRY:
  1978. case SearchFields_ContactOrg::PHONE:
  1979. case SearchFields_ContactOrg::WEBSITE:
  1980. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/internal/views/criteria/__string.tpl');
  1981. break;
  1982. case SearchFields_ContactOrg::CREATED:
  1983. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/internal/views/criteria/__date.tpl');
  1984. break;
  1985. default:
  1986. // Custom Fields
  1987. if('cf_' == substr($field,0,3)) {
  1988. $this->_renderCriteriaCustomField($tpl, substr($field,3));
  1989. } else {
  1990. echo ' ';
  1991. }
  1992. break;
  1993. }
  1994. }
  1995. function renderCriteriaParam($param) {
  1996. $field = $param->field;
  1997. $values = !is_array($param->value) ? array($param->value) : $param->value;
  1998. switch($field) {
  1999. default:
  2000. parent::renderCriteriaParam($param);
  2001. break;
  2002. }
  2003. }
  2004. static function getFields() {
  2005. return SearchFields_ContactOrg::getFields();
  2006. }
  2007. static function getSearchFields() {
  2008. $fields = self::getFields();
  2009. unset($fields[SearchFields_ContactOrg::ID]);
  2010. return $fields;
  2011. }
  2012. static function getColumns() {
  2013. return self::getFields();
  2014. }
  2015. function doSetCriteria($field, $oper, $value) {
  2016. $criteria = null;
  2017. switch($field) {
  2018. case SearchFields_ContactOrg::NAME:
  2019. case SearchFields_ContactOrg::STREET:
  2020. case SearchFields_ContactOrg::CITY:
  2021. case SearchFields_ContactOrg::PROVINCE:
  2022. case SearchFields_ContactOrg::POSTAL:
  2023. case SearchFields_ContactOrg::COUNTRY:
  2024. case SearchFields_ContactOrg::PHONE:
  2025. case SearchFields_ContactOrg::WEBSITE:
  2026. // force wildcards if none used on a LIKE
  2027. if(($oper == DevblocksSearchCriteria::OPER_LIKE || $oper == DevblocksSearchCriteria::OPER_NOT_LIKE)
  2028. && false === (strpos($value,'*'))) {
  2029. $value = '*'.$value.'*';
  2030. }
  2031. $criteria = new DevblocksSearchCriteria($field, $oper, $value);
  2032. break;
  2033. case SearchFields_ContactOrg::CREATED:
  2034. @$from = DevblocksPlatform::importGPC($_REQUEST['from'],'string','');
  2035. @$to = DevblocksPlatform::importGPC($_REQUEST['to'],'string','');
  2036. if(empty($from)) $from = 0;
  2037. if(empty($to)) $to = 'today';
  2038. $criteria = new DevblocksSearchCriteria($field,$oper,array($from,$to));
  2039. break;
  2040. default:
  2041. // Custom Fields
  2042. if(substr($field,0,3)=='cf_') {
  2043. $criteria = $this->_doSetCriteriaCustomField($field, substr($field,3));
  2044. }
  2045. break;
  2046. }
  2047. if(!empty($criteria)) {
  2048. $this->params[$field] = $criteria;
  2049. $this->renderPage = 0;
  2050. }
  2051. }
  2052. function doBulkUpdate($filter, $do, $ids=array()) {
  2053. @set_time_limit(0);
  2054. $change_fields = array();
  2055. $custom_fields = array();
  2056. // Make sure we have actions
  2057. if(empty($do))
  2058. return;
  2059. // Make sure we have checked items if we want a checked list
  2060. if(0 == strcasecmp($filter,"checks") && empty($ids))
  2061. return;
  2062. if(is_array($do))
  2063. foreach($do as $k => $v) {
  2064. switch($k) {
  2065. case 'country':
  2066. $change_fields[DAO_ContactOrg::COUNTRY] = $v;
  2067. break;
  2068. default:
  2069. // Custom fields
  2070. if(substr($k,0,3)=="cf_") {
  2071. $custom_fields[substr($k,3)] = $v;
  2072. }
  2073. break;
  2074. }
  2075. }
  2076. $pg = 0;
  2077. if(empty($ids))
  2078. do {
  2079. list($objects,$null) = DAO_ContactOrg::search(
  2080. array(),
  2081. $this->params,
  2082. 100,
  2083. $pg++,
  2084. SearchFields_ContactOrg::ID,
  2085. true,
  2086. false
  2087. );
  2088. $ids = array_merge($ids, array_keys($objects));
  2089. } while(!empty($objects));
  2090. $batch_total = count($ids);
  2091. for($x=0;$x<=$batch_total;$x+=100) {
  2092. $batch_ids = array_slice($ids,$x,100);
  2093. DAO_ContactOrg::update($batch_ids, $change_fields);
  2094. // Custom Fields
  2095. self::_doBulkSetCustomFields(ChCustomFieldSource_Org::ID, $custom_fields, $batch_ids);
  2096. unset($batch_ids);
  2097. }
  2098. unset($ids);
  2099. }
  2100. };
  2101. class C4_TaskView extends C4_AbstractView {
  2102. const DEFAULT_ID = 'tasks';
  2103. const DEFAULT_TITLE = 'All Open Tasks';
  2104. function __construct() {
  2105. $this->id = self::DEFAULT_ID;
  2106. $this->name = self::DEFAULT_TITLE;
  2107. $this->renderLimit = 25;
  2108. $this->renderSortBy = SearchFields_Task::DUE_DATE;
  2109. $this->renderSortAsc = true;
  2110. $this->view_columns = array(
  2111. SearchFields_Task::SOURCE_EXTENSION,
  2112. SearchFields_Task::UPDATED_DATE,
  2113. SearchFields_Task::DUE_DATE,
  2114. SearchFields_Task::WORKER_ID,
  2115. );
  2116. $this->params = array(
  2117. SearchFields_Task::IS_COMPLETED => new DevblocksSearchCriteria(SearchFields_Task::IS_COMPLETED,'=',0),
  2118. );
  2119. }
  2120. function getData() {
  2121. $objects = DAO_Task::search(
  2122. $this->view_columns,
  2123. $this->params,
  2124. $this->renderLimit,
  2125. $this->renderPage,
  2126. $this->renderSortBy,
  2127. $this->renderSortAsc
  2128. );
  2129. return $objects;
  2130. }
  2131. function render() {
  2132. $this->_sanitize();
  2133. $tpl = DevblocksPlatform::getTemplateService();
  2134. $tpl->assign('id', $this->id);
  2135. $tpl->assign('view', $this);
  2136. $workers = DAO_Worker::getAll();
  2137. $tpl->assign('workers', $workers);
  2138. $tpl->assign('timestamp_now', time());
  2139. // Pull the results so we can do some row introspection
  2140. $results = $this->getData();
  2141. $tpl->assign('results', $results);
  2142. // $source_renderers = DevblocksPlatform::getExtensions('cerberusweb.task.source', true);
  2143. // Make a list of unique source_extension and load their renderers
  2144. $source_extensions = array();
  2145. if(is_array($results) && isset($results[0]))
  2146. foreach($results[0] as $rows) {
  2147. $source_extension = $rows[SearchFields_Task::SOURCE_EXTENSION];
  2148. if(!isset($source_extensions[$source_extension])
  2149. && !empty($source_extension)
  2150. && null != ($mft = DevblocksPlatform::getExtension($source_extension))) {
  2151. $source_extensions[$source_extension] = $mft->createInstance();
  2152. }
  2153. }
  2154. $tpl->assign('source_renderers', $source_extensions);
  2155. // Custom fields
  2156. $custom_fields = DAO_CustomField::getBySource(ChCustomFieldSource_Task::ID);
  2157. $tpl->assign('custom_fields', $custom_fields);
  2158. $tpl->cache_lifetime = "0";
  2159. $tpl->assign('view_fields', $this->getColumns());
  2160. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/tasks/view.tpl');
  2161. }
  2162. function renderCriteria($field) {
  2163. $tpl = DevblocksPlatform::getTemplateService();
  2164. $tpl_path = DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/';
  2165. $tpl->assign('id', $this->id);
  2166. switch($field) {
  2167. case SearchFields_Task::TITLE:
  2168. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/internal/views/criteria/__string.tpl');
  2169. break;
  2170. case SearchFields_Task::SOURCE_EXTENSION:
  2171. $source_renderers = DevblocksPlatform::getExtensions('cerberusweb.task.source', true);
  2172. $tpl->assign('sources', $source_renderers);
  2173. $tpl->display('file:' . $tpl_path . 'tasks/criteria/source.tpl');
  2174. break;
  2175. case SearchFields_Task::IS_COMPLETED:
  2176. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/internal/views/criteria/__bool.tpl');
  2177. break;
  2178. case SearchFields_Task::UPDATED_DATE:
  2179. case SearchFields_Task::DUE_DATE:
  2180. case SearchFields_Task::COMPLETED_DATE:
  2181. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/internal/views/criteria/__date.tpl');
  2182. break;
  2183. case SearchFields_Task::WORKER_ID:
  2184. $workers = DAO_Worker::getAll();
  2185. $tpl->assign('workers', $workers);
  2186. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/internal/views/criteria/__worker.tpl');
  2187. break;
  2188. default:
  2189. // Custom Fields
  2190. if('cf_' == substr($field,0,3)) {
  2191. $this->_renderCriteriaCustomField($tpl, substr($field,3));
  2192. } else {
  2193. echo ' ';
  2194. }
  2195. break;
  2196. }
  2197. }
  2198. function renderCriteriaParam($param) {
  2199. $field = $param->field;
  2200. $translate = DevblocksPlatform::getTranslationService();
  2201. $values = !is_array($param->value) ? array($param->value) : $param->value;
  2202. switch($field) {
  2203. case SearchFields_Task::WORKER_ID:
  2204. $workers = DAO_Worker::getAll();
  2205. $strings = array();
  2206. foreach($values as $val) {
  2207. if(empty($val))
  2208. $strings[] = "Nobody";
  2209. elseif(!isset($workers[$val]))
  2210. continue;
  2211. else
  2212. $strings[] = $workers[$val]->getName();
  2213. }
  2214. echo implode(", ", $strings);
  2215. break;
  2216. case SearchFields_Task::SOURCE_EXTENSION:
  2217. $sources = $ext = DevblocksPlatform::getExtensions('cerberusweb.task.source', true);
  2218. $strings = array();
  2219. foreach($values as $val) {
  2220. if(!isset($sources[$val]))
  2221. continue;
  2222. else
  2223. $strings[] = $sources[$val]->getSourceName();
  2224. }
  2225. echo implode(", ", $strings);
  2226. break;
  2227. default:
  2228. parent::renderCriteriaParam($param);
  2229. break;
  2230. }
  2231. }
  2232. static function getFields() {
  2233. return SearchFields_Task::getFields();
  2234. }
  2235. static function getSearchFields() {
  2236. $fields = self::getFields();
  2237. unset($fields[SearchFields_Task::ID]);
  2238. unset($fields[SearchFields_Task::SOURCE_ID]);
  2239. return $fields;
  2240. }
  2241. static function getColumns() {
  2242. $fields = self::getFields();
  2243. unset($fields[SearchFields_Task::ID]);
  2244. unset($fields[SearchFields_Task::SOURCE_ID]);
  2245. return $fields;
  2246. }
  2247. function doResetCriteria() {
  2248. parent::doResetCriteria();
  2249. $this->params = array(
  2250. SearchFields_Task::IS_COMPLETED => new DevblocksSearchCriteria(SearchFields_Task::IS_COMPLETED,'=',0)
  2251. );
  2252. }
  2253. function doSetCriteria($field, $oper, $value) {
  2254. $criteria = null;
  2255. switch($field) {
  2256. case SearchFields_Task::TITLE:
  2257. // force wildcards if none used on a LIKE
  2258. if(($oper == DevblocksSearchCriteria::OPER_LIKE || $oper == DevblocksSearchCriteria::OPER_NOT_LIKE)
  2259. && false === (strpos($value,'*'))) {
  2260. $value = '*'.$value.'*';
  2261. }
  2262. $criteria = new DevblocksSearchCriteria($field, $oper, $value);
  2263. break;
  2264. case SearchFields_Task::SOURCE_EXTENSION:
  2265. @$sources = DevblocksPlatform::importGPC($_REQUEST['sources'],'array',array());
  2266. $criteria = new DevblocksSearchCriteria($field,$oper,$sources);
  2267. break;
  2268. case SearchFields_Task::UPDATED_DATE:
  2269. case SearchFields_Task::COMPLETED_DATE:
  2270. case SearchFields_Task::DUE_DATE:
  2271. @$from = DevblocksPlatform::importGPC($_REQUEST['from'],'string','');
  2272. @$to = DevblocksPlatform::importGPC($_REQUEST['to'],'string','');
  2273. if(empty($from)) $from = 0;
  2274. if(empty($to)) $to = 'today';
  2275. $criteria = new DevblocksSearchCriteria($field,$oper,array($from,$to));
  2276. break;
  2277. case SearchFields_Task::IS_COMPLETED:
  2278. @$bool = DevblocksPlatform::importGPC($_REQUEST['bool'],'integer',1);
  2279. $criteria = new DevblocksSearchCriteria($field,$oper,$bool);
  2280. break;
  2281. case SearchFields_Task::WORKER_ID:
  2282. @$worker_id = DevblocksPlatform::importGPC($_REQUEST['worker_id'],'array',array());
  2283. $criteria = new DevblocksSearchCriteria($field,$oper,$worker_id);
  2284. break;
  2285. default:
  2286. // Custom Fields
  2287. if(substr($field,0,3)=='cf_') {
  2288. $criteria = $this->_doSetCriteriaCustomField($field, substr($field,3));
  2289. }
  2290. break;
  2291. }
  2292. if(!empty($criteria)) {
  2293. $this->params[$field] = $criteria;
  2294. $this->renderPage = 0;
  2295. }
  2296. }
  2297. function doBulkUpdate($filter, $do, $ids=array()) {
  2298. @set_time_limit(600); // [TODO] Temp!
  2299. $change_fields = array();
  2300. $custom_fields = array();
  2301. // Make sure we have actions
  2302. if(empty($do))
  2303. return;
  2304. // Make sure we have checked items if we want a checked list
  2305. if(0 == strcasecmp($filter,"checks") && empty($ids))
  2306. return;
  2307. if(is_array($do))
  2308. foreach($do as $k => $v) {
  2309. switch($k) {
  2310. case 'due':
  2311. @$date = strtotime($v);
  2312. $change_fields[DAO_Task::DUE_DATE] = intval($date);
  2313. break;
  2314. case 'status':
  2315. if(1==intval($v)) { // completed
  2316. $change_fields[DAO_Task::IS_COMPLETED] = 1;
  2317. $change_fields[DAO_Task::COMPLETED_DATE] = time();
  2318. } else { // active
  2319. $change_fields[DAO_Task::IS_COMPLETED] = 0;
  2320. $change_fields[DAO_Task::COMPLETED_DATE] = 0;
  2321. }
  2322. break;
  2323. case 'worker_id':
  2324. $change_fields[DAO_Task::WORKER_ID] = intval($v);
  2325. break;
  2326. default:
  2327. // Custom fields
  2328. if(substr($k,0,3)=="cf_") {
  2329. $custom_fields[substr($k,3)] = $v;
  2330. }
  2331. }
  2332. }
  2333. $pg = 0;
  2334. if(empty($ids))
  2335. do {
  2336. list($objects,$null) = DAO_Task::search(
  2337. array(),
  2338. $this->params,
  2339. 100,
  2340. $pg++,
  2341. SearchFields_Task::ID,
  2342. true,
  2343. false
  2344. );
  2345. $ids = array_merge($ids, array_keys($objects));
  2346. } while(!empty($objects));
  2347. $batch_total = count($ids);
  2348. for($x=0;$x<=$batch_total;$x+=100) {
  2349. $batch_ids = array_slice($ids,$x,100);
  2350. DAO_Task::update($batch_ids, $change_fields);
  2351. // Custom Fields
  2352. self::_doBulkSetCustomFields(ChCustomFieldSource_Task::ID, $custom_fields, $batch_ids);
  2353. unset($batch_ids);
  2354. }
  2355. unset($ids);
  2356. }
  2357. };
  2358. class C4_WorkerView extends C4_AbstractView {
  2359. const DEFAULT_ID = 'workers';
  2360. function __construct() {
  2361. $this->id = self::DEFAULT_ID;
  2362. $this->name = 'Workers';
  2363. $this->renderLimit = 25;
  2364. $this->renderSortBy = SearchFields_Worker::FIRST_NAME;
  2365. $this->renderSortAsc = true;
  2366. $this->view_columns = array(
  2367. SearchFields_Worker::FIRST_NAME,
  2368. SearchFields_Worker::LAST_NAME,
  2369. SearchFields_Worker::TITLE,
  2370. SearchFields_Worker::EMAIL,
  2371. SearchFields_Worker::LAST_ACTIVITY_DATE,
  2372. SearchFields_Worker::IS_SUPERUSER,
  2373. );
  2374. $this->doResetCriteria();
  2375. }
  2376. function getData() {
  2377. return DAO_Worker::search(
  2378. $this->view_columns,
  2379. $this->params,
  2380. $this->renderLimit,
  2381. $this->renderPage,
  2382. $this->renderSortBy,
  2383. $this->renderSortAsc
  2384. );
  2385. }
  2386. function render() {
  2387. $this->_sanitize();
  2388. $tpl = DevblocksPlatform::getTemplateService();
  2389. $tpl->assign('id', $this->id);
  2390. $tpl->assign('view', $this);
  2391. $custom_fields = DAO_CustomField::getBySource(ChCustomFieldSource_Worker::ID);
  2392. $tpl->assign('custom_fields', $custom_fields);
  2393. $tpl->cache_lifetime = "0";
  2394. $tpl->assign('view_fields', $this->getColumns());
  2395. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/configuration/tabs/workers/view.tpl');
  2396. }
  2397. function renderCriteria($field) {
  2398. $tpl = DevblocksPlatform::getTemplateService();
  2399. $tpl->assign('id', $this->id);
  2400. switch($field) {
  2401. case SearchFields_Worker::EMAIL:
  2402. case SearchFields_Worker::FIRST_NAME:
  2403. case SearchFields_Worker::LAST_NAME:
  2404. case SearchFields_Worker::TITLE:
  2405. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/internal/views/criteria/__string.tpl');
  2406. break;
  2407. case SearchFields_Worker::IS_DISABLED:
  2408. case SearchFields_Worker::IS_SUPERUSER:
  2409. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/internal/views/criteria/__bool.tpl');
  2410. break;
  2411. case SearchFields_Worker::LAST_ACTIVITY_DATE:
  2412. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/internal/views/criteria/__date.tpl');
  2413. break;
  2414. default:
  2415. // Custom Fields
  2416. if('cf_' == substr($field,0,3)) {
  2417. $this->_renderCriteriaCustomField($tpl, substr($field,3));
  2418. } else {
  2419. echo ' ';
  2420. }
  2421. break;
  2422. }
  2423. }
  2424. function renderCriteriaParam($param) {
  2425. $field = $param->field;
  2426. $values = !is_array($param->value) ? array($param->value) : $param->value;
  2427. switch($field) {
  2428. // case SearchFields_WorkerEvent::WORKER_ID:
  2429. // $workers = DAO_Worker::getAll();
  2430. // $strings = array();
  2431. //
  2432. // foreach($values as $val) {
  2433. // if(empty($val))
  2434. // $strings[] = "Nobody";
  2435. // elseif(!isset($workers[$val]))
  2436. // continue;
  2437. // else
  2438. // $strings[] = $workers[$val]->getName();
  2439. // }
  2440. // echo implode(", ", $strings);
  2441. // break;
  2442. default:
  2443. parent::renderCriteriaParam($param);
  2444. break;
  2445. }
  2446. }
  2447. static function getFields() {
  2448. return SearchFields_Worker::getFields();
  2449. }
  2450. static function getSearchFields() {
  2451. $fields = self::getFields();
  2452. unset($fields[SearchFields_Worker::ID]);
  2453. unset($fields[SearchFields_Worker::LAST_ACTIVITY]);
  2454. return $fields;
  2455. }
  2456. static function getColumns() {
  2457. $fields = self::getFields();
  2458. unset($fields[SearchFields_Worker::LAST_ACTIVITY]);
  2459. return $fields;
  2460. }
  2461. function doResetCriteria() {
  2462. parent::doResetCriteria();
  2463. // $this->params = array(
  2464. // SearchFields_WorkerEvent::NUM_NONSPAM => new DevblocksSearchCriteria(SearchFields_WorkerEvent::NUM_NONSPAM,'>',0),
  2465. // );
  2466. }
  2467. function doSetCriteria($field, $oper, $value) {
  2468. $criteria = null;
  2469. switch($field) {
  2470. case SearchFields_Worker::EMAIL:
  2471. case SearchFields_Worker::FIRST_NAME:
  2472. case SearchFields_Worker::LAST_NAME:
  2473. case SearchFields_Worker::TITLE:
  2474. // force wildcards if none used on a LIKE
  2475. if(($oper == DevblocksSearchCriteria::OPER_LIKE || $oper == DevblocksSearchCriteria::OPER_NOT_LIKE)
  2476. && false === (strpos($value,'*'))) {
  2477. $value = '*'.$value.'*';
  2478. }
  2479. $criteria = new DevblocksSearchCriteria($field, $oper, $value);
  2480. break;
  2481. case SearchFields_Worker::LAST_ACTIVITY_DATE:
  2482. @$from = DevblocksPlatform::importGPC($_REQUEST['from'],'string','');
  2483. @$to = DevblocksPlatform::importGPC($_REQUEST['to'],'string','');
  2484. if(empty($from)) $from = 0;
  2485. if(empty($to)) $to = 'today';
  2486. $criteria = new DevblocksSearchCriteria($field,$oper,array($from,$to));
  2487. break;
  2488. case SearchFields_Worker::IS_DISABLED:
  2489. case SearchFields_Worker::IS_SUPERUSER:
  2490. @$bool = DevblocksPlatform::importGPC($_REQUEST['bool'],'integer',1);
  2491. $criteria = new DevblocksSearchCriteria($field,$oper,$bool);
  2492. break;
  2493. default:
  2494. // Custom Fields
  2495. if(substr($field,0,3)=='cf_') {
  2496. $criteria = $this->_doSetCriteriaCustomField($field, substr($field,3));
  2497. }
  2498. break;
  2499. }
  2500. if(!empty($criteria)) {
  2501. $this->params[$field] = $criteria;
  2502. $this->renderPage = 0;
  2503. }
  2504. }
  2505. function doBulkUpdate($filter, $do, $ids=array()) {
  2506. @set_time_limit(600); // [TODO] Temp!
  2507. $change_fields = array();
  2508. $custom_fields = array();
  2509. if(empty($do))
  2510. return;
  2511. if(is_array($do))
  2512. foreach($do as $k => $v) {
  2513. switch($k) {
  2514. case 'is_disabled':
  2515. $change_fields[DAO_Worker::IS_DISABLED] = intval($v);
  2516. break;
  2517. default:
  2518. // Custom fields
  2519. if(substr($k,0,3)=="cf_") {
  2520. $custom_fields[substr($k,3)] = $v;
  2521. }
  2522. break;
  2523. }
  2524. }
  2525. $pg = 0;
  2526. if(empty($ids))
  2527. do {
  2528. list($objects,$null) = DAO_Worker::search(
  2529. array(),
  2530. $this->params,
  2531. 100,
  2532. $pg++,
  2533. SearchFields_Worker::ID,
  2534. true,
  2535. false
  2536. );
  2537. $ids = array_merge($ids, array_keys($objects));
  2538. } while(!empty($objects));
  2539. $batch_total = count($ids);
  2540. for($x=0;$x<=$batch_total;$x+=100) {
  2541. $batch_ids = array_slice($ids,$x,100);
  2542. DAO_Worker::updateAgent($batch_ids, $change_fields);
  2543. // Custom Fields
  2544. self::_doBulkSetCustomFields(ChCustomFieldSource_Worker::ID, $custom_fields, $batch_ids);
  2545. unset($batch_ids);
  2546. }
  2547. unset($ids);
  2548. }
  2549. };
  2550. class C4_WorkerEventView extends C4_AbstractView {
  2551. const DEFAULT_ID = 'worker_events';
  2552. function __construct() {
  2553. $this->id = self::DEFAULT_ID;
  2554. $this->name = 'Worker Events';
  2555. $this->renderLimit = 100;
  2556. $this->renderSortBy = SearchFields_WorkerEvent::CREATED_DATE;
  2557. $this->renderSortAsc = false;
  2558. $this->view_columns = array(
  2559. SearchFields_WorkerEvent::CONTENT,
  2560. SearchFields_WorkerEvent::CREATED_DATE,
  2561. );
  2562. $this->doResetCriteria();
  2563. }
  2564. function getData() {
  2565. $objects = DAO_WorkerEvent::search(
  2566. $this->params,
  2567. $this->renderLimit,
  2568. $this->renderPage,
  2569. $this->renderSortBy,
  2570. $this->renderSortAsc
  2571. );
  2572. return $objects;
  2573. }
  2574. function render() {
  2575. $this->_sanitize();
  2576. $tpl = DevblocksPlatform::getTemplateService();
  2577. $tpl->assign('id', $this->id);
  2578. $tpl->assign('view', $this);
  2579. $workers = DAO_Worker::getAll();
  2580. $tpl->assign('workers', $workers);
  2581. $tpl->cache_lifetime = "0";
  2582. $tpl->assign('view_fields', $this->getColumns());
  2583. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/home/tabs/my_events/view.tpl');
  2584. }
  2585. function renderCriteria($field) {
  2586. $tpl = DevblocksPlatform::getTemplateService();
  2587. $tpl->assign('id', $this->id);
  2588. switch($field) {
  2589. case SearchFields_WorkerEvent::TITLE:
  2590. case SearchFields_WorkerEvent::CONTENT:
  2591. case SearchFields_WorkerEvent::URL:
  2592. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/internal/views/criteria/__string.tpl');
  2593. break;
  2594. // case SearchFields_WorkerEvent::ID:
  2595. // case SearchFields_WorkerEvent::MESSAGE_ID:
  2596. // case SearchFields_WorkerEvent::TICKET_ID:
  2597. // case SearchFields_WorkerEvent::FILE_SIZE:
  2598. // $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/internal/views/criteria/__number.tpl');
  2599. // break;
  2600. case SearchFields_WorkerEvent::IS_READ:
  2601. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/internal/views/criteria/__bool.tpl');
  2602. break;
  2603. case SearchFields_WorkerEvent::CREATED_DATE:
  2604. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/internal/views/criteria/__date.tpl');
  2605. break;
  2606. case SearchFields_WorkerEvent::WORKER_ID:
  2607. $workers = DAO_Worker::getAllActive();
  2608. $tpl->assign('workers', $workers);
  2609. $tpl->display('file:' . DEVBLOCKS_PLUGIN_PATH . 'cerberusweb.core/templates/internal/views/criteria/__worker.tpl');
  2610. break;
  2611. default:
  2612. echo '';
  2613. break;
  2614. }
  2615. }
  2616. function renderCriteriaParam($param) {
  2617. $field = $param->field;
  2618. $values = !is_array($param->value) ? array($param->value) : $param->value;
  2619. switch($field) {
  2620. case SearchFields_WorkerEvent::WORKER_ID:
  2621. $workers = DAO_Worker::getAll();
  2622. $strings = array();
  2623. foreach($values as $val) {
  2624. if(empty($val))
  2625. $strings[] = "Nobody";
  2626. elseif(!isset($workers[$val]))
  2627. continue;
  2628. else
  2629. $strings[] = $workers[$val]->getName();
  2630. }
  2631. echo implode(", ", $strings);
  2632. break;
  2633. default:
  2634. parent::renderCriteriaParam($param);
  2635. break;
  2636. }
  2637. }
  2638. static function getFields() {
  2639. return SearchFields_WorkerEvent::getFields();
  2640. }
  2641. static function getSearchFields() {
  2642. $fields = self::getFields();
  2643. unset($fields[SearchFields_WorkerEvent::ID]);
  2644. return $fields;
  2645. }
  2646. static function getColumns() {
  2647. $fields = self::getFields();
  2648. return $fields;
  2649. }
  2650. function doResetCriteria() {
  2651. parent::doResetCriteria();
  2652. // $this->params = array(
  2653. // SearchFields_WorkerEvent::NUM_NONSPAM => new DevblocksSearchCriteria(SearchFields_WorkerEvent::NUM_NONSPAM,'>',0),
  2654. // );
  2655. }
  2656. function doSetCriteria($field, $oper, $value) {
  2657. $criteria = null;
  2658. switch($field) {
  2659. case SearchFields_WorkerEvent::TITLE:
  2660. case SearchFields_WorkerEvent::CONTENT:
  2661. case SearchFields_WorkerEvent::URL:
  2662. // force wildcards if none used on a LIKE
  2663. if(($oper == DevblocksSearchCriteria::OPER_LIKE || $oper == DevblocksSearchCriteria::OPER_NOT_LIKE)
  2664. && false === (strpos($value,'*'))) {
  2665. $value = '*'.$value.'*';
  2666. }
  2667. $criteria = new DevblocksSearchCriteria($field, $oper, $value);
  2668. break;
  2669. case SearchFields_WorkerEvent::WORKER_ID:
  2670. @$worker_ids = DevblocksPlatform::importGPC($_REQUEST['worker_id'],'array',array());
  2671. $criteria = new DevblocksSearchCriteria($field,$oper,$worker_ids);
  2672. break;
  2673. case SearchFields_WorkerEvent::CREATED_DATE:
  2674. @$from = DevblocksPlatform::importGPC($_REQUEST['from'],'string','');
  2675. @$to = DevblocksPlatform::importGPC($_REQUEST['to'],'string','');
  2676. if(empty($from)) $from = 0;
  2677. if(empty($to)) $to = 'today';
  2678. $criteria = new DevblocksSearchCriteria($field,$oper,array($from,$to));
  2679. break;
  2680. case SearchFields_WorkerEvent::IS_READ:
  2681. @$bool = DevblocksPlatform::importGPC($_REQUEST['bool'],'integer',1);
  2682. $criteria = new DevblocksSearchCriteria($field,$oper,$bool);
  2683. break;
  2684. }
  2685. if(!empty($criteria)) {
  2686. $this->params[$field] = $criteria;
  2687. $this->renderPage = 0;
  2688. }
  2689. }
  2690. // function doBulkUpdate($filter, $do, $ids=array()) {
  2691. // @set_time_limit(600); // [TODO] Temp!
  2692. //
  2693. // $change_fields = array();
  2694. //
  2695. // if(empty($do))
  2696. // return;
  2697. //
  2698. // if(is_array($do))
  2699. // foreach($do as $k => $v) {
  2700. // switch($k) {
  2701. // case 'banned':
  2702. // $change_fields[DAO_Address::IS_BANNED] = intval($v);
  2703. // break;
  2704. // }
  2705. // }
  2706. //
  2707. // $pg = 0;
  2708. //
  2709. // if(empty($ids))
  2710. // do {
  2711. // list($objects,$null) = DAO_Address::search(
  2712. // $this->params,
  2713. // 100,
  2714. // $pg++,
  2715. // SearchFields_Address::ID,
  2716. // true,
  2717. // false
  2718. // );
  2719. //
  2720. // $ids = array_merge($ids, array_keys($objects));
  2721. //
  2722. // } while(!empty($objects));
  2723. //
  2724. // $batch_total = count($ids);
  2725. // for($x=0;$x<=$batch_total;$x+=100) {
  2726. // $batch_ids = array_slice($ids,$x,100);
  2727. // DAO_Address::update($batch_ids, $change_fields);
  2728. // unset($batch_ids);
  2729. // }
  2730. //
  2731. // unset($ids);
  2732. // }
  2733. };
  2734. class Model_ContactOrg {
  2735. public $id;
  2736. public $name;
  2737. public $street;
  2738. public $city;
  2739. public $province;
  2740. public $postal;
  2741. public $country;
  2742. public $phone;
  2743. public $website;
  2744. public $created;
  2745. public $sync_id = '';
  2746. };
  2747. class Model_WorkerWorkspaceList {
  2748. public $id = 0;
  2749. public $worker_id = 0;
  2750. public $workspace = '';
  2751. public $source_extension = '';
  2752. public $list_view = '';
  2753. public $list_pos = 0;
  2754. };
  2755. class Model_WorkerWorkspaceListView {
  2756. public $title = 'New List';
  2757. // public $workspace = '';
  2758. public $columns = array();
  2759. public $num_rows = 10;
  2760. public $params = array();
  2761. public $sort_by = null;
  2762. public $sort_asc = 1;
  2763. };
  2764. class Model_Activity {
  2765. public $translation_code;
  2766. public $params;
  2767. public function __construct($translation_code='activity.default',$params=array()) {
  2768. $this->translation_code = $translation_code;
  2769. $this->params = $params;
  2770. }
  2771. public function toString(CerberusWorker $worker=null) {
  2772. if(null == $worker)
  2773. return;
  2774. $translate = DevblocksPlatform::getTranslationService();
  2775. $params = $this->params;
  2776. // Prepend the worker name to the activity's param list
  2777. array_unshift($params, sprintf("<b>%s</b>%s",
  2778. $worker->getName(),
  2779. (!empty($worker->title)
  2780. ? (' (' . $worker->title . ')')
  2781. : ''
  2782. )
  2783. ));
  2784. return vsprintf(
  2785. $translate->_($this->translation_code),
  2786. $params
  2787. );
  2788. }
  2789. }
  2790. class Model_MailToGroupRule {
  2791. public $id = 0;
  2792. public $pos = 0;
  2793. public $created = 0;
  2794. public $name = '';
  2795. public $criteria = array();
  2796. public $actions = array();
  2797. public $is_sticky = 0;
  2798. public $sticky_order = 0;
  2799. static function getMatches(Model_Address $fromAddress, CerberusParserMessage $message) {
  2800. // print_r($fromAddress);
  2801. // print_r($message);
  2802. $matches = array();
  2803. $rules = DAO_MailToGroupRule::getWhere();
  2804. $message_headers = $message->headers;
  2805. $custom_fields = DAO_CustomField::getAll();
  2806. // Lazy load when needed on criteria basis
  2807. $address_field_values = null;
  2808. $org_field_values = null;
  2809. // Check filters
  2810. if(is_array($rules))
  2811. foreach($rules as $rule) { /* @var $rule Model_MailToGroupRule */
  2812. $passed = 0;
  2813. // check criteria
  2814. foreach($rule->criteria as $crit_key => $crit) {
  2815. @$value = $crit['value'];
  2816. switch($crit_key) {
  2817. case 'dayofweek':
  2818. $current_day = strftime('%w');
  2819. // $current_day = 1;
  2820. // Forced to English abbrevs as indexes
  2821. $days = array('sun','mon','tue','wed','thu','fri','sat');
  2822. // Is the current day enabled?
  2823. if(isset($crit[$days[$current_day]])) {
  2824. $passed++;
  2825. }
  2826. break;
  2827. case 'timeofday':
  2828. $current_hour = strftime('%H');
  2829. $current_min = strftime('%M');
  2830. // $current_hour = 17;
  2831. // $current_min = 5;
  2832. if(null != ($from_time = @$crit['from']))
  2833. list($from_hour, $from_min) = explode(':', $from_time);
  2834. if(null != ($to_time = @$crit['to']))
  2835. if(list($to_hour, $to_min) = explode(':', $to_time));
  2836. // Do we need to wrap around to the next day's hours?
  2837. if($from_hour > $to_hour) { // yes
  2838. $to_hour += 24; // add 24 hrs to the destination (1am = 25th hour)
  2839. }
  2840. // Are we in the right 24 hourly range?
  2841. if((integer)$current_hour >= $from_hour && (integer)$current_hour <= $to_hour) {
  2842. // If we're in the first hour, are we minutes early?
  2843. if($current_hour==$from_hour && (integer)$current_min < $from_min)
  2844. break;
  2845. // If we're in the last hour, are we minutes late?
  2846. if($current_hour==$to_hour && (integer)$current_min > $to_min)
  2847. break;
  2848. $passed++;
  2849. }
  2850. break;
  2851. case 'tocc':
  2852. $tocc = array();
  2853. $destinations = DevblocksPlatform::parseCsvString($value);
  2854. // Build a list of To/Cc addresses on this message
  2855. @$to_list = imap_rfc822_parse_adrlist($message_headers['to'],'localhost');
  2856. @$cc_list = imap_rfc822_parse_adrlist($message_headers['cc'],'localhost');
  2857. if(is_array($to_list))
  2858. foreach($to_list as $addy) {
  2859. $tocc[] = $addy->mailbox . '@' . $addy->host;
  2860. }
  2861. if(is_array($cc_list))
  2862. foreach($cc_list as $addy) {
  2863. $tocc[] = $addy->mailbox . '@' . $addy->host;
  2864. }
  2865. $dest_flag = false; // bail out when true
  2866. if(is_array($destinations) && is_array($tocc))
  2867. foreach($destinations as $dest) {
  2868. if($dest_flag) break;
  2869. $regexp_dest = DevblocksPlatform::strToRegExp($dest);
  2870. foreach($tocc as $addy) {
  2871. if(@preg_match($regexp_dest, $addy)) {
  2872. $passed++;
  2873. $dest_flag = false;
  2874. break;
  2875. }
  2876. }
  2877. }
  2878. break;
  2879. case 'from':
  2880. $regexp_from = DevblocksPlatform::strToRegExp($value);
  2881. if(@preg_match($regexp_from, $fromAddress->email)) {
  2882. $passed++;
  2883. }
  2884. break;
  2885. case 'subject':
  2886. // [TODO] Decode if necessary
  2887. @$subject = $message_headers['subject'];
  2888. $regexp_subject = DevblocksPlatform::strToRegExp($value);
  2889. if(@preg_match($regexp_subject, $subject)) {
  2890. $passed++;
  2891. }
  2892. break;
  2893. case 'body':
  2894. // Line-by-line body scanning (sed-like)
  2895. $lines = preg_split("/[\r\n]/", $message->body);
  2896. if(is_array($lines))
  2897. foreach($lines as $line) {
  2898. if(@preg_match($value, $line)) {
  2899. $passed++;
  2900. break;
  2901. }
  2902. }
  2903. break;
  2904. case 'header1':
  2905. case 'header2':
  2906. case 'header3':
  2907. case 'header4':
  2908. case 'header5':
  2909. @$header = strtolower($crit['header']);
  2910. if(empty($header)) {
  2911. $passed++;
  2912. break;
  2913. }
  2914. if(empty($value)) { // we're checking for null/blanks
  2915. if(!isset($message_headers[$header]) || empty($message_headers[$header])) {
  2916. $passed++;
  2917. }
  2918. } elseif(isset($message_headers[$header]) && !empty($message_headers[$header])) {
  2919. $regexp_header = DevblocksPlatform::strToRegExp($value);
  2920. // Flatten CRLF
  2921. if(@preg_match($regexp_header, str_replace(array("\r","\n"),' ',$message_headers[$header]))) {
  2922. $passed++;
  2923. }
  2924. }
  2925. break;
  2926. default: // ignore invalids
  2927. // Custom Fields
  2928. if(0==strcasecmp('cf_',substr($crit_key,0,3))) {
  2929. $field_id = substr($crit_key,3);
  2930. // Make sure it exists
  2931. if(null == (@$field = $custom_fields[$field_id]))
  2932. continue;
  2933. // Lazy values loader
  2934. $field_values = array();
  2935. switch($field->source_extension) {
  2936. case ChCustomFieldSource_Address::ID:
  2937. if(null == $address_field_values)
  2938. $address_field_values = array_shift(DAO_CustomFieldValue::getValuesBySourceIds(ChCustomFieldSource_Address::ID, $fromAddress->id));
  2939. $field_values =& $address_field_values;
  2940. break;
  2941. case ChCustomFieldSource_Org::ID:
  2942. if(null == $org_field_values)
  2943. $org_field_values = array_shift(DAO_CustomFieldValue::getValuesBySourceIds(ChCustomFieldSource_Org::ID, $fromAddress->contact_org_id));
  2944. $field_values =& $org_field_values;
  2945. break;
  2946. }
  2947. // No values, default.
  2948. if(!isset($field_values[$field_id]))
  2949. continue;
  2950. // Type sensitive value comparisons
  2951. switch($field->type) {
  2952. case 'S': // string
  2953. case 'T': // clob
  2954. case 'U': // URL
  2955. $field_val = isset($field_values[$field_id]) ? $field_values[$field_id] : '';
  2956. $oper = isset($crit['oper']) ? $crit['oper'] : "=";
  2957. if($oper == "=" && @preg_match(DevblocksPlatform::strToRegExp($value, true), $field_val))
  2958. $passed++;
  2959. elseif($oper == "!=" && @!preg_match(DevblocksPlatform::strToRegExp($value, true), $field_val))
  2960. $passed++;
  2961. break;
  2962. case 'N': // number
  2963. if(!isset($field_values[$field_id]))
  2964. break;
  2965. $field_val = isset($field_values[$field_id]) ? $field_values[$field_id] : 0;
  2966. $oper = isset($crit['oper']) ? $crit['oper'] : "=";
  2967. if($oper=="=" && intval($field_val)==intval($value))
  2968. $passed++;
  2969. elseif($oper=="!=" && intval($field_val)!=intval($value))
  2970. $passed++;
  2971. elseif($oper==">" && intval($field_val) > intval($value))
  2972. $passed++;
  2973. elseif($oper=="<" && intval($field_val) < intval($value))
  2974. $passed++;
  2975. break;
  2976. case 'E': // date
  2977. $field_val = isset($field_values[$field_id]) ? intval($field_values[$field_id]) : 0;
  2978. $from = isset($crit['from']) ? $crit['from'] : "0";
  2979. $to = isset($crit['to']) ? $crit['to'] : "now";
  2980. if(intval(@strtotime($from)) <= $field_val && intval(@strtotime($to)) >= $field_val) {
  2981. $passed++;
  2982. }
  2983. break;
  2984. case 'C': // checkbox
  2985. $field_val = isset($field_values[$field_id]) ? $field_values[$field_id] : 0;
  2986. if(intval($value)==intval($field_val))
  2987. $passed++;
  2988. break;
  2989. case 'D': // dropdown
  2990. case 'X': // multi-checkbox
  2991. case 'M': // multi-picklist
  2992. case 'W': // worker
  2993. $field_val = isset($field_values[$field_id]) ? $field_values[$field_id] : array();
  2994. if(!is_array($value)) $value = array($value);
  2995. if(is_array($field_val)) { // if multiple things set
  2996. foreach($field_val as $v) { // loop through possible
  2997. if(isset($value[$v])) { // is any possible set?
  2998. $passed++;
  2999. break;
  3000. }
  3001. }
  3002. } else { // single
  3003. if(isset($value[$field_val])) { // is our set field in possibles?
  3004. $passed++;
  3005. break;
  3006. }
  3007. }
  3008. break;
  3009. }
  3010. }
  3011. break;
  3012. }
  3013. }
  3014. // If our rule matched every criteria, stop and return the filter
  3015. if($passed == count($rule->criteria)) {
  3016. DAO_MailToGroupRule::increment($rule->id); // ++ the times we've matched
  3017. $matches[$rule->id] = $rule;
  3018. // Bail out if this rule had a move action
  3019. if(isset($rule->actions['move']))
  3020. return $matches;
  3021. }
  3022. }
  3023. // If we're at the end of rules and didn't bail out yet
  3024. if(!empty($matches))
  3025. return $matches;
  3026. // No matches
  3027. return NULL;
  3028. }
  3029. /**
  3030. * @param integer[] $ticket_ids
  3031. */
  3032. function run($ticket_ids) {
  3033. if(!is_array($ticket_ids)) $ticket_ids = array($ticket_ids);
  3034. $fields = array();
  3035. $field_values = array();
  3036. $groups = DAO_Group::getAll();
  3037. $buckets = DAO_Bucket::getAll();
  3038. // $workers = DAO_Worker::getAll();
  3039. $custom_fields = DAO_CustomField::getAll();
  3040. // actions
  3041. if(is_array($this->actions))
  3042. foreach($this->actions as $action => $params) {
  3043. switch($action) {
  3044. // case 'status':
  3045. // if(isset($params['is_waiting']))
  3046. // $fields[DAO_Ticket::IS_WAITING] = intval($params['is_waiting']);
  3047. // if(isset($params['is_closed']))
  3048. // $fields[DAO_Ticket::IS_CLOSED] = intval($params['is_closed']);
  3049. // if(isset($params['is_deleted']))
  3050. // $fields[DAO_Ticket::IS_DELETED] = intval($params['is_deleted']);
  3051. // break;
  3052. // case 'assign':
  3053. // if(isset($params['worker_id'])) {
  3054. // $w_id = intval($params['worker_id']);
  3055. // if(0 == $w_id || isset($workers[$w_id]))
  3056. // $fields[DAO_Ticket::NEXT_WORKER_ID] = $w_id;
  3057. // }
  3058. // break;
  3059. case 'move':
  3060. if(isset($params['group_id']) && isset($params['bucket_id'])) {
  3061. $g_id = intval($params['group_id']);
  3062. $b_id = intval($params['bucket_id']);
  3063. if(isset($groups[$g_id]) && (0==$b_id || isset($buckets[$b_id]))) {
  3064. $fields[DAO_Ticket::TEAM_ID] = $g_id;
  3065. $fields[DAO_Ticket::CATEGORY_ID] = $b_id;
  3066. }
  3067. }
  3068. break;
  3069. // case 'spam':
  3070. // if(isset($params['is_spam'])) {
  3071. // if(intval($params['is_spam'])) {
  3072. // foreach($ticket_ids as $ticket_id)
  3073. // CerberusBayes::markTicketAsSpam($ticket_id);
  3074. // } else {
  3075. // foreach($ticket_ids as $ticket_id)
  3076. // CerberusBayes::markTicketAsNotSpam($ticket_id);
  3077. // }
  3078. // }
  3079. // break;
  3080. default:
  3081. // Custom fields
  3082. if(substr($action,0,3)=="cf_") {
  3083. $field_id = intval(substr($action,3));
  3084. if(!isset($custom_fields[$field_id]) || !isset($params['value']))
  3085. break;
  3086. $field_values[$field_id] = $params;
  3087. }
  3088. break;
  3089. }
  3090. }
  3091. if(!empty($ticket_ids)) {
  3092. if(!empty($fields))
  3093. DAO_Ticket::updateTicket($ticket_ids, $fields);
  3094. // Custom Fields
  3095. C4_AbstractView::_doBulkSetCustomFields(ChCustomFieldSource_Ticket::ID, $field_values, $ticket_ids);
  3096. }
  3097. }
  3098. };
  3099. class CerberusVisit extends DevblocksVisit {
  3100. private $worker;
  3101. const KEY_VIEW_LAST_ACTION = 'view_last_action';
  3102. const KEY_MY_WORKSPACE = 'view_my_workspace';
  3103. const KEY_MAIL_MODE = 'mail_mode';
  3104. const KEY_HOME_SELECTED_TAB = 'home_selected_tab';
  3105. const KEY_OVERVIEW_FILTER = 'overview_filter';
  3106. const KEY_WORKFLOW_FILTER = 'workflow_filter';
  3107. public function __construct() {
  3108. $this->worker = null;
  3109. }
  3110. /**
  3111. * @return CerberusWorker
  3112. */
  3113. public function getWorker() {
  3114. return $this->worker;
  3115. }
  3116. public function setWorker(CerberusWorker $worker=null) {
  3117. $this->worker = $worker;
  3118. }
  3119. };
  3120. class CerberusBayesWord {
  3121. public $id = -1;
  3122. public $word = '';
  3123. public $spam = 0;
  3124. public $nonspam = 0;
  3125. public $probability = CerberusBayes::PROBABILITY_UNKNOWN;
  3126. public $interest_rating = 0.0;
  3127. }
  3128. class CerberusWorker {
  3129. public $id;
  3130. public $first_name;
  3131. public $last_name;
  3132. public $email;
  3133. public $pass;
  3134. public $title;
  3135. public $is_superuser=0;
  3136. public $is_disabled=0;
  3137. public $last_activity;
  3138. public $last_activity_date;
  3139. /**
  3140. * @return Model_TeamMember[]
  3141. */
  3142. function getMemberships() {
  3143. return DAO_Worker::getWorkerGroups($this->id);
  3144. }
  3145. function hasPriv($priv_id) {
  3146. // We don't need to do much work if we're a superuser
  3147. if($this->is_superuser)
  3148. return true;
  3149. $settings = CerberusSettings::getInstance();
  3150. $acl_enabled = $settings->get(CerberusSettings::ACL_ENABLED);
  3151. // ACL is a paid feature (please respect the licensing and support the project!)
  3152. $license = CerberusLicense::getInstance();
  3153. if(!$acl_enabled || !isset($license['serial']) || isset($license['a']))
  3154. return ("core.config"==substr($priv_id,0,11)) ? false : true;
  3155. // Check the aggregated worker privs from roles
  3156. $acl = DAO_WorkerRole::getACL();
  3157. $privs_by_worker = $acl[DAO_WorkerRole::CACHE_KEY_PRIVS_BY_WORKER];
  3158. if(!empty($priv_id) && isset($privs_by_worker[$this->id][$priv_id]))
  3159. return true;
  3160. return false;
  3161. }
  3162. function isTeamManager($team_id) {
  3163. @$memberships = $this->getMemberships();
  3164. $teams = DAO_Group::getAll();
  3165. if(
  3166. empty($team_id) // null
  3167. || !isset($teams[$team_id]) // doesn't exist
  3168. || !isset($memberships[$team_id]) // not a member
  3169. || (!$memberships[$team_id]->is_manager && !$this->is_superuser) // not a manager or superuser
  3170. ){
  3171. return false;
  3172. }
  3173. return true;
  3174. }
  3175. function isTeamMember($team_id) {
  3176. @$memberships = $this->getMemberships();
  3177. $teams = DAO_Group::getAll();
  3178. if(
  3179. empty($team_id) // null
  3180. || !isset($teams[$team_id]) // not a team
  3181. || !isset($memberships[$team_id]) // not a member
  3182. ) {
  3183. return false;
  3184. }
  3185. return true;
  3186. }
  3187. function getName($reverse=false) {
  3188. if(!$reverse) {
  3189. $name = sprintf("%s%s%s",
  3190. $this->first_name,
  3191. (!empty($this->first_name) && !empty($this->last_name)) ? " " : "",
  3192. $this->last_name
  3193. );
  3194. } else {
  3195. $name = sprintf("%s%s%s",
  3196. $this->last_name,
  3197. (!empty($this->first_name) && !empty($this->last_name)) ? ", " : "",
  3198. $this->first_name
  3199. );
  3200. }
  3201. return $name;
  3202. }
  3203. };
  3204. class Model_WorkerRole {
  3205. public $id;
  3206. public $name;
  3207. };
  3208. class Model_WorkerEvent {
  3209. public $id;
  3210. public $created_date;
  3211. public $worker_id;
  3212. public $title;
  3213. public $content;
  3214. public $is_read;
  3215. public $url;
  3216. };
  3217. class Model_ViewRss {
  3218. public $id = 0;
  3219. public $title = '';
  3220. public $hash = '';
  3221. public $worker_id = 0;
  3222. public $created = 0;
  3223. public $source_extension = '';
  3224. public $params = array();
  3225. };
  3226. class Model_TicketViewLastAction {
  3227. // [TODO] Recycle the bulk update constants for these actions?
  3228. const ACTION_NOT_SPAM = 'not_spam';
  3229. const ACTION_SPAM = 'spam';
  3230. const ACTION_CLOSE = 'close';
  3231. const ACTION_DELETE = 'delete';
  3232. const ACTION_MOVE = 'move';
  3233. const ACTION_TAKE = 'take';
  3234. const ACTION_SURRENDER = 'surrender';
  3235. const ACTION_WAITING = 'waiting';
  3236. const ACTION_NOT_WAITING = 'not_waiting';
  3237. public $ticket_ids = array(); // key = ticket id, value=old value
  3238. public $action = ''; // spam/closed/move, etc.
  3239. public $action_params = array(); // DAO Actions Taken
  3240. };
  3241. class CerberusTicketStatus {
  3242. const OPEN = 0;
  3243. const CLOSED = 1;
  3244. };
  3245. class CerberusTicketSpamTraining { // [TODO] Append 'Enum' to class name?
  3246. const BLANK = '';
  3247. const NOT_SPAM = 'N';
  3248. const SPAM = 'S';
  3249. };
  3250. class CerberusTicket {
  3251. public $id;
  3252. public $mask;
  3253. public $subject;
  3254. public $is_waiting = 0;
  3255. public $is_closed = 0;
  3256. public $is_deleted = 0;
  3257. public $team_id;
  3258. public $category_id;
  3259. public $first_message_id;
  3260. public $first_wrote_address_id;
  3261. public $last_wrote_address_id;
  3262. public $created_date;
  3263. public $updated_date;
  3264. public $due_date;
  3265. public $unlock_date;
  3266. public $spam_score;
  3267. public $spam_training;
  3268. public $interesting_words;
  3269. public $last_action_code;
  3270. public $last_worker_id;
  3271. public $next_worker_id;
  3272. function CerberusTicket() {}
  3273. function getMessages() {
  3274. $messages = DAO_Ticket::getMessagesByTicket($this->id);
  3275. return $messages;
  3276. }
  3277. function getRequesters() {
  3278. $requesters = DAO_Ticket::getRequestersByTicket($this->id);
  3279. return $requesters;
  3280. }
  3281. /**
  3282. * @return CloudGlueTag[]
  3283. */
  3284. function getTags() {
  3285. $result = DAO_CloudGlue::getTagsOnContents(array($this->id), CerberusApplication::INDEX_TICKETS);
  3286. $tags = array_shift($result);
  3287. return $tags;
  3288. }
  3289. };
  3290. class CerberusTicketActionCode {
  3291. const TICKET_OPENED = 'O';
  3292. const TICKET_CUSTOMER_REPLY = 'R';
  3293. const TICKET_WORKER_REPLY = 'W';
  3294. };
  3295. class CerberusMessage {
  3296. public $id;
  3297. public $ticket_id;
  3298. public $created_date;
  3299. public $address_id;
  3300. public $is_outgoing;
  3301. public $worker_id;
  3302. function CerberusMessage() {}
  3303. function getContent() {
  3304. return DAO_MessageContent::get($this->id);
  3305. }
  3306. function getHeaders() {
  3307. return DAO_MessageHeader::getAll($this->id);
  3308. }
  3309. /**
  3310. * returns an array of the message's attachments
  3311. *
  3312. * @return Model_Attachment[]
  3313. */
  3314. function getAttachments() {
  3315. $attachments = DAO_Attachment::getByMessageId($this->id);
  3316. return $attachments;
  3317. }
  3318. };
  3319. class Model_MessageNote {
  3320. const TYPE_NOTE = 0;
  3321. const TYPE_WARNING = 1;
  3322. const TYPE_ERROR = 2;
  3323. public $id;
  3324. public $type;
  3325. public $message_id;
  3326. public $created;
  3327. public $worker_id;
  3328. public $content;
  3329. };
  3330. class Model_Note {
  3331. const EXTENSION_ID = 'cerberusweb.note';
  3332. public $id;
  3333. public $source_extension_id;
  3334. public $source_id;
  3335. public $created;
  3336. public $worker_id;
  3337. public $content;
  3338. };
  3339. class Model_Attachment {
  3340. public $id;
  3341. public $message_id;
  3342. public $display_name;
  3343. public $filepath;
  3344. public $file_size = 0;
  3345. public $mime_type = '';
  3346. public function getFileContents() {
  3347. $file_path = APP_STORAGE_PATH . '/attachments/';
  3348. if (!empty($this->filepath))
  3349. return file_get_contents($file_path.$this->filepath,false);
  3350. }
  3351. public function getFileSize() {
  3352. $file_path = APP_STORAGE_PATH . '/attachments/';
  3353. if (!empty($this->filepath))
  3354. return filesize($file_path.$this->filepath);
  3355. }
  3356. public static function saveToFile($file_id, $contents) {
  3357. $attachment_path = APP_STORAGE_PATH . '/attachments/';
  3358. // Make file attachments use buckets so we have a max per directory
  3359. $attachment_bucket = sprintf("%03d/",
  3360. mt_rand(1,100)
  3361. );
  3362. $attachment_file = $file_id;
  3363. if(!file_exists($attachment_path.$attachment_bucket)) {
  3364. @mkdir($attachment_path.$attachment_bucket, 0770, true);
  3365. // [TODO] Needs error checking
  3366. }
  3367. file_put_contents($attachment_path.$attachment_bucket.$attachment_file, $contents);
  3368. return $attachment_bucket.$attachment_file;
  3369. }
  3370. };
  3371. class CerberusTeam {
  3372. public $id;
  3373. public $name;
  3374. public $count;
  3375. public $is_default = 0;
  3376. }
  3377. class Model_TeamMember {
  3378. public $id;
  3379. public $team_id;
  3380. public $is_manager = 0;
  3381. }
  3382. class CerberusCategory {
  3383. public $id;
  3384. public $pos=0;
  3385. public $name = '';
  3386. public $team_id = 0;
  3387. public $is_assignable = 1;
  3388. }
  3389. class CerberusPop3Account {
  3390. public $id;
  3391. public $enabled=1;
  3392. public $nickname;
  3393. public $protocol='pop3';
  3394. public $host;
  3395. public $username;
  3396. public $password;
  3397. public $port=110;
  3398. };
  3399. class Model_Community {
  3400. public $id = 0;
  3401. public $name = '';
  3402. }
  3403. class Model_MailTemplate {
  3404. const TYPE_COMPOSE = 1;
  3405. const TYPE_REPLY = 2;
  3406. const TYPE_CREATE = 3;
  3407. // const TYPE_CLOSE = 4;
  3408. public $id = 0;
  3409. public $title = '';
  3410. public $description = '';
  3411. public $folder = '';
  3412. public $owner_id = 0;
  3413. public $template_type = 0;
  3414. public $content = '';
  3415. public function getRenderedContent($message_id) {
  3416. $raw = $this->content;
  3417. $replace = array();
  3418. $with = array();
  3419. $replace[] = '#timestamp#';
  3420. $with[] = date('r');
  3421. if(!empty($message_id)) {
  3422. $message = DAO_Ticket::getMessage($message_id);
  3423. $ticket = DAO_Ticket::getTicket($message->ticket_id);
  3424. $sender = DAO_Address::get($message->address_id);
  3425. $sender_org = DAO_ContactOrg::get($sender->contact_org_id);
  3426. $replace[] = '#sender_first_name#';
  3427. $replace[] = '#sender_last_name#';
  3428. $replace[] = '#sender_org#';
  3429. $with[] = $sender->first_name;
  3430. $with[] = $sender->last_name;
  3431. $with[] = (!empty($sender_org)?$sender_org->name:"");
  3432. $replace[] = '#ticket_id#';
  3433. $replace[] = '#ticket_mask#';
  3434. $replace[] = '#ticket_subject#';
  3435. $with[] = $ticket->id;
  3436. $with[] = $ticket->mask;
  3437. $with[] = $ticket->subject;
  3438. }
  3439. if(null != ($active_worker = CerberusApplication::getActiveWorker())) {
  3440. $worker = DAO_Worker::getAgent($active_worker->id); // most recent info (not session)
  3441. $replace[] = '#worker_first_name#';
  3442. $replace[] = '#worker_last_name#';
  3443. $replace[] = '#worker_title#';
  3444. $with[] = $worker->first_name;
  3445. $with[] = $worker->last_name;
  3446. $with[] = $worker->title;
  3447. }
  3448. return str_replace($replace, $with, $raw);
  3449. }
  3450. };
  3451. class Model_TicketComment {
  3452. public $id;
  3453. public $ticket_id;
  3454. public $address_id;
  3455. public $created;
  3456. public $comment;
  3457. public function getAddress() {
  3458. return DAO_Address::get($this->address_id);
  3459. }
  3460. };
  3461. class Model_CustomField {
  3462. const TYPE_CHECKBOX = 'C';
  3463. const TYPE_DROPDOWN = 'D';
  3464. const TYPE_DATE = 'E';
  3465. const TYPE_MULTI_PICKLIST = 'M';
  3466. const TYPE_NUMBER = 'N';
  3467. const TYPE_SINGLE_LINE = 'S';
  3468. const TYPE_MULTI_LINE = 'T';
  3469. const TYPE_URL = 'U';
  3470. const TYPE_WORKER = 'W';
  3471. const TYPE_MULTI_CHECKBOX = 'X';
  3472. public $id = 0;
  3473. public $name = '';
  3474. public $type = '';
  3475. public $group_id = 0;
  3476. public $source_extension = '';
  3477. public $pos = 0;
  3478. public $options = array();
  3479. static function getTypes() {
  3480. return array(
  3481. self::TYPE_SINGLE_LINE => 'Text: Single Line',
  3482. self::TYPE_MULTI_LINE => 'Text: Multi-Line',
  3483. self::TYPE_NUMBER => 'Number',
  3484. self::TYPE_DATE => 'Date',
  3485. self::TYPE_DROPDOWN => 'Picklist',
  3486. self::TYPE_MULTI_PICKLIST => 'Multi-Picklist',
  3487. self::TYPE_CHECKBOX => 'Checkbox',
  3488. self::TYPE_MULTI_CHECKBOX => 'Multi-Checkbox',
  3489. self::TYPE_WORKER => 'Worker',
  3490. self::TYPE_URL => 'URL',
  3491. // self::TYPE_FILE => 'File',
  3492. );
  3493. }
  3494. };
  3495. class Model_Task {
  3496. public $id;
  3497. public $title;
  3498. public $worker_id;
  3499. public $created;
  3500. public $due_date;
  3501. public $is_completed;
  3502. public $completed_date;
  3503. public $source_extension;
  3504. public $source_id;
  3505. };