PageRenderTime 66ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 1ms

/api/DAO.class.php

https://github.com/stolf/cerb4
PHP | 7276 lines | 5017 code | 1388 blank | 871 comment | 454 complexity | b4be966c677f6ad2bfba842536469640 MD5 | raw file

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

  1. <?php
  2. /***********************************************************************
  3. | Cerberus Helpdesk(tm) developed by WebGroup Media, LLC.
  4. |-----------------------------------------------------------------------
  5. | All source code & content (c) Copyright 2007, WebGroup Media LLC
  6. | unless specifically noted otherwise.
  7. |
  8. | This source code is released under the Cerberus Public License.
  9. | The latest version of this license can be found here:
  10. | http://www.cerberusweb.com/license.php
  11. |
  12. | By using this software, you acknowledge having read this license
  13. | and agree to be bound thereby.
  14. | ______________________________________________________________________
  15. | http://www.cerberusweb.com http://www.webgroupmedia.com/
  16. ***********************************************************************/
  17. /*
  18. * IMPORTANT LICENSING NOTE from your friends on the Cerberus Helpdesk Team
  19. *
  20. * Sure, it would be so easy to just cheat and edit this file to use the
  21. * software without paying for it. But we trust you anyway. In fact, we're
  22. * writing this software for you!
  23. *
  24. * Quality software backed by a dedicated team takes money to develop. We
  25. * don't want to be out of the office bagging groceries when you call up
  26. * needing a helping hand. We'd rather spend our free time coding your
  27. * feature requests than mowing the neighbors' lawns for rent money.
  28. *
  29. * We've never believed in encoding our source code out of paranoia over not
  30. * getting paid. We want you to have the full source code and be able to
  31. * make the tweaks your organization requires to get more done -- despite
  32. * having less of everything than you might need (time, people, money,
  33. * energy). We shouldn't be your bottleneck.
  34. *
  35. * We've been building our expertise with this project since January 2002. We
  36. * promise spending a couple bucks [Euro, Yuan, Rupees, Galactic Credits] to
  37. * let us take over your shared e-mail headache is a worthwhile investment.
  38. * It will give you a sense of control over your in-box that you probably
  39. * haven't had since spammers found you in a game of "E-mail Address
  40. * Battleship". Miss. Miss. You sunk my in-box!
  41. *
  42. * A legitimate license entitles you to support, access to the developer
  43. * mailing list, the ability to participate in betas and the warm fuzzy
  44. * feeling of feeding a couple obsessed developers who want to help you get
  45. * more done than 'the other guy'.
  46. *
  47. * - Jeff Standen, Mike Fogg, Brenan Cavish, Darren Sugita, Dan Hildebrandt
  48. * and Joe Geck.
  49. * WEBGROUP MEDIA LLC. - Developers of Cerberus Helpdesk
  50. */
  51. class C4_ORMHelper extends DevblocksORMHelper {
  52. static public function qstr($str) {
  53. $db = DevblocksPlatform::getDatabaseService();
  54. return $db->qstr($str);
  55. }
  56. static protected function _appendSelectJoinSqlForCustomFieldTables($tables, $params, $key, $select_sql, $join_sql) {
  57. $custom_fields = DAO_CustomField::getAll();
  58. $field_ids = array();
  59. $return_multiple_values = false; // can our CF return more than one hit? (GROUP BY)
  60. if(is_array($tables))
  61. foreach($tables as $tbl_name => $null) {
  62. // Filter and sanitize
  63. if(substr($tbl_name,0,3) != "cf_" // not a custom field
  64. || 0 == ($field_id = intval(substr($tbl_name,3)))) // not a field_id
  65. continue;
  66. // Make sure the field exists for this source
  67. if(!isset($custom_fields[$field_id]))
  68. continue;
  69. $field_table = sprintf("cf_%d", $field_id);
  70. $value_table = '';
  71. // Join value by field data type
  72. switch($custom_fields[$field_id]->type) {
  73. case 'T': // multi-line CLOB
  74. $value_table = 'custom_field_clobvalue';
  75. break;
  76. case 'C': // checkbox
  77. case 'E': // date
  78. case 'N': // number
  79. case 'W': // worker
  80. $value_table = 'custom_field_numbervalue';
  81. break;
  82. default:
  83. case 'S': // single-line
  84. case 'D': // dropdown
  85. case 'U': // URL
  86. $value_table = 'custom_field_stringvalue';
  87. break;
  88. }
  89. $has_multiple_values = false;
  90. switch($custom_fields[$field_id]->type) {
  91. case Model_CustomField::TYPE_MULTI_PICKLIST:
  92. case Model_CustomField::TYPE_MULTI_CHECKBOX:
  93. $has_multiple_values = true;
  94. break;
  95. }
  96. // If we have multiple values but we don't need to WHERE the JOIN, be efficient and don't GROUP BY
  97. if(!isset($params['cf_'.$field_id])) {
  98. $select_sql .= sprintf(",(SELECT field_value FROM %s WHERE %s=source_id AND field_id=%d LIMIT 0,1) AS %s ",
  99. $value_table,
  100. $key,
  101. $field_id,
  102. $field_table
  103. );
  104. } else {
  105. $select_sql .= sprintf(", %s.field_value as %s ",
  106. $field_table,
  107. $field_table
  108. );
  109. $join_sql .= sprintf("LEFT JOIN %s %s ON (%s=%s.source_id AND %s.field_id=%d) ",
  110. $value_table,
  111. $field_table,
  112. $key,
  113. $field_table,
  114. $field_table,
  115. $field_id
  116. );
  117. // If we do need to WHERE this JOIN, make sure we GROUP BY
  118. if($has_multiple_values)
  119. $return_multiple_values = true;
  120. }
  121. }
  122. return array($select_sql, $join_sql, $return_multiple_values);
  123. }
  124. }
  125. /**
  126. * Global Settings DAO
  127. */
  128. class DAO_Setting extends DevblocksORMHelper {
  129. static function set($key, $value) {
  130. $db = DevblocksPlatform::getDatabaseService();
  131. $db->Replace('setting',array('setting'=>$db->qstr($key),'value'=>$db->qstr($value)),array('setting'),false);
  132. }
  133. static function get($key) {
  134. $db = DevblocksPlatform::getDatabaseService();
  135. $sql = sprintf("SELECT value FROM setting WHERE setting = %s",
  136. $db->qstr($key)
  137. );
  138. $value = $db->GetOne($sql) or die(__CLASS__ . ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  139. return $value;
  140. }
  141. static function getSettings() {
  142. $cache = DevblocksPlatform::getCacheService();
  143. if(null === ($settings = $cache->load(CerberusApplication::CACHE_SETTINGS_DAO))) {
  144. $db = DevblocksPlatform::getDatabaseService();
  145. $settings = array();
  146. $sql = sprintf("SELECT setting,value FROM setting");
  147. $rs = $db->Execute($sql) or die(__CLASS__ . ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  148. if(is_a($rs,'ADORecordSet'))
  149. while(!$rs->EOF) {
  150. $settings[$rs->Fields('setting')] = $rs->Fields('value');
  151. $rs->MoveNext();
  152. }
  153. $cache->save($settings, CerberusApplication::CACHE_SETTINGS_DAO);
  154. }
  155. return $settings;
  156. }
  157. };
  158. /**
  159. * Bayesian Anti-Spam DAO
  160. */
  161. class DAO_Bayes {
  162. private function DAO_Bayes() {}
  163. /**
  164. * @return CerberusWord[]
  165. */
  166. static function lookupWordIds($words) {
  167. $db = DevblocksPlatform::getDatabaseService();
  168. $tmp = array();
  169. $outwords = array(); // CerberusWord
  170. // Escaped set
  171. if(is_array($words))
  172. foreach($words as $word) {
  173. $tmp[] = $db->escape($word);
  174. }
  175. if(empty($words))
  176. return array();
  177. $sql = sprintf("SELECT id,word,spam,nonspam FROM bayes_words WHERE word IN ('%s')",
  178. implode("','", $tmp)
  179. );
  180. $rs = $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  181. // [JAS]: Keep a list of words we can check off as we index them with IDs
  182. $tmp = array_flip($words); // words are now keys
  183. // Existing Words
  184. if(is_a($rs,'ADORecordSet'))
  185. while(!$rs->EOF) {
  186. $w = new CerberusBayesWord();
  187. $w->id = intval($rs->fields['id']);
  188. $w->word = mb_convert_case($rs->fields['word'], MB_CASE_LOWER);
  189. $w->spam = intval($rs->fields['spam']);
  190. $w->nonspam = intval($rs->fields['nonspam']);
  191. $outwords[mb_convert_case($w->word, MB_CASE_LOWER)] = $w;
  192. unset($tmp[$w->word]); // check off we've indexed this word
  193. $rs->MoveNext();
  194. }
  195. // Insert new words
  196. if(is_array($tmp))
  197. foreach($tmp as $new_word => $v) {
  198. $new_id = $db->GenID('bayes_words_seq');
  199. $sql = sprintf("INSERT INTO bayes_words (id,word) VALUES (%d,%s)",
  200. $new_id,
  201. $db->qstr($new_word)
  202. );
  203. $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  204. $w = new CerberusBayesWord();
  205. $w->id = $new_id;
  206. $w->word = $new_word;
  207. $outwords[$w->word] = $w;
  208. }
  209. return $outwords;
  210. }
  211. /**
  212. * @return array Two element array (keys: spam,nonspam)
  213. */
  214. static function getStatistics() {
  215. $db = DevblocksPlatform::getDatabaseService();
  216. // [JAS]: [TODO] Change this into a 'replace' index?
  217. $sql = "SELECT spam, nonspam FROM bayes_stats";
  218. $rs = $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  219. if($rs->NumRows()) {
  220. $spam = intval($rs->Fields('spam'));
  221. $nonspam = intval($rs->Fields('nonspam'));
  222. } else {
  223. $spam = 0;
  224. $nonspam = 0;
  225. $sql = "INSERT INTO bayes_stats (spam, nonspam) VALUES (0,0)";
  226. $db->Execute($sql);
  227. }
  228. return array('spam' => $spam,'nonspam' => $nonspam);
  229. }
  230. static function addOneToSpamTotal() {
  231. $db = DevblocksPlatform::getDatabaseService();
  232. $sql = "UPDATE bayes_stats SET spam = spam + 1";
  233. $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  234. }
  235. static function addOneToNonSpamTotal() {
  236. $db = DevblocksPlatform::getDatabaseService();
  237. $sql = "UPDATE bayes_stats SET nonspam = nonspam + 1";
  238. $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  239. }
  240. static function addOneToSpamWord($word_ids=array()) {
  241. if(!is_array($word_ids)) $word_ids = array($word_ids);
  242. if(empty($word_ids)) return;
  243. $db = DevblocksPlatform::getDatabaseService();
  244. $sql = sprintf("UPDATE bayes_words SET spam = spam + 1 WHERE id IN(%s)", implode(',',$word_ids));
  245. $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  246. }
  247. static function addOneToNonSpamWord($word_ids=array()) {
  248. if(!is_array($word_ids)) $word_ids = array($word_ids);
  249. if(empty($word_ids)) return;
  250. $db = DevblocksPlatform::getDatabaseService();
  251. $sql = sprintf("UPDATE bayes_words SET nonspam = nonspam + 1 WHERE id IN(%s)", implode(',',$word_ids));
  252. $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  253. }
  254. };
  255. /**
  256. * Worker DAO
  257. */
  258. class DAO_Worker extends C4_ORMHelper {
  259. private function DAO_Worker() {}
  260. const CACHE_ALL = 'ch_workers';
  261. const ID = 'id';
  262. const FIRST_NAME = 'first_name';
  263. const LAST_NAME = 'last_name';
  264. const TITLE = 'title';
  265. const EMAIL = 'email';
  266. const PASSWORD = 'pass';
  267. const IS_SUPERUSER = 'is_superuser';
  268. const IS_DISABLED = 'is_disabled';
  269. const LAST_ACTIVITY_DATE = 'last_activity_date';
  270. const LAST_ACTIVITY = 'last_activity';
  271. // [TODO] Convert to ::create($id, $fields)
  272. static function create($email, $password, $first_name, $last_name, $title) {
  273. if(empty($email) || empty($password))
  274. return null;
  275. $db = DevblocksPlatform::getDatabaseService();
  276. $id = $db->GenID('generic_seq');
  277. $sql = sprintf("INSERT INTO worker (id, email, pass, first_name, last_name, title, is_superuser, is_disabled) ".
  278. "VALUES (%d, %s, %s, %s, %s, %s,0,0)",
  279. $id,
  280. $db->qstr($email),
  281. $db->qstr(md5($password)),
  282. $db->qstr($first_name),
  283. $db->qstr($last_name),
  284. $db->qstr($title)
  285. );
  286. $rs = $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  287. self::clearCache();
  288. return $id;
  289. }
  290. static function clearCache() {
  291. $cache = DevblocksPlatform::getCacheService();
  292. $cache->remove(self::CACHE_ALL);
  293. }
  294. static function getAllActive() {
  295. return self::getAll(false, false);
  296. }
  297. static function getAllWithDisabled() {
  298. return self::getAll(false, true);
  299. }
  300. static function getAllOnline() {
  301. list($whos_online_workers, $null) = self::search(
  302. array(),
  303. array(
  304. new DevblocksSearchCriteria(SearchFields_Worker::LAST_ACTIVITY_DATE,DevblocksSearchCriteria::OPER_GT,(time()-60*15)), // idle < 15 mins
  305. new DevblocksSearchCriteria(SearchFields_Worker::LAST_ACTIVITY,DevblocksSearchCriteria::OPER_NOT_LIKE,'%translation_code";N;%'), // translation code not null (not just logged out)
  306. ),
  307. -1,
  308. 0,
  309. SearchFields_Worker::LAST_ACTIVITY_DATE,
  310. false,
  311. false
  312. );
  313. if(!empty($whos_online_workers))
  314. return self::getList(array_keys($whos_online_workers));
  315. return array();
  316. }
  317. static function getAll($nocache=false, $with_disabled=true) {
  318. $cache = DevblocksPlatform::getCacheService();
  319. if($nocache || null === ($workers = $cache->load(self::CACHE_ALL))) {
  320. $workers = self::getList();
  321. $cache->save($workers, self::CACHE_ALL);
  322. }
  323. /*
  324. * If the caller doesn't want disabled workers then remove them from the results,
  325. * but don't bother caching two different versions (always cache all)
  326. */
  327. if(!$with_disabled) {
  328. foreach($workers as $worker_id => $worker) { /* @var $worker CerberusWorker */
  329. if($worker->is_disabled)
  330. unset($workers[$worker_id]);
  331. }
  332. }
  333. return $workers;
  334. }
  335. static function getList($ids=array()) {
  336. if(!is_array($ids)) $ids = array($ids);
  337. $db = DevblocksPlatform::getDatabaseService();
  338. $workers = array();
  339. $sql = "SELECT a.id, a.first_name, a.last_name, a.email, a.pass, a.title, a.is_superuser, a.is_disabled, a.last_activity_date, a.last_activity ".
  340. "FROM worker a ".
  341. ((!empty($ids) ? sprintf("WHERE a.id IN (%s) ",implode(',',$ids)) : " ").
  342. "ORDER BY a.first_name, a.last_name "
  343. );
  344. $rs = $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  345. if(is_a($rs,'ADORecordSet'))
  346. while(!$rs->EOF) {
  347. $worker = new CerberusWorker();
  348. $worker->id = intval($rs->fields['id']);
  349. $worker->first_name = $rs->fields['first_name'];
  350. $worker->last_name = $rs->fields['last_name'];
  351. $worker->email = $rs->fields['email'];
  352. $worker->pass = $rs->fields['pass'];
  353. $worker->title = $rs->fields['title'];
  354. $worker->is_superuser = intval($rs->fields['is_superuser']);
  355. $worker->is_disabled = intval($rs->fields['is_disabled']);
  356. $worker->last_activity_date = intval($rs->fields['last_activity_date']);
  357. if(!empty($rs->fields['last_activity']))
  358. $worker->last_activity = unserialize($rs->fields['last_activity']);
  359. $workers[$worker->id] = $worker;
  360. $rs->MoveNext();
  361. }
  362. return $workers;
  363. }
  364. /**
  365. * @return CerberusWorker
  366. */
  367. static function getAgent($id) {
  368. if(empty($id)) return null;
  369. $workers = self::getAllWithDisabled();
  370. if(isset($workers[$id]))
  371. return $workers[$id];
  372. return null;
  373. }
  374. /**
  375. * Enter description here...
  376. *
  377. * @param string $email
  378. * @return integer $id
  379. */
  380. static function lookupAgentEmail($email) {
  381. if(empty($email)) return null;
  382. $db = DevblocksPlatform::getDatabaseService();
  383. $sql = sprintf("SELECT a.id FROM worker a WHERE a.email = %s",
  384. $db->qstr($email)
  385. );
  386. $rs = $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  387. if(is_a($rs,'ADORecordSet') && !$rs->EOF) {
  388. return intval($rs->fields['id']);
  389. }
  390. return null;
  391. }
  392. static function updateAgent($ids, $fields, $flush_cache=true) {
  393. if(!is_array($ids)) $ids = array($ids);
  394. $db = DevblocksPlatform::getDatabaseService();
  395. $sets = array();
  396. if(!is_array($fields) || empty($fields) || empty($ids))
  397. return;
  398. foreach($fields as $k => $v) {
  399. $sets[] = sprintf("%s = %s",
  400. $k,
  401. $db->qstr($v)
  402. );
  403. }
  404. $sql = sprintf("UPDATE worker SET %s WHERE id IN (%s)",
  405. implode(', ', $sets),
  406. implode(',', $ids)
  407. );
  408. $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  409. if($flush_cache) {
  410. self::clearCache();
  411. }
  412. }
  413. static function maint() {
  414. $db = DevblocksPlatform::getDatabaseService();
  415. $logger = DevblocksPlatform::getConsoleLog();
  416. $sql = "DELETE QUICK view_rss FROM view_rss LEFT JOIN worker ON view_rss.worker_id = worker.id WHERE worker.id IS NULL";
  417. $db->Execute($sql);
  418. $logger->info('[Maint] Purged ' . $db->Affected_Rows() . ' view_rss records.');
  419. $sql = "DELETE QUICK worker_mail_forward FROM worker_mail_forward LEFT JOIN worker ON worker_mail_forward.worker_id = worker.id WHERE worker.id IS NULL";
  420. $db->Execute($sql);
  421. $logger->info('[Maint] Purged ' . $db->Affected_Rows() . ' worker_mail_forward records.');
  422. $sql = "DELETE QUICK worker_pref FROM worker_pref LEFT JOIN worker ON worker_pref.worker_id = worker.id WHERE worker.id IS NULL";
  423. $db->Execute($sql);
  424. $logger->info('[Maint] Purged ' . $db->Affected_Rows() . ' worker_pref records.');
  425. $sql = "DELETE QUICK worker_to_team FROM worker_to_team LEFT JOIN worker ON worker_to_team.agent_id = worker.id WHERE worker.id IS NULL";
  426. $db->Execute($sql);
  427. $logger->info('[Maint] Purged ' . $db->Affected_Rows() . ' worker_to_team records.');
  428. $sql = "DELETE QUICK worker_workspace_list FROM worker_workspace_list LEFT JOIN worker ON worker_workspace_list.worker_id = worker.id WHERE worker.id IS NULL";
  429. $db->Execute($sql);
  430. // [TODO] Clear out workers from any group_inbox_filter rows
  431. $logger->info('[Maint] Purged ' . $db->Affected_Rows() . ' worker_workspace_list records.');
  432. }
  433. static function deleteAgent($id) {
  434. if(empty($id)) return;
  435. // [TODO] Delete worker notes, comments, etc.
  436. /* This event fires before the delete takes place in the db,
  437. * so we can denote what is actually changing against the db state
  438. */
  439. $eventMgr = DevblocksPlatform::getEventService();
  440. $eventMgr->trigger(
  441. new Model_DevblocksEvent(
  442. 'worker.delete',
  443. array(
  444. 'worker_ids' => array($id),
  445. )
  446. )
  447. );
  448. $db = DevblocksPlatform::getDatabaseService();
  449. $sql = sprintf("DELETE QUICK FROM worker WHERE id = %d", $id);
  450. $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  451. $sql = sprintf("DELETE QUICK FROM address_to_worker WHERE worker_id = %d", $id);
  452. $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  453. $sql = sprintf("DELETE QUICK FROM worker_to_team WHERE agent_id = %d", $id);
  454. $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  455. $sql = sprintf("DELETE QUICK FROM view_rss WHERE worker_id = %d", $id);
  456. $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  457. $sql = sprintf("DELETE QUICK FROM worker_workspace_list WHERE worker_id = %d", $id);
  458. $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  459. // Clear assigned workers
  460. $sql = sprintf("UPDATE ticket SET next_worker_id = 0 WHERE next_worker_id = %d", $id);
  461. $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  462. // Clear roles
  463. $db->Execute(sprintf("DELETE FROM worker_to_role WHERE worker_id = %d", $id));
  464. // Invalidate caches
  465. self::clearCache();
  466. $cache = DevblocksPlatform::getCacheService();
  467. $cache->remove(DAO_Group::CACHE_ROSTERS);
  468. }
  469. static function login($email, $password) {
  470. $db = DevblocksPlatform::getDatabaseService();
  471. // [TODO] Uniquely salt hashes
  472. $sql = sprintf("SELECT id ".
  473. "FROM worker ".
  474. "WHERE is_disabled = 0 ".
  475. "AND email = %s ".
  476. "AND pass = MD5(%s)",
  477. $db->qstr($email),
  478. $db->qstr($password)
  479. );
  480. $worker_id = $db->GetOne($sql); // or die(__CLASS__ . ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  481. if(!empty($worker_id)) {
  482. return self::getAgent($worker_id);
  483. }
  484. return null;
  485. }
  486. static function setAgentTeams($agent_id, $team_ids) {
  487. if(!is_array($team_ids)) $team_ids = array($team_ids);
  488. if(empty($agent_id)) return;
  489. $db = DevblocksPlatform::getDatabaseService();
  490. $sql = sprintf("DELETE QUICK FROM worker_to_team WHERE agent_id = %d",
  491. $agent_id
  492. );
  493. $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  494. foreach($team_ids as $team_id) {
  495. $sql = sprintf("INSERT INTO worker_to_team (agent_id, team_id) ".
  496. "VALUES (%d,%d)",
  497. $agent_id,
  498. $team_id
  499. );
  500. $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  501. }
  502. // Invalidate caches
  503. $cache = DevblocksPlatform::getCacheService();
  504. $cache->remove(DAO_Group::CACHE_ROSTERS);
  505. }
  506. /**
  507. * @return Model_TeamMember[]
  508. */
  509. static function getWorkerGroups($worker_id) {
  510. // Get the cache
  511. $rosters = DAO_Group::getRosters();
  512. $memberships = array();
  513. // Remove any groups our desired worker isn't in
  514. if(is_array($rosters))
  515. foreach($rosters as $group_id => $members) {
  516. if(isset($members[$worker_id])) {
  517. $memberships[$group_id] = $members[$worker_id];
  518. }
  519. }
  520. return $memberships;
  521. }
  522. /**
  523. * Store the workers last activity (provided by the page extension).
  524. *
  525. * @param integer $worker_id
  526. * @param Model_Activity $activity
  527. */
  528. static function logActivity($worker_id, Model_Activity $activity) {
  529. DAO_Worker::updateAgent($worker_id,array(
  530. DAO_Worker::LAST_ACTIVITY_DATE => time(),
  531. DAO_Worker::LAST_ACTIVITY => serialize($activity)
  532. ),false);
  533. }
  534. /**
  535. * Enter description here...
  536. *
  537. * @param DevblocksSearchCriteria[] $params
  538. * @param integer $limit
  539. * @param integer $page
  540. * @param string $sortBy
  541. * @param boolean $sortAsc
  542. * @param boolean $withCounts
  543. * @return array
  544. */
  545. static function search($columns, $params, $limit=10, $page=0, $sortBy=null, $sortAsc=null, $withCounts=true) {
  546. $db = DevblocksPlatform::getDatabaseService();
  547. $fields = SearchFields_Worker::getFields();
  548. // Sanitize
  549. if(!isset($fields[$sortBy]))
  550. $sortBy=null;
  551. list($tables,$wheres) = parent::_parseSearchParams($params, $columns, $fields, $sortBy);
  552. $start = ($page * $limit); // [JAS]: 1-based [TODO] clean up + document
  553. $total = -1;
  554. $select_sql = sprintf("SELECT ".
  555. "w.id as %s, ".
  556. "w.first_name as %s, ".
  557. "w.last_name as %s, ".
  558. "w.title as %s, ".
  559. "w.email as %s, ".
  560. "w.is_superuser as %s, ".
  561. "w.last_activity_date as %s, ".
  562. "w.is_disabled as %s ",
  563. SearchFields_Worker::ID,
  564. SearchFields_Worker::FIRST_NAME,
  565. SearchFields_Worker::LAST_NAME,
  566. SearchFields_Worker::TITLE,
  567. SearchFields_Worker::EMAIL,
  568. SearchFields_Worker::IS_SUPERUSER,
  569. SearchFields_Worker::LAST_ACTIVITY_DATE,
  570. SearchFields_Worker::IS_DISABLED
  571. );
  572. $join_sql = "FROM worker w ";
  573. // Custom field joins
  574. list($select_sql, $join_sql, $has_multiple_values) = self::_appendSelectJoinSqlForCustomFieldTables(
  575. $tables,
  576. $params,
  577. 'w.id',
  578. $select_sql,
  579. $join_sql
  580. );
  581. $where_sql = "".
  582. (!empty($wheres) ? sprintf("WHERE %s ",implode(' AND ',$wheres)) : "");
  583. $sort_sql = (!empty($sortBy)) ? sprintf("ORDER BY %s %s ",$sortBy,($sortAsc || is_null($sortAsc))?"ASC":"DESC") : " ";
  584. $sql =
  585. $select_sql.
  586. $join_sql.
  587. $where_sql.
  588. ($has_multiple_values ? 'GROUP BY w.id ' : '').
  589. $sort_sql;
  590. // [TODO] Could push the select logic down a level too
  591. if($limit > 0) {
  592. $rs = $db->SelectLimit($sql,$limit,$start) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  593. } else {
  594. $rs = $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  595. $total = $rs->RecordCount();
  596. }
  597. $results = array();
  598. if(is_a($rs,'ADORecordSet'))
  599. while(!$rs->EOF) {
  600. $result = array();
  601. foreach($rs->fields as $f => $v) {
  602. $result[$f] = $v;
  603. }
  604. $object_id = intval($rs->fields[SearchFields_Worker::ID]);
  605. $results[$object_id] = $result;
  606. $rs->MoveNext();
  607. }
  608. // [JAS]: Count all
  609. if($withCounts) {
  610. $count_sql =
  611. ($has_multiple_values ? "SELECT COUNT(DISTINCT w.id) " : "SELECT COUNT(w.id) ").
  612. $join_sql.
  613. $where_sql;
  614. $total = $db->GetOne($count_sql);
  615. }
  616. return array($results,$total);
  617. }
  618. };
  619. /**
  620. * ...
  621. *
  622. */
  623. class SearchFields_Worker implements IDevblocksSearchFields {
  624. // Worker
  625. const ID = 'w_id';
  626. const FIRST_NAME = 'w_first_name';
  627. const LAST_NAME = 'w_last_name';
  628. const TITLE = 'w_title';
  629. const EMAIL = 'w_email';
  630. const IS_SUPERUSER = 'w_is_superuser';
  631. const LAST_ACTIVITY = 'w_last_activity';
  632. const LAST_ACTIVITY_DATE = 'w_last_activity_date';
  633. const IS_DISABLED = 'w_is_disabled';
  634. /**
  635. * @return DevblocksSearchField[]
  636. */
  637. static function getFields() {
  638. $translate = DevblocksPlatform::getTranslationService();
  639. $columns = array(
  640. self::ID => new DevblocksSearchField(self::ID, 'w', 'id', null, $translate->_('common.id')),
  641. self::FIRST_NAME => new DevblocksSearchField(self::FIRST_NAME, 'w', 'first_name', null, $translate->_('worker.first_name')),
  642. self::LAST_NAME => new DevblocksSearchField(self::LAST_NAME, 'w', 'last_name', null, $translate->_('worker.last_name')),
  643. self::TITLE => new DevblocksSearchField(self::TITLE, 'w', 'title', null, $translate->_('worker.title')),
  644. self::EMAIL => new DevblocksSearchField(self::EMAIL, 'w', 'email', null, ucwords($translate->_('common.email'))),
  645. self::IS_SUPERUSER => new DevblocksSearchField(self::IS_SUPERUSER, 'w', 'is_superuser', null, $translate->_('worker.is_superuser')),
  646. self::LAST_ACTIVITY => new DevblocksSearchField(self::LAST_ACTIVITY, 'w', 'last_activity', null, $translate->_('worker.last_activity')),
  647. self::LAST_ACTIVITY_DATE => new DevblocksSearchField(self::LAST_ACTIVITY_DATE, 'w', 'last_activity_date', null, $translate->_('worker.last_activity_date')),
  648. self::IS_DISABLED => new DevblocksSearchField(self::IS_DISABLED, 'w', 'is_disabled', null, ucwords($translate->_('common.disabled'))),
  649. );
  650. // Custom Fields
  651. $fields = DAO_CustomField::getBySource(ChCustomFieldSource_Worker::ID);
  652. if(is_array($fields))
  653. foreach($fields as $field_id => $field) {
  654. $key = 'cf_'.$field_id;
  655. $columns[$key] = new DevblocksSearchField($key,$key,'field_value',null,$field->name);
  656. }
  657. // Sort by label (translation-conscious)
  658. uasort($columns, create_function('$a, $b', "return strcasecmp(\$a->db_label,\$b->db_label);\n"));
  659. return $columns;
  660. }
  661. };
  662. class DAO_WorkerRole extends DevblocksORMHelper {
  663. const _CACHE_ALL = 'ch_acl';
  664. const CACHE_KEY_ROLES = 'roles';
  665. const CACHE_KEY_PRIVS_BY_ROLE = 'privs_by_role';
  666. const CACHE_KEY_WORKERS_BY_ROLE = 'workers_by_role';
  667. const CACHE_KEY_PRIVS_BY_WORKER = 'privs_by_worker';
  668. const ID = 'id';
  669. const NAME = 'name';
  670. static function create($fields) {
  671. $db = DevblocksPlatform::getDatabaseService();
  672. $id = $db->GenID('generic_seq');
  673. $sql = sprintf("INSERT INTO worker_role (id) ".
  674. "VALUES (%d)",
  675. $id
  676. );
  677. $db->Execute($sql);
  678. self::update($id, $fields);
  679. return $id;
  680. }
  681. static function update($ids, $fields) {
  682. parent::_update($ids, 'worker_role', $fields);
  683. }
  684. static function getACL($nocache=false) {
  685. $cache = DevblocksPlatform::getCacheService();
  686. if($nocache || null === ($acl = $cache->load(self::_CACHE_ALL))) {
  687. $db = DevblocksPlatform::getDatabaseService();
  688. // All roles
  689. $all_roles = self::getWhere();
  690. $all_worker_ids = array();
  691. // All privileges by role
  692. $all_privs = array();
  693. $rs = $db->Execute("SELECT role_id, priv_id FROM worker_role_acl WHERE has_priv = 1 ORDER BY role_id, priv_id");
  694. while(!$rs->EOF) {
  695. $role_id = intval($rs->fields['role_id']);
  696. $priv_id = $rs->fields['priv_id'];
  697. if(!isset($all_privs[$role_id]))
  698. $all_privs[$role_id] = array();
  699. $all_privs[$role_id][$priv_id] = $priv_id;
  700. $rs->MoveNext();
  701. }
  702. // All workers by role
  703. $all_rosters = array();
  704. $rs = $db->Execute("SELECT role_id, worker_id FROM worker_to_role");
  705. while(!$rs->EOF) {
  706. $role_id = intval($rs->fields['role_id']);
  707. $worker_id = intval($rs->fields['worker_id']);
  708. if(!isset($all_rosters[$role_id]))
  709. $all_rosters[$role_id] = array();
  710. $all_rosters[$role_id][$worker_id] = $worker_id;
  711. $all_worker_ids[$worker_id] = $worker_id;
  712. $rs->MoveNext();
  713. }
  714. // Aggregate privs by workers' roles (if set anywhere, keep)
  715. $privs_by_worker = array();
  716. if(is_array($all_worker_ids))
  717. foreach($all_worker_ids as $worker_id) {
  718. if(!isset($privs_by_worker[$worker_id]))
  719. $privs_by_worker[$worker_id] = array();
  720. foreach($all_rosters as $role_id => $role_roster) {
  721. if(isset($role_roster[$worker_id]) && isset($all_privs[$role_id])) {
  722. // If we have privs from other groups, merge on the keys
  723. $current_privs = (is_array($privs_by_worker[$worker_id])) ? $privs_by_worker[$worker_id] : array();
  724. $privs_by_worker[$worker_id] = array_merge($current_privs,$all_privs[$role_id]);
  725. }
  726. }
  727. }
  728. $acl = array(
  729. self::CACHE_KEY_ROLES => $all_roles,
  730. self::CACHE_KEY_PRIVS_BY_ROLE => $all_privs,
  731. self::CACHE_KEY_WORKERS_BY_ROLE => $all_rosters,
  732. self::CACHE_KEY_PRIVS_BY_WORKER => $privs_by_worker,
  733. );
  734. $cache->save($acl, self::_CACHE_ALL);
  735. }
  736. return $acl;
  737. }
  738. /**
  739. * @param string $where
  740. * @return Model_WorkerRole[]
  741. */
  742. static function getWhere($where=null) {
  743. $db = DevblocksPlatform::getDatabaseService();
  744. $sql = "SELECT id, name ".
  745. "FROM worker_role ".
  746. (!empty($where) ? sprintf("WHERE %s ",$where) : "").
  747. "ORDER BY name asc";
  748. $rs = $db->Execute($sql);
  749. return self::_getObjectsFromResult($rs);
  750. }
  751. /**
  752. * @param integer $id
  753. * @return Model_WorkerRole */
  754. static function get($id) {
  755. $objects = self::getWhere(sprintf("%s = %d",
  756. self::ID,
  757. $id
  758. ));
  759. if(isset($objects[$id]))
  760. return $objects[$id];
  761. return null;
  762. }
  763. /**
  764. * @param ADORecordSet $rs
  765. * @return Model_WorkerRole[]
  766. */
  767. static private function _getObjectsFromResult($rs) {
  768. $objects = array();
  769. while(!$rs->EOF) {
  770. $object = new Model_WorkerRole();
  771. $object->id = $rs->fields['id'];
  772. $object->name = $rs->fields['name'];
  773. $objects[$object->id] = $object;
  774. $rs->MoveNext();
  775. }
  776. return $objects;
  777. }
  778. static function delete($ids) {
  779. if(!is_array($ids)) $ids = array($ids);
  780. $db = DevblocksPlatform::getDatabaseService();
  781. if(empty($ids))
  782. return;
  783. $ids_list = implode(',', $ids);
  784. $db->Execute(sprintf("DELETE FROM worker_role WHERE id IN (%s)", $ids_list));
  785. $db->Execute(sprintf("DELETE FROM worker_to_role WHERE role_id IN (%s)", $ids_list));
  786. $db->Execute(sprintf("DELETE FROM worker_role_acl WHERE role_id IN (%s)", $ids_list));
  787. return true;
  788. }
  789. static function getRolePrivileges($role_id) {
  790. $acl = self::getACL();
  791. if(empty($role_id) || !isset($acl[self::CACHE_KEY_PRIVS_BY_ROLE][$role_id]))
  792. return array();
  793. return $acl[self::CACHE_KEY_PRIVS_BY_ROLE][$role_id];
  794. }
  795. /**
  796. * @param integer $role_id
  797. * @param array $privileges
  798. * @param boolean $replace
  799. */
  800. static function setRolePrivileges($role_id, $privileges) {
  801. if(!is_array($privileges)) $privileges = array($privileges);
  802. $db = DevblocksPlatform::getDatabaseService();
  803. if(empty($role_id))
  804. return;
  805. // Wipe all privileges on blank replace
  806. $sql = sprintf("DELETE FROM worker_role_acl WHERE role_id = %d", $role_id);
  807. $db->Execute($sql);
  808. // Load entire ACL list
  809. $acl = DevblocksPlatform::getAclRegistry();
  810. // Set ACLs according to the new master list
  811. if(!empty($privileges) && !empty($acl)) {
  812. foreach($privileges as $priv) { /* @var $priv DevblocksAclPrivilege */
  813. $sql = sprintf("INSERT INTO worker_role_acl (role_id, priv_id, has_priv) ".
  814. "VALUES (%d, %s, %d)",
  815. $role_id,
  816. $db->qstr($priv),
  817. 1
  818. );
  819. $db->Execute($sql);
  820. }
  821. }
  822. unset($privileges);
  823. self::clearCache();
  824. }
  825. static function getRoleWorkers($role_id) {
  826. $acl = self::getACL();
  827. if(empty($role_id) || !isset($acl[self::CACHE_KEY_WORKERS_BY_ROLE][$role_id]))
  828. return array();
  829. return $acl[self::CACHE_KEY_WORKERS_BY_ROLE][$role_id];
  830. }
  831. static function setRoleWorkers($role_id, $worker_ids) {
  832. if(!is_array($worker_ids)) $worker_ids = array($worker_ids);
  833. $db = DevblocksPlatform::getDatabaseService();
  834. if(empty($role_id))
  835. return;
  836. // Wipe roster
  837. $sql = sprintf("DELETE FROM worker_to_role WHERE role_id = %d", $role_id);
  838. $db->Execute($sql);
  839. // Add desired workers to role's roster
  840. if(is_array($worker_ids))
  841. foreach($worker_ids as $worker_id) {
  842. $sql = sprintf("INSERT INTO worker_to_role (worker_id, role_id) ".
  843. "VALUES (%d, %d)",
  844. $worker_id,
  845. $role_id
  846. );
  847. $db->Execute($sql);
  848. }
  849. self::clearCache();
  850. }
  851. static function clearCache() {
  852. $cache = DevblocksPlatform::getCacheService();
  853. $cache->remove(self::_CACHE_ALL);
  854. }
  855. };
  856. class DAO_WorkerEvent extends DevblocksORMHelper {
  857. const CACHE_COUNT_PREFIX = 'workerevent_count_';
  858. const ID = 'id';
  859. const CREATED_DATE = 'created_date';
  860. const WORKER_ID = 'worker_id';
  861. const TITLE = 'title';
  862. const CONTENT = 'content';
  863. const IS_READ = 'is_read';
  864. const URL = 'url';
  865. static function create($fields) {
  866. $db = DevblocksPlatform::getDatabaseService();
  867. $id = $db->GenID('worker_event_seq');
  868. $sql = sprintf("INSERT INTO worker_event (id) ".
  869. "VALUES (%d)",
  870. $id
  871. );
  872. $db->Execute($sql);
  873. self::update($id, $fields);
  874. // Invalidate the worker notification count cache
  875. if(isset($fields[self::WORKER_ID])) {
  876. $cache = DevblocksPlatform::getCacheService();
  877. self::clearCountCache($fields[self::WORKER_ID]);
  878. }
  879. return $id;
  880. }
  881. static function update($ids, $fields) {
  882. parent::_update($ids, 'worker_event', $fields);
  883. }
  884. static function updateWhere($fields, $where) {
  885. parent::_updateWhere('worker_event', $fields, $where);
  886. }
  887. /**
  888. * @param string $where
  889. * @return Model_WorkerEvent[]
  890. */
  891. static function getWhere($where=null) {
  892. $db = DevblocksPlatform::getDatabaseService();
  893. $sql = "SELECT id, created_date, worker_id, title, content, is_read, url ".
  894. "FROM worker_event ".
  895. (!empty($where) ? sprintf("WHERE %s ",$where) : "").
  896. "ORDER BY id asc";
  897. $rs = $db->Execute($sql);
  898. return self::_getObjectsFromResult($rs);
  899. }
  900. /**
  901. * @param integer $id
  902. * @return Model_WorkerEvent */
  903. static function get($id) {
  904. $objects = self::getWhere(sprintf("%s = %d",
  905. self::ID,
  906. $id
  907. ));
  908. if(isset($objects[$id]))
  909. return $objects[$id];
  910. return null;
  911. }
  912. static function getUnreadCountByWorker($worker_id) {
  913. $db = DevblocksPlatform::getDatabaseService();
  914. $cache = DevblocksPlatform::getCacheService();
  915. if(null === ($count = $cache->load(self::CACHE_COUNT_PREFIX.$worker_id))) {
  916. $sql = sprintf("SELECT count(*) ".
  917. "FROM worker_event ".
  918. "WHERE worker_id = %d ".
  919. "AND is_read = 0",
  920. $worker_id
  921. );
  922. $count = $db->GetOne($sql);
  923. $cache->save($count, self::CACHE_COUNT_PREFIX.$worker_id);
  924. }
  925. return intval($count);
  926. }
  927. /**
  928. * @param ADORecordSet $rs
  929. * @return Model_WorkerEvent[]
  930. */
  931. static private function _getObjectsFromResult($rs) {
  932. $objects = array();
  933. while(!$rs->EOF) {
  934. $object = new Model_WorkerEvent();
  935. $object->id = $rs->fields['id'];
  936. $object->created_date = $rs->fields['created_date'];
  937. $object->worker_id = $rs->fields['worker_id'];
  938. $object->title = $rs->fields['title'];
  939. $object->url = $rs->fields['url'];
  940. $object->content = $rs->fields['content'];
  941. $object->is_read = $rs->fields['is_read'];
  942. $objects[$object->id] = $object;
  943. $rs->MoveNext();
  944. }
  945. return $objects;
  946. }
  947. static function delete($ids) {
  948. if(!is_array($ids)) $ids = array($ids);
  949. $db = DevblocksPlatform::getDatabaseService();
  950. if(empty($ids))
  951. return;
  952. $ids_list = implode(',', $ids);
  953. $db->Execute(sprintf("DELETE FROM worker_event WHERE id IN (%s)", $ids_list));
  954. return true;
  955. }
  956. static function clearCountCache($worker_id) {
  957. $cache = DevblocksPlatform::getCacheService();
  958. $cache->remove(self::CACHE_COUNT_PREFIX.$worker_id);
  959. }
  960. /**
  961. * Enter description here...
  962. *
  963. * @param DevblocksSearchCriteria[] $params
  964. * @param integer $limit
  965. * @param integer $page
  966. * @param string $sortBy
  967. * @param boolean $sortAsc
  968. * @param boolean $withCounts
  969. * @return array
  970. */
  971. static function search($params, $limit=10, $page=0, $sortBy=null, $sortAsc=null, $withCounts=true) {
  972. $db = DevblocksPlatform::getDatabaseService();
  973. $fields = SearchFields_WorkerEvent::getFields();
  974. // Sanitize
  975. if(!isset($fields[$sortBy]))
  976. $sortBy=null;
  977. list($tables,$wheres) = parent::_parseSearchParams($params, array(),$fields,$sortBy);
  978. $start = ($page * $limit); // [JAS]: 1-based [TODO] clean up + document
  979. $total = -1;
  980. $sql = sprintf("SELECT ".
  981. "we.id as %s, ".
  982. "we.created_date as %s, ".
  983. "we.worker_id as %s, ".
  984. "we.title as %s, ".
  985. "we.content as %s, ".
  986. "we.is_read as %s, ".
  987. "we.url as %s ".
  988. "FROM worker_event we ",
  989. // "INNER JOIN team tm ON (tm.id = t.team_id) ".
  990. SearchFields_WorkerEvent::ID,
  991. SearchFields_WorkerEvent::CREATED_DATE,
  992. SearchFields_WorkerEvent::WORKER_ID,
  993. SearchFields_WorkerEvent::TITLE,
  994. SearchFields_WorkerEvent::CONTENT,
  995. SearchFields_WorkerEvent::IS_READ,
  996. SearchFields_WorkerEvent::URL
  997. ).
  998. // [JAS]: Dynamic table joins
  999. // (isset($tables['ra']) ? "INNER JOIN requester r ON (r.ticket_id=t.id)" : " ").
  1000. (!empty($wheres) ? sprintf("WHERE %s ",implode(' AND ',$wheres)) : "").
  1001. (!empty($sortBy) ? sprintf("ORDER BY %s %s",$sortBy,($sortAsc || is_null($sortAsc))?"ASC":"DESC") : "")
  1002. ;
  1003. // [TODO] Could push the select logic down a level too
  1004. if($limit > 0) {
  1005. $rs = $db->SelectLimit($sql,$limit,$start) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  1006. } else {
  1007. $rs = $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  1008. $total = $rs->RecordCount();
  1009. }
  1010. $results = array();
  1011. if(is_a($rs,'ADORecordSet'))
  1012. while(!$rs->EOF) {
  1013. $result = array();
  1014. foreach($rs->fields as $f => $v) {
  1015. $result[$f] = $v;
  1016. }
  1017. $ticket_id = intval($rs->fields[SearchFields_WorkerEvent::ID]);
  1018. $results[$ticket_id] = $result;
  1019. $rs->MoveNext();
  1020. }
  1021. // [JAS]: Count all
  1022. if($withCounts) {
  1023. $rs = $db->Execute($sql);
  1024. $total = $rs->RecordCount();
  1025. }
  1026. return array($results,$total);
  1027. }
  1028. };
  1029. class SearchFields_WorkerEvent implements IDevblocksSearchFields {
  1030. // Worker Event
  1031. const ID = 'we_id';
  1032. const CREATED_DATE = 'we_created_date';
  1033. const WORKER_ID = 'we_worker_id';
  1034. const TITLE = 'we_title';
  1035. const CONTENT = 'we_content';
  1036. const IS_READ = 'we_is_read';
  1037. const URL = 'we_url';
  1038. /**
  1039. * @return DevblocksSearchField[]
  1040. */
  1041. static function getFields() {
  1042. $translate = DevblocksPlatform::getTranslationService();
  1043. $columns = array(
  1044. self::ID => new DevblocksSearchField(self::ID, 'we', 'id', null, $translate->_('worker_event.id')),
  1045. self::CREATED_DATE => new DevblocksSearchField(self::CREATED_DATE, 'we', 'created_date', null, $translate->_('worker_event.created_date')),
  1046. self::WORKER_ID => new DevblocksSearchField(self::WORKER_ID, 'we', 'worker_id', null, $translate->_('worker_event.worker_id')),
  1047. self::TITLE => new DevblocksSearchField(self::TITLE, 'we', 'title', null, $translate->_('worker_event.title')),
  1048. self::CONTENT => new DevblocksSearchField(self::CONTENT, 'we', 'content', null, $translate->_('worker_event.content')),
  1049. self::IS_READ => new DevblocksSearchField(self::IS_READ, 'we', 'is_read', null, $translate->_('worker_event.is_read')),
  1050. self::URL => new DevblocksSearchField(self::URL, 'we', 'url', null, $translate->_('common.url')),
  1051. );
  1052. // Sort by label (translation-conscious)
  1053. uasort($columns, create_function('$a, $b', "return strcasecmp(\$a->db_label,\$b->db_label);\n"));
  1054. return $columns;
  1055. }
  1056. };
  1057. class DAO_ContactOrg extends C4_ORMHelper {
  1058. const ID = 'id';
  1059. const NAME = 'name';
  1060. const STREET = 'street';
  1061. const CITY = 'city';
  1062. const PROVINCE = 'province';
  1063. const POSTAL = 'postal';
  1064. const COUNTRY = 'country';
  1065. const PHONE = 'phone';
  1066. const WEBSITE = 'website';
  1067. const CREATED = 'created';
  1068. private function __construct() {}
  1069. public static function getFields() {
  1070. $translate = DevblocksPlatform::getTranslationService();
  1071. return array(
  1072. 'id' => $translate->_('contact_org.id'),
  1073. 'name' => $translate->_('contact_org.name'),
  1074. 'street' => $translate->_('contact_org.street'),
  1075. 'city' => $translate->_('contact_org.city'),
  1076. 'province' => $translate->_('contact_org.province'),
  1077. 'postal' => $translate->_('contact_org.postal'),
  1078. 'country' => $translate->_('contact_org.country'),
  1079. 'phone' => $translate->_('contact_org.phone'),
  1080. 'website' => $translate->_('contact_org.website'),
  1081. 'created' => $translate->_('contact_org.created'),
  1082. );
  1083. }
  1084. /**
  1085. * Enter description here...
  1086. *
  1087. * @param array $fields
  1088. * @return integer
  1089. */
  1090. static function create($fields) {
  1091. $db = DevblocksPlatform::getDatabaseService();
  1092. $id = $db->GenID('contact_org_seq');
  1093. $sql = sprintf("INSERT INTO contact_org (id,name,street,city,province,postal,country,phone,website,created) ".
  1094. "VALUES (%d,'','','','','','','','',%d)",
  1095. $id,
  1096. time()
  1097. );
  1098. $rs = $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  1099. self::update($id, $fields);
  1100. return $id;
  1101. }
  1102. /**
  1103. * Enter description here...
  1104. *
  1105. * @param array $ids
  1106. * @param array $fields
  1107. * @return Model_ContactOrg
  1108. */
  1109. static function update($ids, $fields) {
  1110. if(!is_array($ids)) $ids = array($ids);
  1111. parent::_update($ids, 'contact_org', $fields);
  1112. }
  1113. /**
  1114. * @param array $ids
  1115. */
  1116. static function delete($ids) {
  1117. if(!is_array($ids)) $ids = array($ids);
  1118. $db = DevblocksPlatform::getDatabaseService();
  1119. if(empty($ids))
  1120. return;
  1121. $id_list = implode(',', $ids);
  1122. // Orgs
  1123. $sql = sprintf("DELETE QUICK FROM contact_org WHERE id IN (%s)",
  1124. $id_list
  1125. );
  1126. $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  1127. // Clear any associated addresses
  1128. $sql = sprintf("UPDATE address SET contact_org_id = 0 WHERE contact_org_id IN (%s)",
  1129. $id_list
  1130. );
  1131. $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  1132. // Tasks
  1133. DAO_Task::deleteBySourceIds('cerberusweb.tasks.org', $ids);
  1134. // Custom fields
  1135. DAO_CustomFieldValue::deleteBySourceIds(ChCustomFieldSource_Org::ID, $ids);
  1136. // Notes
  1137. DAO_Note::deleteBySourceIds(ChNotesSource_Org::ID, $ids);
  1138. }
  1139. /**
  1140. * @param string $where
  1141. * @return Model_ContactOrg[]
  1142. */
  1143. static function getWhere($where=null) {
  1144. $db = DevblocksPlatform::getDatabaseService();
  1145. $sql = "SELECT id,name,street,city,province,postal,country,phone,website,created ".
  1146. "FROM contact_org ".
  1147. (!empty($where) ? sprintf("WHERE %s ", $where) : " ")
  1148. ;
  1149. $rs = $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  1150. return self::_getObjectsFromResultSet($rs);
  1151. }
  1152. static private function _getObjectsFromResultSet($rs) {
  1153. $objects = array();
  1154. if(is_a($rs,'ADORecordSet'))
  1155. while(!$rs->EOF) {
  1156. $object = new Model_ContactOrg();
  1157. $object->id = intval($rs->fields['id']);
  1158. $object->name = $rs->fields['name'];
  1159. $object->street = $rs->fields['street'];
  1160. $object->city = $rs->fields['city'];
  1161. $object->province = $rs->fields['province'];
  1162. $object->postal = $rs->fields['postal'];
  1163. $object->country = $rs->fields['country'];
  1164. $object->phone = $rs->fields['phone'];
  1165. $object->website = $rs->fields['website'];
  1166. $object->created = intval($rs->fields['created']);
  1167. $objects[$object->id] = $object;
  1168. $rs->MoveNext();
  1169. }
  1170. return $objects;
  1171. }
  1172. /**
  1173. * @param integer $id
  1174. * @return Model_ContactOrg
  1175. */
  1176. static function get($id) {
  1177. $where = sprintf("%s = %d",
  1178. self::ID,
  1179. $id
  1180. );
  1181. $objects = self::getWhere($where);
  1182. if(isset($objects[$id]))
  1183. return $objects[$id];
  1184. return null;
  1185. }
  1186. /**
  1187. * Enter description here...
  1188. *
  1189. * @param string $name
  1190. * @param boolean $create_if_null
  1191. * @return Model_ContactOrg
  1192. */
  1193. static function lookup($name, $create_if_null=false) {
  1194. $db = DevblocksPlatform::getDatabaseService();
  1195. @$orgs = self::getWhere(
  1196. sprintf('%s = %s', self::NAME, $db->qstr($name))
  1197. );
  1198. if(empty($orgs)) {
  1199. if($create_if_null) {
  1200. $fields = array(
  1201. self::NAME => $name
  1202. );
  1203. return self::create($fields);
  1204. }
  1205. } else {
  1206. return key($orgs);
  1207. }
  1208. return NULL;
  1209. }
  1210. /**
  1211. * Enter description here...
  1212. *
  1213. * @param DevblocksSearchCriteria[] $params
  1214. * @param integer $limit
  1215. * @param integer $page
  1216. * @param string $sortBy
  1217. * @param boolean $sortAsc
  1218. * @param boolean $withCounts
  1219. * @return array
  1220. */
  1221. static function search($columns, $params, $limit=10, $page=0, $sortBy=null, $sortAsc=null, $withCounts=true) {
  1222. $db = DevblocksPlatform::getDatabaseService();
  1223. $fields = SearchFields_ContactOrg::getFields();
  1224. // Sanitize
  1225. if(!isset($fields[$sortBy]))
  1226. $sortBy=null;
  1227. list($tables,$wheres) = parent::_parseSearchParams($params, $columns, $fields,$sortBy);
  1228. $start = ($page * $limit); // [JAS]: 1-based [TODO] clean up + document
  1229. $total = -1;
  1230. $select_sql = sprintf("SELECT ".
  1231. "c.id as %s, ".
  1232. "c.name as %s, ".
  1233. "c.street as %s, ".
  1234. "c.city as %s, ".
  1235. "c.province as %s, ".
  1236. "c.postal as %s, ".
  1237. "c.country as %s, ".
  1238. "c.phone as %s, ".
  1239. "c.website as %s, ".
  1240. "c.created as %s ",
  1241. // "INNER JOIN team tm ON (tm.id = t.team_id) ".
  1242. SearchFields_ContactOrg::ID,
  1243. SearchFields_ContactOrg::NAME,
  1244. SearchFields_ContactOrg::STREET,
  1245. SearchFields_ContactOrg::CITY,
  1246. SearchFields_ContactOrg::PROVINCE,
  1247. SearchFields_ContactOrg::POSTAL,
  1248. SearchFields_ContactOrg::COUNTRY,
  1249. SearchFields_ContactOrg::PHONE,
  1250. SearchFields_ContactOrg::WEBSITE,
  1251. SearchFields_ContactOrg::CREATED
  1252. );
  1253. $join_sql = 'FROM contact_org c ';
  1254. // Custom field joins
  1255. list($select_sql, $join_sql, $has_multiple_values) = self::_appendSelectJoinSqlForCustomFieldTables(
  1256. $tables,
  1257. $params,
  1258. 'c.id',
  1259. $select_sql,
  1260. $join_sql
  1261. );
  1262. $where_sql = "".
  1263. (!empty($wheres) ? sprintf("WHERE %s ",implode(' AND ',$wheres)) : "");
  1264. $sort_sql = (!empty($sortBy)) ? sprintf("ORDER BY %s %s ",$sortBy,($sortAsc || is_null($sortAsc))?"ASC":"DESC") : " ";
  1265. $sql =
  1266. $select_sql.
  1267. $join_sql.
  1268. $where_sql.
  1269. ($has_multiple_values ? 'GROUP BY c.id ' : '').
  1270. $sort_sql;
  1271. // [TODO] Could push the select logic down a level too
  1272. if($limit > 0) {
  1273. $rs = $db->SelectLimit($sql,$limit,$start) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  1274. } else {
  1275. $rs = $db->Execute($sql) or die(__CLASS__ . '('.__LINE__.')'. ':' . $db->ErrorMsg()); /* @var $rs ADORecordSet */
  1276. $total = $rs->RecordCount();
  1277. }
  1278. $results = array();
  1279. if(is_a($rs,'ADORecordSet'))
  1280. while(!$rs->EOF) {
  1281. $result = array();
  1282. foreach($rs->fields as $f => $v) {
  1283. $result[$f] = $v;
  1284. }
  1285. $ticket_id = intval($rs->fields[SearchFields_ContactOrg::ID]);
  1286. $results[$ticket_id] = $result;
  1287. $rs->MoveNext();
  1288. }
  1289. // [JAS]: Count all
  1290. if($withCounts) {
  1291. $count_sql =
  1292. ($has_multiple_values ? "SELECT COUNT(DISTINCT c.id) " : "SELECT COUNT(c.id) ").
  1293. $join_sql.
  1294. $where_sql;
  1295. $total = $db->GetOne($count_sql);
  1296. }
  1297. return array($results,$total);
  1298. }
  1299. };
  1300. class SearchFields_ContactOrg {
  1301. const ID = 'c_id';
  1302. const NAME = 'c_name';
  1303. const STREET = 'c_street';
  1304. const CITY = 'c_city';
  1305. const PROVINCE = 'c_province';
  1306. const POSTAL = 'c_postal';
  1307. const COUNTRY = 'c_country';
  1308. const PHONE = 'c_phone';
  1309. const WEBSITE = 'c_website';
  1310. const CREATED = 'c_created';
  1311. /**
  1312. * @return DevblocksSearchField[]
  1313. */
  1314. static function getFields() {
  1315. $translate = DevblocksPlatform::getTranslationService();
  1316. $columns = array(
  1317. self::ID => new DevblocksSearchField(self::ID, 'c', 'id', null, $translate->_('contact_org.id')),
  1318. self::NAME => new DevblocksSearchField(self::NAME, 'c', 'name', null, $translate->_('contact_org.name')),
  1319. self::STREET => new DevblocksSearchField(self::STREET, 'c', 'street', null, $translate->_('contact_org.street')),
  1320. self::CITY => new DevblocksSearchField(self::CITY, 'c', 'city', null, $translate->_('contact_org.city')),
  1321. self::PROVINCE => new DevblocksSearchField(self::PROVINCE, 'c', 'province', null, $translate->_('contact_org.province')),
  1322. self::POSTAL => new DevblocksSearchField(self::POSTAL, 'c', 'postal', null, $translate->_('contact_org.postal')),
  1323. self::COUNTRY => new DevblocksSearchField(self::COUNTRY, 'c', 'country', null, $translate->_('contact_org.country')),
  1324. self::PHONE => new DevblocksSearchField(self::PHONE, 'c', 'phone', null, $translate->_('contact_org.phone')),
  1325. self::WEBSITE => new DevblocksSearchField(self::WEBSITE, 'c', 'website', null, $translate->_('contact_org.website')),
  1326. self::CREATED => new DevblocksSearchField(self::CREATED, 'c', 'created', null, $translate->_('contact_org.created')),
  1327. );
  1328. // Custom Fields
  1329. $fields = DAO_CustomField::getBySource(ChCustomFieldSource_Org::ID);
  1330. if(is_array($fields))
  1331. foreach($fields as $field_id => $field) {
  1332. $key = 'cf_'.$field_id;
  1333. $columns[$key] = new DevblocksSearchField($key,$key,'field_value',null,$field->name);
  1334. }
  1335. // Sort by label (translation-conscious)
  1336. uasort($columns, create_function('$a, $b', "return strcasecmp(\$a->db_label,\$b->db_label);\n"));
  1337. return $columns;
  1338. }
  1339. };
  1340. /**
  1341. * Address DAO
  1342. *
  1343. */
  1344. class DAO_Address extends C4_ORMHelper {
  1345. const ID = 'id';
  1346. const EMAIL = 'email';
  1347. const FIRST_NAME = 'first_name';
  1348. const LAST_NAM

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