api.adcomments.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. <?php
  2. /**
  3. * Universal additional comments class which allows attach comments for any items on some scope
  4. */
  5. class ADcomments {
  6. /**
  7. * Current scope and item comments data as id=>commentData
  8. *
  9. * @var array
  10. */
  11. protected $allCommentsData = array();
  12. /**
  13. * Current instance scope
  14. *
  15. * @var string
  16. */
  17. protected $scope = '';
  18. /**
  19. * UbillingCache object placeholder
  20. *
  21. * @var object
  22. */
  23. protected $cache = '';
  24. /**
  25. * Comments caching time
  26. *
  27. * @var int
  28. */
  29. protected $cacheTime = 2592000; //month by default
  30. /**
  31. * Current instance item id
  32. *
  33. * @var string
  34. */
  35. protected $item = '';
  36. /**
  37. * Current instance administrator login
  38. *
  39. * @var string
  40. */
  41. protected $myLogin = '';
  42. /**
  43. * Current scope items counters as item=>commentsCount
  44. *
  45. * @var array
  46. */
  47. protected $scopeItems = array();
  48. /**
  49. * Comments data database abstraction layer
  50. *
  51. * @var object
  52. */
  53. protected $commentsDb = '';
  54. /**
  55. * Scope items loaded flag
  56. *
  57. * @var bool
  58. */
  59. protected $scopeItemsLoaded = false;
  60. /**
  61. * Default editing area size
  62. *
  63. * @var string
  64. */
  65. protected $textAreaSize = '60x10';
  66. /**
  67. * Some predefined stuff here
  68. */
  69. const TABLE_COMMENTS = 'adcomments';
  70. const PROUTE_NEW_TEXT = 'newadcommentstext';
  71. const PROUTE_EDIT_FORM = 'adcommentseditid';
  72. const PROUTE_EDIT_ID = 'adcommentsmodifyid';
  73. const PROUTE_EDIT_TEXT = 'adcommentsmodifytext';
  74. const PROUTE_DELETE = 'adcommentsdeleteid';
  75. const CACHE_KEY = 'ADCOMMENTS_';
  76. /**
  77. * when everything goes wrong
  78. */
  79. const EX_EMPTY_SCOPE = 'EMPTY_SCOPE_RECEIVED';
  80. const EX_EMPTY_ITEM = 'EMPTY_ITEMID_RECEIVED';
  81. const EX_EMPTY_QUERY_STRING = 'EMPTY_SERVER_QUERY_STRING_RECEIVED';
  82. /**
  83. * ADcomments class constructor
  84. *
  85. * @param string $scope Object scope for comments tree
  86. */
  87. public function __construct($scope) {
  88. if (!empty($scope)) {
  89. $this->setScope($scope);
  90. $this->setMyLogin();
  91. $this->initCache();
  92. $this->initDb();
  93. } else {
  94. throw new Exception(self::EX_EMPTY_SCOPE);
  95. }
  96. }
  97. /**
  98. * Current instance comments scope
  99. *
  100. * @param string $scope scope of items comments
  101. *
  102. * @return void
  103. */
  104. protected function setScope($scope) {
  105. $scope = trim($scope);
  106. $scope = ubRouting::filters($scope, 'mres');
  107. $this->scope = $scope;
  108. }
  109. /**
  110. * Sets current administrator login into private prop
  111. *
  112. * @return void
  113. */
  114. protected function setMyLogin() {
  115. $this->myLogin = whoami();
  116. }
  117. /**
  118. * Initalizes system cache object for further usage
  119. *
  120. * @return void
  121. */
  122. protected function initCache() {
  123. $this->cache = new UbillingCache();
  124. }
  125. /**
  126. * Inits database abstraction layer
  127. *
  128. * @return void
  129. */
  130. protected function initDb() {
  131. $this->commentsDb = new NyanORM(self::TABLE_COMMENTS);
  132. }
  133. /**
  134. * Clear scope cache object
  135. *
  136. * @return void
  137. */
  138. protected function clearScopeCache() {
  139. $this->cache->delete(self::CACHE_KEY . $this->scope);
  140. }
  141. /**
  142. * Sets current scope item commenting ID
  143. *
  144. * @param string $item target item ID
  145. *
  146. * @return void
  147. */
  148. protected function setItem($item) {
  149. $item = trim($item);
  150. $item = ubRouting::filters($item, 'mres');
  151. $this->item = $item;
  152. }
  153. /**
  154. * Loads selected scope and item comments into private data property
  155. *
  156. * @return void
  157. */
  158. protected function loadComments() {
  159. if (!empty($this->scope)) {
  160. if (!empty($this->item)) {
  161. $this->commentsDb->where('scope', '=', $this->scope);
  162. $this->commentsDb->where('item', '=', $this->item);
  163. $this->commentsDb->orderBy('date', 'ASC');
  164. $this->allCommentsData = $this->commentsDb->getAll('id');
  165. } else {
  166. throw new Exception(self::EX_EMPTY_ITEM);
  167. }
  168. } else {
  169. throw new Exception(self::EX_EMPTY_SCOPE);
  170. }
  171. }
  172. /**
  173. * Returns new comment creation form
  174. *
  175. * @return string
  176. */
  177. protected function commentAddForm() {
  178. $result = '';
  179. $inputs = wf_TextArea(self::PROUTE_NEW_TEXT, '', '', true, $this->textAreaSize);
  180. $inputs .= wf_Submit(__('Save'));
  181. $result .= wf_tag('div', false, '', 'style="float:left;"');
  182. $result .= wf_Form('', 'POST', $inputs, 'glamour');
  183. $result .= wf_tag('div', true);
  184. //princess fast reply form if its enabled?
  185. if ($this->scope == 'TASKMAN') {
  186. $adCommFr = new ADcommFR();
  187. $result .= $adCommFr->renderPrincessFastReplies();
  188. }
  189. $result .= wf_CleanDiv();
  190. return ($result);
  191. }
  192. /**
  193. * Creates some new comment in database
  194. *
  195. * @param string $text text of new comment
  196. *
  197. * @return void
  198. */
  199. protected function createComment($text) {
  200. $curdate = curdatetime();
  201. $text = strip_tags($text);
  202. $text = ubRouting::filters($text, 'mres');
  203. $this->commentsDb->data('scope', $this->scope);
  204. $this->commentsDb->data('item', $this->item);
  205. $this->commentsDb->data('date', $curdate);
  206. $this->commentsDb->data('admin', $this->myLogin);
  207. $this->commentsDb->data('text', $text);
  208. $this->commentsDb->create();
  209. log_register('ADCOMM CREATE SCOPE `' . $this->scope . '` ITEM [' . $this->item . ']');
  210. $this->clearScopeCache();
  211. }
  212. /**
  213. * Deletes comment from database
  214. *
  215. * @param int $id existing comment database ID
  216. *
  217. * @return void
  218. */
  219. protected function deleteComment($id) {
  220. $id = ubRouting::filters($id, 'int');
  221. $this->commentsDb->where('id', '=', $id);
  222. $this->commentsDb->delete();
  223. log_register('ADCOMM DELETE SCOPE `' . $this->scope . '` ITEM [' . $this->item . ']');
  224. $this->clearScopeCache();
  225. }
  226. /**
  227. * Edits some comment text in database
  228. *
  229. * @param int $id existing comment database ID
  230. * @param string $text new text for comment
  231. *
  232. * @return void
  233. */
  234. protected function modifyComment($id, $text) {
  235. $id = ubRouting::filters($id, 'int');
  236. $text = strip_tags($text);
  237. $text = ubRouting::filters($text, 'mres');
  238. $this->commentsDb->data('text', $text);
  239. $this->commentsDb->where('id', '=', $id);
  240. $this->commentsDb->save();
  241. log_register('ADCOMM CHANGE SCOPE `' . $this->scope . '` ITEM [' . $this->item . ']');
  242. $this->clearScopeCache();
  243. }
  244. /**
  245. * Controls post environment and do something object actions when its required
  246. *
  247. * @return void
  248. */
  249. protected function commentSaver() {
  250. //detecting return URL
  251. if (isset($_SERVER['QUERY_STRING'])) {
  252. $returnUrl = '?' . $_SERVER['QUERY_STRING'];
  253. } else {
  254. $returnUrl = '';
  255. show_error(__('Strange exeption') . ': ' . self::EX_EMPTY_QUERY_STRING);
  256. }
  257. ///new comment creation
  258. if (ubRouting::checkPost(self::PROUTE_NEW_TEXT)) {
  259. $this->createComment(ubRouting::post(self::PROUTE_NEW_TEXT));
  260. if ($returnUrl) {
  261. ubRouting::nav($returnUrl);
  262. }
  263. }
  264. //comment deletion
  265. if (ubRouting::checkPost(self::PROUTE_DELETE)) {
  266. $this->deleteComment(ubRouting::post(self::PROUTE_DELETE));
  267. if ($returnUrl) {
  268. ubRouting::nav($returnUrl);
  269. }
  270. }
  271. //comment editing
  272. if (ubRouting::checkPost(array(self::PROUTE_EDIT_ID, self::PROUTE_EDIT_TEXT))) {
  273. $this->modifyComment(ubRouting::post(self::PROUTE_EDIT_ID), ubRouting::post(self::PROUTE_EDIT_TEXT));
  274. if ($returnUrl) {
  275. ubRouting::nav($returnUrl);
  276. }
  277. }
  278. }
  279. /**
  280. * Returns JavaScript comfirmation box for deleting/editing inputs
  281. *
  282. * @param string $alertText
  283. *
  284. * @return string
  285. */
  286. protected function jsAlert($alertText) {
  287. $result = 'onClick="return confirm(\'' . $alertText . '\');"';
  288. return ($result);
  289. }
  290. /**
  291. * Returns coment controls for own comments or for the user with root rights
  292. *
  293. * @param int $commentid existing additional comment ID
  294. * @return string
  295. */
  296. protected function commentControls($commentid) {
  297. $result = '';
  298. if (isset($this->allCommentsData[$commentid])) {
  299. if (($this->allCommentsData[$commentid]['admin'] == $this->myLogin) or (cfr('ROOT'))) {
  300. $deleteInputs = wf_HiddenInput(self::PROUTE_DELETE, $commentid);
  301. $deleteInputs .= wf_tag('input', false, '', 'type="image" src="skins/icon_del.gif" title="' . __('Delete') . '" ' . $this->jsAlert(__('Removing this may lead to irreparable results')));
  302. $deleteForm = wf_Form('', 'POST', $deleteInputs, '');
  303. $editInputs = wf_HiddenInput(self::PROUTE_EDIT_FORM, $commentid);
  304. $editInputs .= wf_tag('input', false, '', 'type="image" src="skins/icon_edit.gif" title="' . __('Edit') . '" ' . $this->jsAlert(__('Are you serious')));
  305. $editForm = wf_Form('', 'POST', $editInputs, '');
  306. $result .= wf_tag('div', false, '', 'style="display:inline-block;"') . $deleteForm . wf_tag('div', true);
  307. $result .= wf_tag('div', false, '', 'style="display:inline-block;"') . $editForm . wf_tag('div', true);
  308. }
  309. }
  310. return ($result);
  311. }
  312. /**
  313. * Returns comment editing form
  314. *
  315. * @param int $commentid existing database comment ID
  316. *
  317. * @return string
  318. */
  319. protected function commentEditForm($commentid) {
  320. $result = '';
  321. if (isset($this->allCommentsData[$commentid])) {
  322. $inputs = wf_HiddenInput(self::PROUTE_EDIT_ID, $commentid);
  323. $inputs .= wf_TextArea(self::PROUTE_EDIT_TEXT, '', $this->allCommentsData[$commentid]['text'], true, $this->textAreaSize);
  324. $inputs .= wf_Submit(__('Save'));
  325. $result = wf_Form('', 'POST', $inputs, 'glamour');
  326. }
  327. return ($result);
  328. }
  329. /**
  330. * Returns list of available comments for some item
  331. *
  332. * @param string $item
  333. * @return string
  334. */
  335. public function renderComments($item) {
  336. $result = '';
  337. $rows = '';
  338. $this->setItem($item);
  339. $this->loadComments();
  340. $this->commentSaver();
  341. $employeeLogins = ts_GetAllEmployeeLoginsAssocCached();
  342. if (!empty($this->allCommentsData)) {
  343. foreach ($this->allCommentsData as $io => $each) {
  344. $authorRealname = (isset($employeeLogins[$each['admin']])) ? $employeeLogins[$each['admin']] : $each['admin'];
  345. $authorName = wf_tag('center') . wf_tag('b') . $authorRealname . wf_tag('b', true) . wf_tag('center', true);
  346. $authorAvatar = wf_tag('center') . @gravatar_ShowAdminAvatar($each['admin'], '64') . wf_tag('center', true);
  347. $commentController = wf_tag('center') . $this->commentControls($each['id']) . wf_tag('center', true);
  348. $authorPanel = $authorName . wf_tag('br') . $authorAvatar . wf_tag('br') . $commentController;
  349. $commentText = nl2br($each['text']);
  350. if (ubRouting::checkPost(self::PROUTE_EDIT_FORM)) {
  351. //is editing form required for this comment?
  352. if (ubRouting::post(self::PROUTE_EDIT_FORM) == $each['id']) {
  353. //overriding text with editing form
  354. $commentText = $this->commentEditForm($each['id']);
  355. }
  356. }
  357. $cells = wf_TableCell('', '20%');
  358. $cells .= wf_TableCell($each['date']);
  359. $rows .= wf_TableRow($cells, 'row2');
  360. $cells = wf_TableCell($authorPanel);
  361. $cells .= wf_TableCell($commentText);
  362. $rows .= wf_TableRow($cells, 'row3');
  363. }
  364. $result .= wf_TableBody($rows, '100%', '0', '');
  365. }
  366. $result .= $this->commentAddForm();
  367. return ($result);
  368. }
  369. /**
  370. * Loads current scope items from database or cache
  371. *
  372. * @return array
  373. */
  374. protected function getScopeItemsCached() {
  375. $cachedData = array();
  376. //getting from cache
  377. $cachedData = $this->cache->get(self::CACHE_KEY . $this->scope, $this->cacheTime);
  378. if (empty($cachedData)) {
  379. //cache must be updated
  380. $this->commentsDb->selectable(array('id', 'scope', 'item', 'text'));
  381. $this->commentsDb->where('scope', '=', $this->scope);
  382. $cachedData = $this->commentsDb->getAll();
  383. if (empty($cachedData)) {
  384. $cachedData = array();
  385. }
  386. $this->cache->set(self::CACHE_KEY . $this->scope, $cachedData, $this->cacheTime);
  387. }
  388. return ($cachedData);
  389. }
  390. /**
  391. * Loads scope items list with counters if its really required
  392. *
  393. * @rerturn void
  394. */
  395. protected function loadScopeItems() {
  396. if ($this->scope) {
  397. $cachedData = $this->getScopeItemsCached();
  398. if (!empty($cachedData)) {
  399. foreach ($cachedData as $io => $each) {
  400. if (isset($this->scopeItems[$each['item']])) {
  401. $this->scopeItems[$each['item']]++;
  402. } else {
  403. $this->scopeItems[$each['item']] = 1;
  404. }
  405. }
  406. }
  407. $this->scopeItemsLoaded = true;
  408. } else {
  409. throw new Exception(self::EX_EMPTY_SCOPE);
  410. }
  411. }
  412. /**
  413. * Checks have item some comments or not?
  414. *
  415. * @param string $item
  416. *
  417. * @return bool
  418. */
  419. public function haveComments($item) {
  420. if (!$this->scopeItemsLoaded) {
  421. $this->loadScopeItems();
  422. }
  423. if (isset($this->scopeItems[$item])) {
  424. $result = true;
  425. } else {
  426. $result = false;
  427. }
  428. return ($result);
  429. }
  430. /**
  431. * Checks have item some additional comments and return native indicator
  432. *
  433. * @param string $item
  434. *
  435. * @return int
  436. */
  437. public function getCommentsCount($item) {
  438. if ($this->haveComments($item)) {
  439. $result = $this->scopeItems[$item];
  440. } else {
  441. $result = 0;
  442. }
  443. return ($result);
  444. }
  445. /**
  446. * Checks have item some additional comments and return native indicator
  447. *
  448. * @param string $item
  449. * @param int $size
  450. *
  451. * @return string
  452. */
  453. public function getCommentsIndicator($item, $size = '') {
  454. if ($this->haveComments($item)) {
  455. $size = (!$size) ? 16 : $size;
  456. $counter = $this->getCommentsCount($item);
  457. $result = wf_img_sized('skins/adcomments.png', __('Additional comments') . ' (' . $counter . ')', $size, $size);
  458. } else {
  459. // . .
  460. // |\_|\
  461. // | a_a\ I'm Batman.
  462. // | | "]
  463. // ____| '-\___
  464. // /.----.___.-'\
  465. // // _ \
  466. // // .-. (~v~) /|
  467. // |'| /\: .-- / \
  468. // // |-/ \_/____/\/~|
  469. // |/ \ | []_|_|_] \ |
  470. // | \ | \ |___ _\ ]_}
  471. // | | '-' / '.' |
  472. // | | / /|: |
  473. // | | | / |: /\
  474. // | | / / | / \
  475. // | | | / / | \
  476. // \ | |/\/ |/|/\ \
  477. // \|\ |\| | | / /\/\__\
  478. // \ \| | / | |__
  479. // / | |____)
  480. // |_/
  481. $result = '';
  482. }
  483. return ($result);
  484. }
  485. /**
  486. * Returns all items comments data for a given scope, like:
  487. * $item => array( [0] => array($comment1),
  488. * [1] => array($comment2),
  489. * .......................
  490. * [N] => array($commentN)
  491. * )
  492. *
  493. * where $comment will be represented as an associative array
  494. * with following keys: id,scope,item,text
  495. *
  496. * @return array
  497. *
  498. * @throws Exception
  499. */
  500. public function getScopeItemsCommentsAll() {
  501. if ($this->scope) {
  502. $itemsComments = array();
  503. $cachedData = $this->getScopeItemsCached();
  504. if (!empty($cachedData)) {
  505. foreach ($cachedData as $io => $each) {
  506. $itemsComments[$each['item']][] = $each;
  507. }
  508. }
  509. return ($itemsComments);
  510. } else {
  511. throw new Exception(self::EX_EMPTY_SCOPE);
  512. }
  513. }
  514. }