tao.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. <?php
  2. /**
  3. * The tao file of mail module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
  6. * @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author Yidong Wang <yidong@easycorp.ltd>
  8. * @package mail
  9. * @link https://www.zentao.net
  10. */
  11. class mailTao extends mailModel
  12. {
  13. /**
  14. * Exclude me from toList and ccList.
  15. *
  16. * @param array $toList
  17. * @param array $ccList
  18. * @access protected
  19. * @return array
  20. */
  21. protected function excludeMe($toList, $ccList)
  22. {
  23. $account = isset($this->app->user->account) ? $this->app->user->account : '';
  24. $toList = array_unique(array_filter(array_map(function($to) use($account){$to = trim($to); return $to == $account ? '' : $to;}, $toList)));
  25. $ccList = array_unique(array_filter(array_map(function($cc) use($account){$cc = trim($cc); return $cc == $account ? '' : $cc;}, $ccList)));
  26. return array($toList, $ccList);
  27. }
  28. /**
  29. * Process toList and ccList. Exclude me and remove deleted users.
  30. *
  31. * @param string $toList
  32. * @param string $ccList
  33. * @param bool $includeMe
  34. * @access protected
  35. * @return array
  36. */
  37. protected function processToAndCC($toList, $ccList, $includeMe = false)
  38. {
  39. $toList = $toList ? explode(',', str_replace(' ', '', $toList)) : array();
  40. $ccList = $ccList ? explode(',', str_replace(' ', '', $ccList)) : array();
  41. /* Process toList and ccList, remove current user from them. If toList is empty, use the first cc as to. */
  42. if(!$includeMe) list($toList, $ccList) = $this->excludeMe($toList, $ccList);
  43. /* Remove deleted users. */
  44. $this->app->loadConfig('message');
  45. $users = $this->loadModel('user')->getPairs('nodeleted|all');
  46. $blockUsers = isset($this->config->message->blockUser) ? explode(',', $this->config->message->blockUser) : array();
  47. $toList = array_unique(array_filter(array_map(function($to) use($users, $blockUsers) {$to = trim($to); return (isset($users[$to]) && !in_array($to, $blockUsers)) ? $to : '';}, $toList)));
  48. $ccList = array_unique(array_filter(array_map(function($cc) use($users, $blockUsers) {$cc = trim($cc); return (isset($users[$cc]) && !in_array($cc, $blockUsers)) ? $cc : '';}, $ccList)));
  49. if(empty($toList) and $ccList) $toList = array(array_shift($ccList));
  50. $toList = implode(',', $toList);
  51. $ccList = implode(',', $ccList);
  52. return array($toList, $ccList);
  53. }
  54. /**
  55. * 获取邮件内容中的图片 url 和物理文件的键值对。
  56. * Get key-value pairs of image URL and physical file in mail content.
  57. *
  58. * @param string $body
  59. * @access protected
  60. * @return array
  61. */
  62. public function getImages($body)
  63. {
  64. $images = array();
  65. /* 匹配形如 src="/file-read-1.jpg" 或 scr="/index.php?m=file&f=read&fileID=1" 的图片。 Match images like src="/file-read-1.jpg" or scr="/index.php?m=file&f=read&fileID=1". */
  66. $readLinkReg = str_replace(array('%fileID%', '/', '.', '?'), array('[0-9]+', '\/', '\.', '\?'), helper::createLink('file', 'read', 'fileID=(%fileID%)', '\w+'));
  67. preg_match_all('/ src="(' . $readLinkReg . ')" /', $body, $matches);
  68. $images += $this->getImagesByFileID($matches);
  69. /* 匹配形如 src="{1.jpg}" 的图片。 Match images like src="{1.jpg}". */
  70. preg_match_all('/ src="({([0-9]+)\.\w+?})" /', $body, $matches);
  71. $images += $this->getImagesByFileID($matches);
  72. /* 匹配形如 src="/data/upload/1.jpg" 的图片。 Match images like src="/data/upload/1.jpg". */
  73. preg_match_all('/ src="(\/?data\/upload\/[\/\w+]*)"/', $body, $matches);
  74. $images += $this->getImagesByPath($matches);
  75. return $images;
  76. }
  77. /**
  78. * 根据文件 ID 获取图片 url 和物理文件的键值对。
  79. * Get key-value pairs of image URL and physical file by file ID.
  80. *
  81. * @param array $matches
  82. * @access public
  83. * @return array
  84. */
  85. public function getImagesByFileID($matches)
  86. {
  87. if(!isset($matches[2])) return array();
  88. $this->loadModel('file');
  89. $images = array();
  90. foreach($matches[2] as $key => $fileID)
  91. {
  92. if(!$fileID) continue;
  93. $file = $this->file->getById((int)$fileID);
  94. if(!$file) continue;
  95. if(!in_array($file->extension, $this->config->file->imageExtensions)) continue;
  96. $images[$matches[1][$key]] = $file->realPath;
  97. }
  98. return $images;
  99. }
  100. /**
  101. * 根据路径获取图片 url 和物理文件的键值对。
  102. * Get key-value pairs of image URL and physical file by path.
  103. *
  104. * @param array $matches
  105. * @access public
  106. * @return array
  107. */
  108. public function getImagesByPath($matches)
  109. {
  110. if(!isset($matches[1])) return array();
  111. $images = array();
  112. foreach($matches[1] as $key => $path)
  113. {
  114. if(!$path) continue;
  115. $images[$path] = $path;
  116. }
  117. return $images;
  118. }
  119. /**
  120. * Replace image URL for mail content.
  121. *
  122. * @param string $body
  123. * @param array $images
  124. * @access protected
  125. * @return string
  126. */
  127. protected function replaceImageURL($body, $images)
  128. {
  129. foreach($images as $url => $file)
  130. {
  131. if(!$file) continue;
  132. $body = str_replace($url, 'cid:' . basename($file), $body);
  133. }
  134. return $body;
  135. }
  136. /**
  137. * Get action for mail.
  138. *
  139. * @param int $actionID
  140. * @access protected
  141. * @return object|false
  142. */
  143. protected function getActionForMail($actionID)
  144. {
  145. $this->loadModel('action');
  146. $action = $this->action->getById($actionID);
  147. $history = $this->action->getHistory($actionID);
  148. if(!$action) return false;
  149. $action->history = zget($history, $actionID, array());
  150. $action->appendLink = '';
  151. if(strpos($action->extra, ':') !== false)
  152. {
  153. list($extra, $id) = explode(':', $action->extra);
  154. $action->extra = $extra;
  155. $action->appendLink = $id;
  156. }
  157. return $action;
  158. }
  159. /**
  160. * Get object for mail by objectType.
  161. *
  162. * @param string $objectType kanbancard|testtask|doc|bug|story|task|release
  163. * @param int $objectID
  164. * @access protected
  165. * @return object
  166. */
  167. protected function getObjectForMail($objectType, $objectID)
  168. {
  169. if(empty($objectType) || empty($objectID)) return false;
  170. $objectModel = $objectType == 'kanbancard' ? $this->loadModel('kanban') : $this->loadModel($objectType);
  171. if(!$objectModel) return false;
  172. $getObjectMethod = $objectType == 'kanbancard' ? 'getCardByID' : 'getByID';
  173. if($objectType == 'task') $getObjectMethod = 'fetchByID';
  174. if(method_exists($objectModel, $getObjectMethod))
  175. {
  176. $object = call_user_func(array($objectModel, $getObjectMethod), $objectID);
  177. }
  178. else
  179. {
  180. $object = $this->fetchByID($objectID, $objectType);
  181. }
  182. if(!$object) return false;
  183. if($objectType == 'doc' && $object->contentType == 'markdown')
  184. {
  185. $object->content = commonModel::processMarkdown($object->content);
  186. $object->content = str_replace("<table>", "<table style='border-collapse: collapse;'>", $object->content);
  187. $object->content = str_replace("<th>", "<th style='word-break: break-word; border:1px solid #000;'>", $object->content);
  188. $object->content = str_replace("<td>", "<td style='word-break: break-word; border:1px solid #000;'>", $object->content);
  189. }
  190. return $object;
  191. }
  192. /**
  193. * Get addressees by objectType.
  194. *
  195. * @param string $objectType
  196. * @param object $action
  197. * @param object $object
  198. * @access protected
  199. * @return array|bool
  200. */
  201. protected function getAddressees($objectType, $object, $action)
  202. {
  203. if(empty($objectType) || empty($object) || empty($action) || empty($action->action)) return false;
  204. $objectModel = $this->loadModel($objectType);
  205. if(!$objectModel) return false;
  206. if(in_array($objectType, array('epic', 'requirement', 'story', 'task', 'meeting', 'review', 'deploy', 'reviewissue'))) return $objectModel->getToAndCcList($object, $action->action);
  207. if(in_array($objectType, array('ticket', 'rule'))) return $objectModel->getToAndCcList($object, $action);
  208. return $objectModel->getToAndCcList($object);
  209. }
  210. /**
  211. * Get mail content by objectType.
  212. *
  213. * @param string $objectType
  214. * @param object $action
  215. * @param object $object
  216. * @access protected
  217. * @return string
  218. */
  219. protected function getMailContent($objectType, $object, $action)
  220. {
  221. if(empty($objectType) || empty($object) || empty($action)) return '';
  222. if($objectType == 'mr') return '';
  223. $domain = zget($this->config->mail, 'domain', common::getSysURL());
  224. $domain = rtrim($domain, '/');
  225. $mailTitle = strtoupper($objectType) . ' #' . $object->id;
  226. $modulePath = $this->app->getModulePath('', $objectType);
  227. if(!file_exists($modulePath)) return '';
  228. $oldcwd = getcwd();
  229. $viewFile = $modulePath . 'view/sendmail.html.php';
  230. $viewExtPath = $this->app->getModuleExtPath($objectType, 'view');
  231. $extHookFiles = array();
  232. if(!empty($viewExtPath))
  233. {
  234. $commonExtViewFile = $viewExtPath['common'] . "sendmail.html.php";
  235. if(file_exists($commonExtViewFile)) $viewFile = $commonExtViewFile;
  236. $extHookFiles = glob($viewExtPath['common'] . "sendmail.*.html.hook.php");
  237. }
  238. if(!file_exists($viewFile)) return '';
  239. chdir(dirname($viewFile, 2));
  240. ob_start();
  241. include $viewFile;
  242. foreach($extHookFiles as $hookFile) include $hookFile;
  243. $mailContent = ob_get_contents();
  244. ob_end_clean();
  245. chdir($oldcwd);
  246. return $mailContent;
  247. }
  248. /**
  249. * Get MR mail content.
  250. *
  251. * @param object $object
  252. * @param string $action
  253. * @param string $role to|cc
  254. * @access protected
  255. * @return string
  256. */
  257. protected function getMRMailContent($object, $action, $role = 'to')
  258. {
  259. $this->app->loadLang('mr');
  260. $title = $this->getObjectTitle($object, 'mr');
  261. $domain = zget($this->config->mail, 'domain', common::getSysURL());
  262. $domain = rtrim($domain, '/');
  263. $MRLink = $domain . helper::createLink('mr', 'view', "id={$object->id}");
  264. if($action == 'compilefail') return sprintf($this->lang->mr->failMessage, $MRLink, $title);
  265. if($action == 'compilepass')
  266. {
  267. $message = $this->lang->mr->toCreatedMessage;
  268. if($role == 'cc') $message = $this->lang->mr->toReviewerMessage;
  269. return sprintf($message, $MRLink, $title);
  270. }
  271. return '';
  272. }
  273. /**
  274. * Get object title for mail.
  275. *
  276. * @param object $object
  277. * @param string $objectType
  278. * @access protected
  279. * @return string
  280. */
  281. protected function getObjectTitle($object, $objectType)
  282. {
  283. $this->loadModel('action');
  284. if($objectType == 'auditplan') return $this->lang->auditplan->common . ' #' . $object->id;
  285. $nameFields = zget($this->config->action->objectNameFields, $objectType, array());
  286. return zget($object, $nameFields, '');
  287. }
  288. }