xuanxuan.class.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. <?php
  2. class xuanxuanIm extends imModel
  3. {
  4. public function getExtensionList($userID)
  5. {
  6. $clientLang = $this->app->input['lang'];
  7. $this->app->clientLang = $clientLang;
  8. $this->app->loadLang('im');
  9. $entries = array();
  10. $baseURL = $this->getServer('zentao');
  11. $this->loadModel('user');
  12. $user = $this->dao->select('*')->from(TABLE_USER)->where('id')->eq($userID)->fetch();
  13. $user->admin = strpos($this->app->company->admins, ",{$user->account},") !== false;
  14. $user->rights = $this->user->authorize($user->account);
  15. $user->groups = $this->user->getGroups($user->account);
  16. $user->view = $this->user->grantUserView($user->account, $user->rights['acls']);
  17. $this->session->set('user', $user);
  18. $this->app->user = $this->session->user;
  19. $products = $this->loadModel('product')->getPairs();
  20. $executions = $this->loadModel('execution')->getPairs();
  21. $products = empty($products) ? array() : array_keys($products);
  22. $executions = empty($executions) ? array() : array_keys($executions);
  23. $libIdList = array_keys($this->loadModel('doc')->getLibs('all'));
  24. $productID = isset($products[0]) ? $products[0] : 0;
  25. $executionID = isset($executions[0]) ? $executions[0] : 0;
  26. $libID = isset($libIdList[0]) ? $libIdList[0] : 1;
  27. $actions = new stdclass();
  28. if(common::hasPriv('bug', 'create')) $actions->createBug = array('title' => $this->lang->im->createBug, 'url' => $baseURL . str_replace('/x.php', '/index.php', helper::createLink('bug', 'create', "productID=$productID", 'html')), 'height' => "600px", 'width' => "1200px");
  29. if(common::hasPriv('story', 'create')) $actions->createStory = array('title' => $this->lang->im->createStory, 'url' => $baseURL . str_replace('/x.php', '/index.php', helper::createLink('story', 'create', "productID=$productID", 'html')), 'height' => "600px", 'width' => "1200px");
  30. if(common::hasPriv('task', 'create')) $actions->createTask = array('title' => $this->lang->im->createTask, 'url' => $baseURL . str_replace('/x.php', '/index.php', helper::createLink('task', 'create', "executionID=$executionID", 'html')), 'height' => "600px", 'width' => "1200px");
  31. if(common::hasPriv('doc', 'create')) $actions->createDoc = array('title' => $this->lang->im->createDoc, 'url' => $baseURL . str_replace('/x.php', '/index.php', helper::createLink('doc', 'create', "objectType=mine&objectID=0&libID=0", 'html')), 'height' => "600px", 'width' => "1200px");
  32. if(common::hasPriv('todo', 'create')) $actions->createTodo = array('title' => $this->lang->im->createTodo, 'url' => $baseURL . str_replace('/x.php', '/index.php', helper::createLink('todo', 'create', '', 'html')), 'height' => "600px", 'width' => "1200px");
  33. $urls = array();
  34. foreach($this->config->im->cards as $moduleName => $methods)
  35. {
  36. foreach($methods as $methodName => $size)
  37. {
  38. if($this->config->requestType == 'GET')
  39. {
  40. $url = $this->config->webRoot . "index.php?m={$moduleName}&f={$methodName}";
  41. }
  42. else
  43. {
  44. $url = $this->config->webRoot . "{$moduleName}-{$methodName}-";
  45. }
  46. $urls[$url] = $size;
  47. }
  48. }
  49. $data = new stdClass();
  50. $data->entryID = 1;
  51. $data->name = 'zentao-integrated';
  52. $data->displayName = $this->lang->im->zentaoIntegrate;
  53. $data->webViewUrl = trim($baseURL . $this->config->webRoot, '/');
  54. $data->download = $baseURL . $this->config->webRoot . 'data/xuanxuan/zentao-integrated.zip';
  55. $data->md5 = md5_file($this->app->getDataRoot() . 'xuanxuan/zentao-integrated.zip');
  56. $data->data['actions'] = $actions;
  57. $data->data['urls'] = $urls;
  58. $data->data['entryUrl'] = trim($baseURL . $this->config->webRoot, '/');
  59. $entries[] = $data;
  60. unset($_SESSION['user']);
  61. return $entries;
  62. }
  63. public function getServer($backend = 'xxb')
  64. {
  65. $server = commonModel::getSysURL();
  66. if(!empty($this->config->xuanxuan->server))
  67. {
  68. $host = $this->server->http_host;
  69. $position = strrpos($host, ':');
  70. $port = $position === false ? '' : substr($host, $position + 1);
  71. $server = $this->config->xuanxuan->server;
  72. if($port and strpos($server, ":") === false)
  73. {
  74. $server = rtrim($server, '/');
  75. $server = "{$server}:{$port}";
  76. }
  77. }
  78. $server = rtrim($server, '/');
  79. /* Try to fix server url when server is still localhost. */
  80. $serverURLComponents = parse_url($server);
  81. if(!empty($serverURLComponents['host']) && in_array($serverURLComponents['host'], array('127.0.0.1', 'localhost', '::1')))
  82. {
  83. $loginURL = $this->loadModel('setting')->getItem("owner=system&module=im&section=loginurl&key={$this->app->session->userID}");
  84. if(!empty($loginURL))
  85. {
  86. $loginURLComponents = parse_url($loginURL);
  87. if(!empty($loginURLComponents['host'])) $server = substr_replace($server, $loginURLComponents['host'], strpos($server, $serverURLComponents['host']), strlen($serverURLComponents['host']));
  88. }
  89. }
  90. return $server;
  91. }
  92. public function uploadFile($fileName, $path, $size, $time, $userID, $users, $chat)
  93. {
  94. $user = $this->userGetByID($userID);
  95. $extension = $this->loadModel('file')->getExtension($fileName);
  96. $file = new stdclass();
  97. $file->pathname = $path;
  98. $file->title = rtrim($fileName, ".$extension");
  99. $file->extension = $extension;
  100. $file->size = $size;
  101. $file->objectType = 'chat';
  102. $file->objectID = $chat->id;
  103. $file->addedBy = !empty($user->account) ? $user->account : '';
  104. $file->addedDate = date(DT_DATETIME1, $time);
  105. $this->dao->insert(TABLE_FILE)->data($file)->exec();
  106. $fileID = $this->dao->lastInsertID();
  107. $path .= md5($fileName . $fileID . $time);
  108. $this->dao->update(TABLE_FILE)->set('pathname')->eq($path)->where('id')->eq($fileID)->exec();
  109. return $fileID;
  110. }
  111. public function chatAddAction($chatId = '', $action = '', $actorId = '', $result = '', $comment = '')
  112. {
  113. return;
  114. }
  115. public function userAddAction($user, $actionType, $result, $comment = '', $common = false)
  116. {
  117. if(!zget($this->config->xuanxuan, 'logLevel', 1) && !$common) return;
  118. $account = '';
  119. $userID = 0;
  120. if(is_int($user))
  121. {
  122. $account = $this->dao->select('account')->from(TABLE_USER)->where('id')->eq($user)->fetch('account');
  123. $userID = $user;
  124. }
  125. if(is_string($user))
  126. {
  127. $userID = $this->dao->select('id')->from(TABLE_USER)->where('account')->eq($user)->fetch('id');
  128. $account = $user;
  129. }
  130. if(empty($userID)) return;
  131. $actor = !empty($account) ? $account : '';
  132. $extra = json_encode(array('actorId' => $userID));
  133. $this->loadModel('action')->create('user', $userID, $actionType, $comment, $extra, $actor);
  134. }
  135. public function messageGetNotifyList()
  136. {
  137. $onlineUsers = $this->loadModel('im')->userGetList('online');
  138. if(empty($onlineUsers)) return array();
  139. $onlineUsers = array_keys($onlineUsers);
  140. $messageUserPairsData = $this->dao->select('message,user')->from(TABLE_IM_MESSAGESTATUS)
  141. ->where('status')->eq('waiting')
  142. ->andWhere('user')->in($onlineUsers)
  143. ->fetchAll();
  144. if(empty($messageUserPairsData)) return array();
  145. $messageUserPairs = array();
  146. foreach($messageUserPairsData as $data)
  147. {
  148. if(isset($messageUserPairs[$data->message]))
  149. {
  150. $messageUserPairs[$data->message][] = $data->user;
  151. continue;
  152. }
  153. $messageUserPairs[$data->message] = array($data->user);
  154. }
  155. $notifyMessages = $this->im->messageGetList('', array_keys($messageUserPairs), null, '', 'notify', false);
  156. if(empty($notifyMessages)) return array();
  157. $messageIDs = array();
  158. foreach($notifyMessages as $message) $messageIDs[] = $message->id;
  159. $messageUserPairs = array_intersect_key($messageUserPairs, array_flip($messageIDs));
  160. $notifications = $this->im->messageFormatNotify($notifyMessages);
  161. $data = array();
  162. $messages = array();
  163. foreach($notifications as $message)
  164. {
  165. foreach($messageUserPairs[$message->id] as $userID)
  166. {
  167. $messages[$userID][] = $message->id;
  168. $data[$userID][] = $message;
  169. }
  170. }
  171. foreach($messages as $userID => $message)
  172. {
  173. $this->dao->delete()->from(TABLE_IM_MESSAGESTATUS)
  174. ->where('message')->in($message)
  175. ->andWhere('user')->eq($userID)
  176. ->exec();
  177. }
  178. return $this->mergeNotifications($data);
  179. }
  180. /**
  181. * Merge notifications with same actor, objectType, actionType. Notifications with contentType of 'text' won't be merged.
  182. *
  183. * @param array $notificationData
  184. * @access public
  185. * @return array
  186. */
  187. public function mergeNotifications($notificationData)
  188. {
  189. /* Notification data: array($userID1 => array($notification1, $notification2), $userID2 => array($notification3)) */
  190. foreach($notificationData as $userID => $userMessages)
  191. {
  192. $messageGroups = array();
  193. foreach($userMessages as $message)
  194. {
  195. /* Group by $message->content->content->objectType, ...->parentType, ...->action, ...->actor */
  196. $contentDataOuter = json_decode($message->content);
  197. if(!isset($contentDataOuter->content))
  198. {
  199. $messageGroups['text'][] = $message;
  200. continue;
  201. }
  202. $contentData = json_decode($contentDataOuter->content);
  203. if(isset($contentData->contentType) && $contentData->contentType == 'text')
  204. {
  205. $messageGroups[$contentData->contentType][] = $message;
  206. continue;
  207. }
  208. if ($contentData->objectType == 'story' && $contentData->action == 'reviewed')
  209. {
  210. $extra = explode(',', $contentDataOuter->extra);
  211. $actor = $extra[0];
  212. $messageGroups["$contentData->objectType-$contentData->parentType-$contentData->action-$contentData->actor-$actor"][] = $message;
  213. }
  214. else
  215. {
  216. $messageGroups["$contentData->objectType-$contentData->parentType-$contentData->action-$contentData->actor"][] = $message;
  217. }
  218. }
  219. foreach($messageGroups as $groupKey => $messages)
  220. {
  221. if(count($messages) < 2 || $groupKey == 'text') continue;
  222. $notification = current($messages);
  223. array_shift($messages);
  224. $notificationContent = json_decode($notification->content);
  225. $notificationInnerContent = json_decode($notificationContent->content);
  226. /* Inner content: array($id => array($content1, $content2)) */
  227. $objectGroups = array($notificationInnerContent->id => array($notificationInnerContent));
  228. foreach($messages as $message)
  229. {
  230. $messageContent = json_decode($message->content);
  231. $messageInnerContent = json_decode($messageContent->content);
  232. $objectGroups[$messageInnerContent->id][] = $messageInnerContent;
  233. }
  234. foreach($objectGroups as $id => $objectGroup)
  235. {
  236. $object = current($objectGroup);
  237. $object->count = count($objectGroup);
  238. $object->url = $object->parentURL;
  239. unset($object->title);
  240. $objectGroups[$id] = $object;
  241. }
  242. $objectTotal = count($objectGroups);
  243. $notificationContent->content = json_encode(array_values($objectGroups));
  244. /* Hack alert: title count replacement currently assumes that default count is 1. */
  245. $notification->title = substr_replace($notification->title, "$objectTotal", strrpos($notification->title, '1'), 1);
  246. $notificationContent->title = $notification->title;
  247. $notification->content = json_encode($notificationContent);
  248. $messageGroups[$groupKey] = array($notification);
  249. }
  250. $mergedMessages = array();
  251. $userMessageGroups = array_values($messageGroups);
  252. foreach($userMessageGroups as $messageGroup) $mergedMessages = array_merge($mergedMessages, $messageGroup);
  253. $notificationData[$userID] = $mergedMessages;
  254. }
  255. return $notificationData;
  256. }
  257. public function getAiChatLatestContext($moduleId, $userId)
  258. {
  259. // use the last broadcast message as the divider
  260. $lastDivider = $this->dao->select('*')->from(TABLE_IM_MESSAGE)
  261. ->where('cgid')->eq("$userId&ai-{$moduleId}")
  262. ->andWhere('type')->eq('broadcast')
  263. ->orderBy('id desc')
  264. ->limit(1)
  265. ->fetch();
  266. if($lastDivider)
  267. {
  268. $messages = $this->dao->select('*')->from(TABLE_IM_MESSAGE)
  269. ->where('cgid')->eq("$userId&ai-{$moduleId}")
  270. ->andWhere('id')->gt($lastDivider->id)
  271. ->orderBy('id asc')
  272. ->fetchAll();
  273. } else {
  274. $messages = $this->dao->select('*')->from(TABLE_IM_MESSAGE)
  275. ->where('cgid')->eq("$userId&ai-{$moduleId}")
  276. ->fetchAll();
  277. }
  278. return $messages;
  279. }
  280. }