admin.js 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130
  1. module.exports = function(crowi, app) {
  2. 'use strict';
  3. var debug = require('debug')('growi:routes:admin')
  4. , fs = require('fs')
  5. , models = crowi.models
  6. , Page = models.Page
  7. , PageGroupRelation = models.PageGroupRelation
  8. , User = models.User
  9. , ExternalAccount = models.ExternalAccount
  10. , UserGroup = models.UserGroup
  11. , UserGroupRelation = models.UserGroupRelation
  12. , Config = models.Config
  13. , PluginUtils = require('../plugins/plugin-utils')
  14. , pluginUtils = new PluginUtils()
  15. , ApiResponse = require('../util/apiResponse')
  16. , MAX_PAGE_LIST = 50
  17. , actions = {};
  18. function createPager(total, limit, page, pagesCount, maxPageList) {
  19. const pager = {
  20. page: page,
  21. pagesCount: pagesCount,
  22. pages: [],
  23. total: total,
  24. previous: null,
  25. previousDots: false,
  26. next: null,
  27. nextDots: false,
  28. };
  29. if (page > 1) {
  30. pager.previous = page - 1;
  31. }
  32. if (page < pagesCount) {
  33. pager.next = page + 1;
  34. }
  35. let pagerMin = Math.max(1, Math.ceil(page - maxPageList/2));
  36. let pagerMax = Math.min(pagesCount, Math.floor(page + maxPageList/2));
  37. if (pagerMin === 1) {
  38. if (MAX_PAGE_LIST < pagesCount) {
  39. pagerMax = MAX_PAGE_LIST;
  40. }
  41. else {
  42. pagerMax = pagesCount;
  43. }
  44. }
  45. if (pagerMax === pagesCount) {
  46. if ((pagerMax - MAX_PAGE_LIST) < 1) {
  47. pagerMin = 1;
  48. }
  49. else {
  50. pagerMin = pagerMax - MAX_PAGE_LIST;
  51. }
  52. }
  53. pager.previousDots = null;
  54. if (pagerMin > 1) {
  55. pager.previousDots = true;
  56. }
  57. pager.nextDots = null;
  58. if (pagerMax < pagesCount) {
  59. pager.nextDots = true;
  60. }
  61. for (let i = pagerMin; i <= pagerMax; i++) {
  62. pager.pages.push(i);
  63. }
  64. return pager;
  65. }
  66. actions.index = function(req, res) {
  67. return res.render('admin/index', {
  68. plugins: pluginUtils.listPlugins(crowi.rootDir),
  69. });
  70. };
  71. // app.get('/admin/app' , admin.app.index);
  72. actions.app = {};
  73. actions.app.index = function(req, res) {
  74. var settingForm;
  75. settingForm = Config.setupCofigFormData('crowi', req.config);
  76. return res.render('admin/app', {
  77. settingForm: settingForm,
  78. });
  79. };
  80. actions.app.settingUpdate = function(req, res) {
  81. };
  82. // app.get('/admin/security' , admin.security.index);
  83. actions.security = {};
  84. actions.security.index = function(req, res) {
  85. const settingForm = Config.setupCofigFormData('crowi', req.config);
  86. return res.render('admin/security', { settingForm });
  87. };
  88. // app.get('/admin/markdown' , admin.markdown.index);
  89. actions.markdown = {};
  90. actions.markdown.index = function(req, res) {
  91. var config = crowi.getConfig();
  92. var markdownSetting = Config.setupCofigFormData('markdown', config);
  93. return res.render('admin/markdown', {
  94. markdownSetting: markdownSetting,
  95. });
  96. };
  97. // app.post('/admin/markdown/lineBreaksSetting' , admin.markdown.lineBreaksSetting);
  98. actions.markdown.lineBreaksSetting = function(req, res) {
  99. var markdownSetting = req.form.markdownSetting;
  100. req.session.markdownSetting = markdownSetting;
  101. if (req.form.isValid) {
  102. Config.updateNamespaceByArray('markdown', markdownSetting, function(err, config) {
  103. Config.updateConfigCache('markdown', config);
  104. req.session.markdownSetting = null;
  105. req.flash('successMessage', ['Successfully updated!']);
  106. return res.redirect('/admin/markdown');
  107. });
  108. }
  109. else {
  110. req.flash('errorMessage', req.form.errors);
  111. return res.redirect('/admin/markdown');
  112. }
  113. };
  114. // app.post('/admin/markdown/XSSSetting' , admin.markdown.XSSSetting);
  115. actions.markdown.XSSSetting = function(req, res) {
  116. var XSSSetting = req.form.markdownSetting;
  117. req.session.markdownSetting = XSSSetting;
  118. if (req.form.isValid) {
  119. Config.updateNamespaceByArray('markdown', XSSSetting, function(err, config) {
  120. Config.updateConfigCache('markdown', config);
  121. req.session.XSSSetting = null;
  122. req.flash('successMessage', ['Successfully updated!']);
  123. return res.redirect('/admin/markdown');
  124. });
  125. }
  126. else {
  127. req.flash('errorMessage', req.form.errors);
  128. return res.redirect('/admin/markdown');
  129. }
  130. };
  131. // app.get('/admin/customize' , admin.customize.index);
  132. actions.customize = {};
  133. actions.customize.index = function(req, res) {
  134. var settingForm;
  135. settingForm = Config.setupCofigFormData('crowi', req.config);
  136. const highlightJsCssSelectorOptions = {
  137. 'github': { name: '[Light] GitHub', border: false },
  138. 'github-gist': { name: '[Light] GitHub Gist', border: true },
  139. 'atom-one-light': { name: '[Light] Atom One Light', border: true },
  140. 'xcode': { name: '[Light] Xcode', border: true },
  141. 'vs': { name: '[Light] Vs', border: true },
  142. 'atom-one-dark': { name: '[Dark] Atom One Dark', border: false },
  143. 'hybrid': { name: '[Dark] Hybrid', border: false },
  144. 'monokai': { name: '[Dark] Monokai', border: false },
  145. 'tomorrow-night': { name: '[Dark] Tomorrow Night', border: false },
  146. 'vs2015': { name: '[Dark] Vs 2015', border: false },
  147. };
  148. return res.render('admin/customize', {
  149. settingForm: settingForm,
  150. highlightJsCssSelectorOptions: highlightJsCssSelectorOptions
  151. });
  152. };
  153. // app.get('/admin/notification' , admin.notification.index);
  154. actions.notification = {};
  155. actions.notification.index = function(req, res) {
  156. var config = crowi.getConfig();
  157. var UpdatePost = crowi.model('UpdatePost');
  158. var slackSetting = Config.setupCofigFormData('notification', config);
  159. var hasSlackIwhUrl = Config.hasSlackIwhUrl(config);
  160. var hasSlackToken = Config.hasSlackToken(config);
  161. var slack = crowi.slack;
  162. if (!Config.hasSlackIwhUrl(req.config)) {
  163. slackSetting['slack:incomingWebhookUrl'] = '';
  164. }
  165. if (req.session.slackSetting) {
  166. slackSetting = req.session.slackSetting;
  167. req.session.slackSetting = null;
  168. }
  169. UpdatePost.findAll()
  170. .then(function(settings) {
  171. return res.render('admin/notification', {
  172. settings,
  173. slackSetting,
  174. hasSlackIwhUrl,
  175. hasSlackToken,
  176. });
  177. });
  178. };
  179. // app.post('/admin/notification/slackSetting' , admin.notification.slackauth);
  180. actions.notification.slackSetting = function(req, res) {
  181. var slackSetting = req.form.slackSetting;
  182. req.session.slackSetting = slackSetting;
  183. if (req.form.isValid) {
  184. Config.updateNamespaceByArray('notification', slackSetting, function(err, config) {
  185. Config.updateConfigCache('notification', config);
  186. req.flash('successMessage', ['Successfully Updated!']);
  187. req.session.slackSetting = null;
  188. // Re-setup
  189. crowi.setupSlack().then(function() {
  190. return res.redirect('/admin/notification');
  191. });
  192. });
  193. }
  194. else {
  195. req.flash('errorMessage', req.form.errors);
  196. return res.redirect('/admin/notification');
  197. }
  198. };
  199. // app.get('/admin/notification/slackAuth' , admin.notification.slackauth);
  200. actions.notification.slackAuth = function(req, res) {
  201. const code = req.query.code;
  202. const config = crowi.getConfig();
  203. if (!code || !Config.hasSlackConfig(req.config)) {
  204. return res.redirect('/admin/notification');
  205. }
  206. const slack = crowi.slack;
  207. slack.getOauthAccessToken(code)
  208. .then(data => {
  209. debug('oauth response', data);
  210. Config.updateNamespaceByArray('notification', {'slack:token': data.access_token}, function(err, config) {
  211. if (err) {
  212. req.flash('errorMessage', ['Failed to save access_token. Please try again.']);
  213. }
  214. else {
  215. Config.updateConfigCache('notification', config);
  216. req.flash('successMessage', ['Successfully Connected!']);
  217. }
  218. return res.redirect('/admin/notification');
  219. });
  220. }).catch(err => {
  221. debug('oauth response ERROR', err);
  222. req.flash('errorMessage', ['Failed to fetch access_token. Please do connect again.']);
  223. return res.redirect('/admin/notification');
  224. });
  225. };
  226. actions.search = {};
  227. actions.search.index = function(req, res) {
  228. return res.render('admin/search', {
  229. });
  230. };
  231. // app.post('/admin/notification/slackIwhSetting' , admin.notification.slackIwhSetting);
  232. actions.notification.slackIwhSetting = function(req, res) {
  233. var slackIwhSetting = req.form.slackIwhSetting;
  234. if (req.form.isValid) {
  235. Config.updateNamespaceByArray('notification', slackIwhSetting, function(err, config) {
  236. Config.updateConfigCache('notification', config);
  237. req.flash('successMessage', ['Successfully Updated!']);
  238. // Re-setup
  239. crowi.setupSlack().then(function() {
  240. return res.redirect('/admin/notification#slack-incoming-webhooks');
  241. });
  242. });
  243. }
  244. else {
  245. req.flash('errorMessage', req.form.errors);
  246. return res.redirect('/admin/notification#slack-incoming-webhooks');
  247. }
  248. };
  249. // app.post('/admin/notification/slackSetting/disconnect' , admin.notification.disconnectFromSlack);
  250. actions.notification.disconnectFromSlack = function(req, res) {
  251. const config = crowi.getConfig();
  252. const slack = crowi.slack;
  253. Config.updateNamespaceByArray('notification', {'slack:token': ''}, function(err, config) {
  254. Config.updateConfigCache('notification', config);
  255. req.flash('successMessage', ['Successfully Disconnected!']);
  256. return res.redirect('/admin/notification');
  257. });
  258. };
  259. actions.search.buildIndex = function(req, res) {
  260. var search = crowi.getSearcher();
  261. if (!search) {
  262. return res.redirect('/admin');
  263. }
  264. return new Promise(function(resolve, reject) {
  265. search.deleteIndex()
  266. .then(function(data) {
  267. debug('Index deleted.');
  268. resolve();
  269. }).catch(function(err) {
  270. debug('Delete index Error, but if it is initialize, its ok.', err);
  271. resolve();
  272. });
  273. })
  274. .then(function() {
  275. return search.buildIndex();
  276. })
  277. .then(function(data) {
  278. if (!data.errors) {
  279. debug('Index created.');
  280. }
  281. return search.addAllPages();
  282. })
  283. .then(function(data) {
  284. if (!data.errors) {
  285. debug('Data is successfully indexed.');
  286. req.flash('successMessage', 'Data is successfully indexed.');
  287. }
  288. else {
  289. debug('Data index error.', data.errors);
  290. req.flash('errorMessage', `Data index error: ${data.errors}`);
  291. }
  292. return res.redirect('/admin/search');
  293. })
  294. .catch(function(err) {
  295. debug('Error', err);
  296. req.flash('errorMessage', `Error: ${err}`);
  297. return res.redirect('/admin/search');
  298. });
  299. };
  300. actions.user = {};
  301. actions.user.index = function(req, res) {
  302. var page = parseInt(req.query.page) || 1;
  303. User.findUsersWithPagination({page: page}, function(err, result) {
  304. const pager = createPager(result.total, result.limit, result.page, result.pages, MAX_PAGE_LIST);
  305. return res.render('admin/users', {
  306. users: result.docs,
  307. pager: pager
  308. });
  309. });
  310. };
  311. actions.user.invite = function(req, res) {
  312. var form = req.form.inviteForm;
  313. var toSendEmail = form.sendEmail || false;
  314. if (req.form.isValid) {
  315. User.createUsersByInvitation(form.emailList.split('\n'), toSendEmail, function(err, userList) {
  316. if (err) {
  317. req.flash('errorMessage', req.form.errors.join('\n'));
  318. }
  319. else {
  320. req.flash('createdUser', userList);
  321. }
  322. return res.redirect('/admin/users');
  323. });
  324. }
  325. else {
  326. req.flash('errorMessage', req.form.errors.join('\n'));
  327. return res.redirect('/admin/users');
  328. }
  329. };
  330. actions.user.makeAdmin = function(req, res) {
  331. var id = req.params.id;
  332. User.findById(id, function(err, userData) {
  333. userData.makeAdmin(function(err, userData) {
  334. if (err === null) {
  335. req.flash('successMessage', userData.name + 'さんのアカウントを管理者に設定しました。');
  336. }
  337. else {
  338. req.flash('errorMessage', '更新に失敗しました。');
  339. debug(err, userData);
  340. }
  341. return res.redirect('/admin/users');
  342. });
  343. });
  344. };
  345. actions.user.removeFromAdmin = function(req, res) {
  346. var id = req.params.id;
  347. User.findById(id, function(err, userData) {
  348. userData.removeFromAdmin(function(err, userData) {
  349. if (err === null) {
  350. req.flash('successMessage', userData.name + 'さんのアカウントを管理者から外しました。');
  351. }
  352. else {
  353. req.flash('errorMessage', '更新に失敗しました。');
  354. debug(err, userData);
  355. }
  356. return res.redirect('/admin/users');
  357. });
  358. });
  359. };
  360. actions.user.activate = function(req, res) {
  361. var id = req.params.id;
  362. User.findById(id, function(err, userData) {
  363. userData.statusActivate(function(err, userData) {
  364. if (err === null) {
  365. req.flash('successMessage', userData.name + 'さんのアカウントを有効化しました');
  366. }
  367. else {
  368. req.flash('errorMessage', '更新に失敗しました。');
  369. debug(err, userData);
  370. }
  371. return res.redirect('/admin/users');
  372. });
  373. });
  374. };
  375. actions.user.suspend = function(req, res) {
  376. var id = req.params.id;
  377. User.findById(id, function(err, userData) {
  378. userData.statusSuspend(function(err, userData) {
  379. if (err === null) {
  380. req.flash('successMessage', userData.name + 'さんのアカウントを利用停止にしました');
  381. }
  382. else {
  383. req.flash('errorMessage', '更新に失敗しました。');
  384. debug(err, userData);
  385. }
  386. return res.redirect('/admin/users');
  387. });
  388. });
  389. };
  390. actions.user.remove = function(req, res) {
  391. var id = req.params.id;
  392. let username = '';
  393. return new Promise((resolve, reject) => {
  394. User.findById(id, (err, userData) => {
  395. username = userData.username;
  396. return resolve(userData);
  397. });
  398. })
  399. .then((userData) => {
  400. return new Promise((resolve, reject) => {
  401. userData.statusDelete((err, userData) => {
  402. if (err) {
  403. reject(err);
  404. }
  405. resolve(userData);
  406. });
  407. });
  408. })
  409. .then((userData) => {
  410. // remove all External Accounts
  411. ExternalAccount.remove({user: userData})
  412. .then((err) => {
  413. if (err) {
  414. throw new Error(err.message);
  415. }
  416. return userData;
  417. });
  418. })
  419. .then((userData) => {
  420. return Page.removePageByPath(`/user/${username}`)
  421. .then(() => userData);
  422. })
  423. .then((userData) => {
  424. req.flash('successMessage', `${username} さんのアカウントを削除しました`);
  425. return res.redirect('/admin/users');
  426. })
  427. .catch((err) => {
  428. req.flash('errorMessage', '削除に失敗しました。');
  429. return res.redirect('/admin/users');
  430. });
  431. };
  432. // これやったときの relation の挙動未確認
  433. actions.user.removeCompletely = function(req, res) {
  434. // ユーザーの物理削除
  435. var id = req.params.id;
  436. User.removeCompletelyById(id, function(err, removed) {
  437. if (err) {
  438. debug('Error while removing user.', err, id);
  439. req.flash('errorMessage', '完全な削除に失敗しました。');
  440. }
  441. else {
  442. req.flash('successMessage', '削除しました');
  443. }
  444. return res.redirect('/admin/users');
  445. });
  446. };
  447. // app.post('/_api/admin/users.resetPassword' , admin.api.usersResetPassword);
  448. actions.user.resetPassword = function(req, res) {
  449. const id = req.body.user_id;
  450. const User = crowi.model('User');
  451. User.resetPasswordByRandomString(id)
  452. .then(function(data) {
  453. data.user = User.filterToPublicFields(data.user);
  454. return res.json(ApiResponse.success(data));
  455. }).catch(function(err) {
  456. debug('Error on reseting password', err);
  457. return res.json(ApiResponse.error('Error'));
  458. });
  459. };
  460. actions.externalAccount = {};
  461. actions.externalAccount.index = function(req, res) {
  462. const page = parseInt(req.query.page) || 1;
  463. ExternalAccount.findAllWithPagination({page})
  464. .then((result) => {
  465. const pager = createPager(result.total, result.limit, result.page, result.pages, MAX_PAGE_LIST);
  466. return res.render('admin/external-accounts', {
  467. accounts: result.docs,
  468. pager: pager
  469. });
  470. });
  471. };
  472. actions.externalAccount.remove = function(req, res) {
  473. const accountId = req.params.id;
  474. ExternalAccount.findOneAndRemove({accountId})
  475. .then((result) => {
  476. if (result == null) {
  477. req.flash('errorMessage', '削除に失敗しました。');
  478. return res.redirect('/admin/users/external-accounts');
  479. }
  480. else {
  481. req.flash('successMessage', `外部アカウント '${accountId}' を削除しました`);
  482. return res.redirect('/admin/users/external-accounts');
  483. }
  484. });
  485. };
  486. actions.userGroup = {};
  487. actions.userGroup.index = function(req, res) {
  488. var page = parseInt(req.query.page) || 1;
  489. var renderVar = {
  490. userGroups: [],
  491. userGroupRelations: new Map(),
  492. pager: null,
  493. };
  494. UserGroup.findUserGroupsWithPagination({ page: page })
  495. .then((result) => {
  496. const pager = createPager(result.total, result.limit, result.page, result.pages, MAX_PAGE_LIST);
  497. var userGroups = result.docs;
  498. renderVar.userGroups = userGroups;
  499. renderVar.pager = pager;
  500. return userGroups.map((userGroup) => {
  501. return new Promise((resolve, reject) => {
  502. UserGroupRelation.findAllRelationForUserGroup(userGroup)
  503. .then((relations) => {
  504. return resolve([userGroup, relations]);
  505. });
  506. });
  507. });
  508. })
  509. .then((allRelationsPromise) => {
  510. return Promise.all(allRelationsPromise);
  511. })
  512. .then((relations) => {
  513. renderVar.userGroupRelations = new Map(relations);
  514. debug('in findUserGroupsWithPagination findAllRelationForUserGroupResult', renderVar.userGroupRelations);
  515. return res.render('admin/user-groups', renderVar);
  516. })
  517. .catch( function(err) {
  518. debug('Error on find all relations', err);
  519. return res.json(ApiResponse.error('Error'));
  520. });
  521. };
  522. // グループ詳細
  523. actions.userGroup.detail = function(req, res) {
  524. var name = req.params.name;
  525. var renderVar = {
  526. userGroup: null,
  527. userGroupRelations: [],
  528. pageGroupRelations: [],
  529. notRelatedusers: []
  530. };
  531. var targetUserGroup = null;
  532. UserGroup.findUserGroupByName(name)
  533. .then(function(userGroup) {
  534. targetUserGroup = userGroup;
  535. if (targetUserGroup == null) {
  536. req.flash('errorMessage', 'グループがありません');
  537. throw new Error('no userGroup is exists. ', name);
  538. }
  539. else {
  540. renderVar.userGroup = targetUserGroup;
  541. return Promise.all([
  542. // get all user and group relations
  543. UserGroupRelation.findAllRelationForUserGroup(targetUserGroup),
  544. // get all page and group relations
  545. PageGroupRelation.findAllRelationForUserGroup(targetUserGroup),
  546. // get all not related users for group
  547. UserGroupRelation.findUserByNotRelatedGroup(targetUserGroup),
  548. ]);
  549. }
  550. })
  551. .then((resolves) => {
  552. renderVar.userGroupRelations = resolves[0];
  553. renderVar.pageGroupRelations = resolves[1];
  554. renderVar.notRelatedusers = resolves[2];
  555. debug('notRelatedusers', renderVar.notRelatedusers);
  556. return res.render('admin/user-group-detail', renderVar);
  557. })
  558. .catch((err) => {
  559. req.flash('errorMessage', 'ユーザグループの検索に失敗しました');
  560. debug('Error on get userGroupDetail', err);
  561. return res.redirect('/admin/user-groups');
  562. });
  563. };
  564. //グループの生成
  565. actions.userGroup.create = function(req, res) {
  566. var form = req.form.createGroupForm;
  567. if (req.form.isValid) {
  568. UserGroup.createGroupByName(form.userGroupName)
  569. .then((newUserGroup) => {
  570. req.flash('successMessage', newUserGroup.name);
  571. req.flash('createdUserGroup', newUserGroup);
  572. return res.redirect('/admin/user-groups');
  573. })
  574. .catch((err) => {
  575. debug('create userGroup error:', err);
  576. req.flash('errorMessage', '同じグループ名が既に存在します。');
  577. });
  578. }
  579. else {
  580. req.flash('errorMessage', req.form.errors.join('\n'));
  581. return res.redirect('/admin/user-groups');
  582. }
  583. };
  584. //
  585. actions.userGroup.update = function(req, res) {
  586. var userGroupId = req.params.userGroupId;
  587. var name = req.body.name;
  588. UserGroup.findById(userGroupId)
  589. .then((userGroupData) => {
  590. if (userGroupData == null) {
  591. req.flash('errorMessage', 'グループの検索に失敗しました。');
  592. return new Promise();
  593. }
  594. else {
  595. // 名前存在チェック
  596. return UserGroup.isRegisterableName(name)
  597. .then((isRegisterableName) => {
  598. // 既に存在するグループ名に更新しようとした場合はエラー
  599. if (!isRegisterableName) {
  600. req.flash('errorMessage', 'グループ名が既に存在します。');
  601. }
  602. else {
  603. return userGroupData.updateName(name)
  604. .then(() => {
  605. req.flash('successMessage', 'グループ名を更新しました。');
  606. })
  607. .catch((err) => {
  608. req.flash('errorMessage', 'グループ名の更新に失敗しました。');
  609. });
  610. }
  611. });
  612. }
  613. })
  614. .then(() => {
  615. return res.redirect('/admin/user-group-detail/' + name);
  616. });
  617. };
  618. actions.userGroup.uploadGroupPicture = function(req, res) {
  619. var fileUploader = require('../util/fileUploader')(crowi, app);
  620. //var storagePlugin = new pluginService('storage');
  621. //var storage = require('../service/storage').StorageService(config);
  622. var userGroupId = req.params.userGroupId;
  623. var tmpFile = req.file || null;
  624. if (!tmpFile) {
  625. return res.json({
  626. 'status': false,
  627. 'message': 'File type error.'
  628. });
  629. }
  630. UserGroup.findById(userGroupId, function(err, userGroupData) {
  631. if (!userGroupData) {
  632. return res.json({
  633. 'status': false,
  634. 'message': 'UserGroup error.'
  635. });
  636. }
  637. var tmpPath = tmpFile.path;
  638. var filePath = UserGroup.createUserGroupPictureFilePath(userGroupData, tmpFile.filename + tmpFile.originalname);
  639. var acceptableFileType = /image\/.+/;
  640. if (!tmpFile.mimetype.match(acceptableFileType)) {
  641. return res.json({
  642. 'status': false,
  643. 'message': 'File type error. Only image files is allowed to set as user picture.',
  644. });
  645. }
  646. var tmpFileStream = fs.createReadStream(tmpPath, { flags: 'r', encoding: null, fd: null, mode: '0666', autoClose: true });
  647. fileUploader.uploadFile(filePath, tmpFile.mimetype, tmpFileStream, {})
  648. .then(function(data) {
  649. var imageUrl = fileUploader.generateUrl(filePath);
  650. userGroupData.updateImage(imageUrl)
  651. .then(() => {
  652. fs.unlink(tmpPath, function(err) {
  653. if (err) {
  654. debug('Error while deleting tmp file.', err);
  655. }
  656. return res.json({
  657. 'status': true,
  658. 'url': imageUrl,
  659. 'message': '',
  660. });
  661. });
  662. });
  663. }).catch(function(err) {
  664. debug('Uploading error', err);
  665. return res.json({
  666. 'status': false,
  667. 'message': 'Error while uploading to ',
  668. });
  669. });
  670. });
  671. };
  672. actions.userGroup.deletePicture = function(req, res) {
  673. var userGroupId = req.params.userGroupId;
  674. let userGroupName = null;
  675. UserGroup.findById(userGroupId)
  676. .then((userGroupData) => {
  677. if (userGroupData == null) {
  678. return Promise.reject();
  679. }
  680. else {
  681. userGroupName = userGroupData.name;
  682. return userGroupData.deleteImage();
  683. }
  684. })
  685. .then((updated) => {
  686. req.flash('successMessage', 'Deleted group picture');
  687. return res.redirect('/admin/user-group-detail/' + userGroupName);
  688. })
  689. .catch((err) => {
  690. debug('An error occured.', err);
  691. req.flash('errorMessage', 'Error while deleting group picture');
  692. if (userGroupName == null) {
  693. return res.redirect('/admin/user-groups/');
  694. }
  695. else {
  696. return res.redirect('/admin/user-group-detail/' + userGroupName);
  697. }
  698. });
  699. };
  700. // app.post('/_api/admin/user-group/delete' , admin.userGroup.removeCompletely);
  701. actions.userGroup.removeCompletely = function(req, res) {
  702. const id = req.body.user_group_id;
  703. const fileUploader = require('../util/fileUploader')(crowi, app);
  704. UserGroup.removeCompletelyById(id)
  705. //// TODO remove attachments
  706. // couldn't remove because filePath includes '/uploads/uploads'
  707. // Error: ENOENT: no such file or directory, unlink 'C:\dev\growi\public\uploads\uploads\userGroup\5b1df18ab69611651cc71495.png
  708. //
  709. // .then(removed => {
  710. // if (removed.image != null) {
  711. // fileUploader.deleteFile(null, removed.image);
  712. // }
  713. // })
  714. .then(() => {
  715. req.flash('successMessage', '削除しました');
  716. return res.redirect('/admin/user-groups');
  717. })
  718. .catch((err) => {
  719. debug('Error while removing userGroup.', err, id);
  720. req.flash('errorMessage', '完全な削除に失敗しました。');
  721. return res.redirect('/admin/user-groups');
  722. });
  723. };
  724. actions.userGroupRelation = {};
  725. actions.userGroupRelation.index = function(req, res) {
  726. };
  727. actions.userGroupRelation.create = function(req, res) {
  728. const User = crowi.model('User');
  729. const UserGroup = crowi.model('UserGroup');
  730. const UserGroupRelation = crowi.model('UserGroupRelation');
  731. // req params
  732. const userName = req.body.user_name;
  733. const userGroupId = req.body.user_group_id;
  734. let user = null;
  735. let userGroup = null;
  736. Promise.all([
  737. // ユーザグループをIDで検索
  738. UserGroup.findById(userGroupId),
  739. // ユーザを名前で検索
  740. User.findUserByUsername(userName),
  741. ])
  742. .then((resolves) => {
  743. userGroup = resolves[0];
  744. user = resolves[1];
  745. // Relation を作成
  746. UserGroupRelation.createRelation(userGroup, user);
  747. })
  748. .then((result) => {
  749. return res.redirect('/admin/user-group-detail/' + userGroup.name);
  750. }).catch((err) => {
  751. debug('Error on create user-group relation', err);
  752. req.flash('errorMessage', 'Error on create user-group relation');
  753. return res.redirect('/admin/user-group-detail/' + userGroup.name);
  754. });
  755. };
  756. actions.userGroupRelation.remove = function(req, res) {
  757. const UserGroupRelation = crowi.model('UserGroupRelation');
  758. var name = req.params.name;
  759. var relationId = req.params.relationId;
  760. debug(name, relationId);
  761. UserGroupRelation.removeById(relationId)
  762. .then(() =>{
  763. return res.redirect('/admin/user-group-detail/' + name);
  764. })
  765. .catch((err) => {
  766. debug('Error on remove user-group-relation', err);
  767. req.flash('errorMessage', 'グループのユーザ削除に失敗しました。');
  768. });
  769. };
  770. actions.api = {};
  771. actions.api.appSetting = function(req, res) {
  772. var form = req.form.settingForm;
  773. if (req.form.isValid) {
  774. debug('form content', form);
  775. // mail setting ならここで validation
  776. if (form['mail:from']) {
  777. validateMailSetting(req, form, function(err, data) {
  778. debug('Error validate mail setting: ', err, data);
  779. if (err) {
  780. req.form.errors.push('SMTPを利用したテストメール送信に失敗しました。設定をみなおしてください。');
  781. return res.json({status: false, message: req.form.errors.join('\n')});
  782. }
  783. return saveSetting(req, res, form);
  784. });
  785. }
  786. else {
  787. return saveSetting(req, res, form);
  788. }
  789. }
  790. else {
  791. return res.json({status: false, message: req.form.errors.join('\n')});
  792. }
  793. };
  794. actions.api.securitySetting = function(req, res) {
  795. const form = req.form.settingForm;
  796. if (req.form.isValid) {
  797. debug('form content', form);
  798. return saveSetting(req, res, form);
  799. }
  800. else {
  801. return res.json({status: false, message: req.form.errors.join('\n')});
  802. }
  803. };
  804. actions.api.securityPassportLdapSetting = function(req, res) {
  805. var form = req.form.settingForm;
  806. if (!req.form.isValid) {
  807. return res.json({status: false, message: req.form.errors.join('\n')});
  808. }
  809. debug('form content', form);
  810. return saveSettingAsync(form)
  811. .then(() => {
  812. const config = crowi.getConfig();
  813. // reset strategy
  814. crowi.passportService.resetLdapStrategy();
  815. // setup strategy
  816. if (Config.isEnabledPassportLdap(config)) {
  817. crowi.passportService.setupLdapStrategy(true);
  818. }
  819. return;
  820. })
  821. .then(() => {
  822. res.json({status: true});
  823. });
  824. };
  825. actions.api.securityPassportGoogleSetting = async(req, res) => {
  826. const form = req.form.settingForm;
  827. if (!req.form.isValid) {
  828. return res.json({status: false, message: req.form.errors.join('\n')});
  829. }
  830. debug('form content', form);
  831. await saveSettingAsync(form);
  832. const config = await crowi.getConfig();
  833. // reset strategy
  834. await crowi.passportService.resetGoogleStrategy();
  835. // setup strategy
  836. if (Config.isEnabledPassportGoogle(config)) {
  837. try {
  838. await crowi.passportService.setupGoogleStrategy(true);
  839. }
  840. catch (err) {
  841. // reset
  842. await crowi.passportService.resetGoogleStrategy();
  843. return res.json({status: false, message: err.message});
  844. }
  845. }
  846. return res.json({status: true});
  847. };
  848. actions.api.securityPassportGitHubSetting = async(req, res) => {
  849. const form = req.form.settingForm;
  850. if (!req.form.isValid) {
  851. return res.json({status: false, message: req.form.errors.join('\n')});
  852. }
  853. debug('form content', form);
  854. await saveSettingAsync(form);
  855. const config = await crowi.getConfig();
  856. // reset strategy
  857. await crowi.passportService.resetGitHubStrategy();
  858. // setup strategy
  859. if (Config.isEnabledPassportGoogle(config)) {
  860. try {
  861. await crowi.passportService.setupGitHubStrategy(true);
  862. }
  863. catch (err) {
  864. // reset
  865. await crowi.passportService.resetGoogleStrategy();
  866. return res.json({status: false, message: err.message});
  867. }
  868. }
  869. return res.json({status: true});
  870. };
  871. actions.api.customizeSetting = function(req, res) {
  872. const form = req.form.settingForm;
  873. if (req.form.isValid) {
  874. debug('form content', form);
  875. return saveSetting(req, res, form);
  876. }
  877. else {
  878. return res.json({status: false, message: req.form.errors.join('\n')});
  879. }
  880. };
  881. actions.api.customizeSetting = function(req, res) {
  882. const form = req.form.settingForm;
  883. if (req.form.isValid) {
  884. debug('form content', form);
  885. return saveSetting(req, res, form);
  886. }
  887. else {
  888. return res.json({status: false, message: req.form.errors.join('\n')});
  889. }
  890. };
  891. // app.post('/_api/admin/notifications.add' , admin.api.notificationAdd);
  892. actions.api.notificationAdd = function(req, res) {
  893. var UpdatePost = crowi.model('UpdatePost');
  894. var pathPattern = req.body.pathPattern;
  895. var channel = req.body.channel;
  896. debug('notification.add', pathPattern, channel);
  897. UpdatePost.create(pathPattern, channel, req.user)
  898. .then(function(doc) {
  899. debug('Successfully save updatePost', doc);
  900. // fixme: うーん
  901. doc.creator = doc.creator._id.toString();
  902. return res.json(ApiResponse.success({updatePost: doc}));
  903. }).catch(function(err) {
  904. debug('Failed to save updatePost', err);
  905. return res.json(ApiResponse.error());
  906. });
  907. };
  908. // app.post('/_api/admin/notifications.remove' , admin.api.notificationRemove);
  909. actions.api.notificationRemove = function(req, res) {
  910. var UpdatePost = crowi.model('UpdatePost');
  911. var id = req.body.id;
  912. UpdatePost.remove(id)
  913. .then(function() {
  914. debug('Successfully remove updatePost');
  915. return res.json(ApiResponse.success({}));
  916. }).catch(function(err) {
  917. debug('Failed to remove updatePost', err);
  918. return res.json(ApiResponse.error());
  919. });
  920. };
  921. // app.get('/_api/admin/users.search' , admin.api.userSearch);
  922. actions.api.usersSearch = function(req, res) {
  923. const User = crowi.model('User');
  924. const email =req.query.email;
  925. User.findUsersByPartOfEmail(email, {})
  926. .then(users => {
  927. const result = {
  928. data: users
  929. };
  930. return res.json(ApiResponse.success(result));
  931. }).catch(err => {
  932. return res.json(ApiResponse.error());
  933. });
  934. };
  935. /**
  936. * save settings, update config cache, and response json
  937. *
  938. * @param {any} req
  939. * @param {any} res
  940. * @param {any} form
  941. */
  942. function saveSetting(req, res, form) {
  943. Config.updateNamespaceByArray('crowi', form, function(err, config) {
  944. Config.updateConfigCache('crowi', config);
  945. return res.json({status: true});
  946. });
  947. }
  948. /**
  949. * save settings, update config cache ONLY. (this method don't response json)
  950. *
  951. * @param {any} form
  952. * @returns
  953. */
  954. function saveSettingAsync(form) {
  955. return new Promise((resolve, reject) => {
  956. Config.updateNamespaceByArray('crowi', form, (err, config) => {
  957. if (err) {
  958. return reject(err);
  959. }
  960. Config.updateConfigCache('crowi', config);
  961. return resolve();
  962. });
  963. });
  964. }
  965. function validateMailSetting(req, form, callback) {
  966. var mailer = crowi.mailer;
  967. var option = {
  968. host: form['mail:smtpHost'],
  969. port: form['mail:smtpPort'],
  970. };
  971. if (form['mail:smtpUser'] && form['mail:smtpPassword']) {
  972. option.auth = {
  973. user: form['mail:smtpUser'],
  974. pass: form['mail:smtpPassword'],
  975. };
  976. }
  977. if (option.port === 465) {
  978. option.secure = true;
  979. }
  980. var smtpClient = mailer.createSMTPClient(option);
  981. debug('mailer setup for validate SMTP setting', smtpClient);
  982. smtpClient.sendMail({
  983. to: req.user.email,
  984. subject: 'Wiki管理設定のアップデートによるメール通知',
  985. text: 'このメールは、WikiのSMTP設定のアップデートにより送信されています。'
  986. }, callback);
  987. }
  988. return actions;
  989. };