PageRenderTime 134ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 3ms

/api/DAO.class.php

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

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