1<?php 2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project 3// 4// All Rights Reserved. See copyright.txt for details and a complete list of authors. 5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. 6// $Id$ 7 8/** 9 * Class Services_Annotator_Controller 10 * 11 * Linking annotatorjs to inline comments 12 */ 13 14const RANGE_ATTRIBUTE = 'tiki.comment.ranges'; 15 16class Services_Comment_AnnotationController 17{ 18 /** @var Comments */ 19 private $commentslib; 20 /** @var Services_Comment_Controller */ 21 private $commentController; 22 23 24 /** 25 * @throws Exception 26 * @throws Services_Exception_Disabled 27 */ 28 function setUp() 29 { 30 Services_Exception_Disabled::check('comments_inline_annotator'); 31 32 $this->commentslib = TikiLib::lib('comments'); 33 $this->commentController = new Services_Comment_Controller(); 34 } 35 36 /** 37 * Create an inline comment 38 * 39 * @param jitFilter $input 40 * string json encoded comment info from annotatorjs 41 * containing: 42 * string text comment text 43 * string quote quoted text on page 44 * array ranges range info for quoted text 45 * string uri actually object-type:object-id identifier for the tiki object to be commented 46 * 47 * @return array unused probably 48 * 49 * @throws Exception 50 * @throws Services_Exception_Denied 51 */ 52 53 function action_create($input) 54 { 55 global $user; 56 57 // annotatejs sends the params in the request payload by default, so we use option emulateJSON 58 // but then need to decode the json string here 59 $params = new jitFilter(json_decode($input->json->none(), true)); 60 61 $text = $params->text->wikicontent(); 62 $quote = $params->quote->text(); 63 $ranges = $params->asArray('ranges'); 64 $identifier = urldecode($params->uri->url()); // not really the uri but object-type:object-id identifier 65 list($objectType, $objectId) = explode(':', $identifier, 2); 66 67 if (! $this->commentController->canPost($objectType, $objectId)) { 68 throw new Services_Exception_Denied; 69 } 70 71 $comment = $this->formatComment($quote, $text); 72 $title = $this->createTitle($text); 73 $messageId = ''; 74 75 // create the comment 76 $threadId = $this->commentslib->post_new_comment( 77 $identifier, 78 0, 79 $user, 80 $title, 81 $comment, 82 $messageId 83 ); 84 85 TikiLib::lib('attribute')->set_attribute( 86 'comment', 87 $threadId, 88 RANGE_ATTRIBUTE, 89 json_encode($ranges) 90 ); 91 92 return [ 93 'id' => $threadId, 94 ]; 95 } 96 97 /** 98 * Update an inline comment 99 * 100 * @param jitFilter $input 101 * string json encoded comment info from annotatorjs 102 * containing: 103 * int id comment threadId 104 * string text comment text 105 * string quote quoted text on page 106 * array ranges range info for quoted text 107 * string uri actually object-type:object-id identifier for the tiki object to be commented 108 * string created datetime updated (?) 109 * string updated datetime updated 110 * 111 * @return array unused probably 112 * 113 * @throws Exception 114 * @throws Services_Exception_Denied 115 * @throws Services_Exception_NotFound 116 */ 117 118 function action_update($input) 119 { 120 $threadId = $input->threadId->int(); 121 $params = new jitFilter(json_decode($input->json->none(), true)); 122 123 $ranges = $params->asArray('ranges'); 124 $text = $params->text->wikicontent(); 125 $quote = $params->quote->text(); 126 127 $input->offsetSet('data', $this->formatComment($quote, $text)); 128 $input->offsetSet('title', $this->createTitle($text)); 129 $input->offsetSet('edit', 1); 130 131 $ret = $this->commentController->action_edit($input); 132 133 if ($ret) { 134 TikiLib::lib('attribute')->set_attribute( 135 'comment', 136 $threadId, 137 RANGE_ATTRIBUTE, 138 json_encode($ranges) 139 ); 140 } 141 142 return $ret; 143 } 144 145 /** 146 * Remove an inline comment - warning, no confirmation yet 147 * 148 * @param jitFilter $input 149 * int threadId comment id to delete 150 * 151 * @return array 152 * @throws Services_Exception 153 */ 154 155 function action_destroy($input) 156 { 157 $input->offsetSet('confirm', 1); // TODO but not sure how? 158 159 $ret = $this->commentController->action_remove($input); 160 161 $ret['id'] = $ret['threadId']; 162 return $ret; 163 } 164 165 /** 166 * List inline comments for a tiki object 167 * 168 * @param jitFilter $input 169 * int limit page size TODO 170 * int offset page start 171 * string uri object-type:object-id identifier for the tiki object to search 172 * 173 * @return array [total, rows] 174 * @throws Exception 175 * @throws Services_Exception 176 */ 177 178 function action_search($input) 179 { 180 $tikilib = TikiLib::lib('tiki'); 181 182 $limit = $input->limit->int(); // unused so far 183 $offset = $input->offset->int(); // TODO pagination 184 185 $identifier = urldecode($input->uri->url()); 186 $object = explode(':', $identifier); 187 188 $list = $this->commentController->action_list(new jitFilter([ 189 'type' => $object[0], 190 'objectId' => $object[1], 191 ])); 192 193 $comments = []; 194 195 foreach ($list['comments'] as $comment) { 196 if (strpos($comment['data'], ';note:') === 0) { // only the "inline" ones starting ;note: so far 197 $data = explode("\n", $comment['data'], 2); 198 $quote = trim(substr($data[0], 6)); 199 $text = trim($data[1]); 200 201 $ranges = json_decode( 202 TikiLib::lib('attribute')->get_attribute( 203 'comment', 204 $comment['threadId'], 205 RANGE_ATTRIBUTE 206 ), 207 true 208 ); 209 210 $permissions = [ 211 'create' => $list['allow_post'], 212 'update' => $comment['can_edit'], 213 'delete' => $list['allow_remove'], 214 ]; 215 216 $comments[] = [ 217 'id' => $comment['threadId'], 218 'text' => $text, 219 'quote' => $quote, 220 'created' => $tikilib->get_iso8601_datetime($comment['commentDate']), 221 'updated' => $tikilib->get_iso8601_datetime($comment['commentDate']), // we don't have a commentUpdated column? 222 'ranges' => $ranges ? $ranges : [], 223 'permissions' => $permissions, 224 225 ]; 226 } 227 }; 228 229 return [ 230 'total' => count($comments), // TODO pagination 231 'rows' => $comments, 232 ]; 233 } 234 235 /** 236 * @param $text 237 * @return string 238 * @throws Exception 239 * @throws SmartyException 240 */ 241 private function createTitle($text) 242 { 243 $smarty = TikiLib::lib('smarty'); 244 $smarty->loadPlugin('smarty_modifier_truncate'); 245 246 return smarty_modifier_truncate($text, 50); 247 } 248 249 /** 250 * @param $quote 251 * @param $text 252 * @return string 253 */ 254 private function formatComment($quote, $text) 255 { 256 $safeQuote = str_replace(["\n", "\r"], ' ', $quote); 257 $safeQuote = addslashes($safeQuote); 258 return ';note:' . $safeQuote . "\n\n" . $text; 259 } 260} 261