admin.js 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051
  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. UserGroup.removeCompletelyById(id)
  704. .then(() => {
  705. req.flash('successMessage', '削除しました');
  706. return res.redirect('/admin/user-groups');
  707. })
  708. .catch((err) => {
  709. debug('Error while removing userGroup.', err, id);
  710. req.flash('errorMessage', '完全な削除に失敗しました。');
  711. return res.redirect('/admin/user-groups');
  712. });
  713. };
  714. actions.userGroupRelation = {};
  715. actions.userGroupRelation.index = function(req, res) {
  716. };
  717. actions.userGroupRelation.create = function(req, res) {
  718. const User = crowi.model('User');
  719. const UserGroup = crowi.model('UserGroup');
  720. const UserGroupRelation = crowi.model('UserGroupRelation');
  721. // req params
  722. const userName = req.body.user_name;
  723. const userGroupId = req.body.user_group_id;
  724. let user = null;
  725. let userGroup = null;
  726. Promise.all([
  727. // ユーザグループをIDで検索
  728. UserGroup.findById(userGroupId),
  729. // ユーザを名前で検索
  730. User.findUserByUsername(userName),
  731. ])
  732. .then((resolves) => {
  733. userGroup = resolves[0];
  734. user = resolves[1];
  735. // Relation を作成
  736. UserGroupRelation.createRelation(userGroup, user);
  737. })
  738. .then((result) => {
  739. return res.redirect('/admin/user-group-detail/' + userGroup.name);
  740. }).catch((err) => {
  741. debug('Error on create user-group relation', err);
  742. req.flash('errorMessage', 'Error on create user-group relation');
  743. return res.redirect('/admin/user-group-detail/' + userGroup.name);
  744. });
  745. };
  746. actions.userGroupRelation.remove = function(req, res) {
  747. const UserGroupRelation = crowi.model('UserGroupRelation');
  748. var name = req.params.name;
  749. var relationId = req.params.relationId;
  750. debug(name, relationId);
  751. UserGroupRelation.removeById(relationId)
  752. .then(() =>{
  753. return res.redirect('/admin/user-group-detail/' + name);
  754. })
  755. .catch((err) => {
  756. debug('Error on remove user-group-relation', err);
  757. req.flash('errorMessage', 'グループのユーザ削除に失敗しました。');
  758. });
  759. };
  760. actions.api = {};
  761. actions.api.appSetting = function(req, res) {
  762. var form = req.form.settingForm;
  763. if (req.form.isValid) {
  764. debug('form content', form);
  765. // mail setting ならここで validation
  766. if (form['mail:from']) {
  767. validateMailSetting(req, form, function(err, data) {
  768. debug('Error validate mail setting: ', err, data);
  769. if (err) {
  770. req.form.errors.push('SMTPを利用したテストメール送信に失敗しました。設定をみなおしてください。');
  771. return res.json({status: false, message: req.form.errors.join('\n')});
  772. }
  773. return saveSetting(req, res, form);
  774. });
  775. }
  776. else {
  777. return saveSetting(req, res, form);
  778. }
  779. }
  780. else {
  781. return res.json({status: false, message: req.form.errors.join('\n')});
  782. }
  783. };
  784. actions.api.securitySetting = function(req, res) {
  785. const form = req.form.settingForm;
  786. if (req.form.isValid) {
  787. debug('form content', form);
  788. return saveSetting(req, res, form);
  789. }
  790. else {
  791. return res.json({status: false, message: req.form.errors.join('\n')});
  792. }
  793. };
  794. actions.api.securityPassportLdapSetting = function(req, res) {
  795. var form = req.form.settingForm;
  796. if (!req.form.isValid) {
  797. return res.json({status: false, message: req.form.errors.join('\n')});
  798. }
  799. debug('form content', form);
  800. return saveSettingAsync(form)
  801. .then(() => {
  802. const config = crowi.getConfig();
  803. // reset strategy
  804. crowi.passportService.resetLdapStrategy();
  805. // setup strategy
  806. if (Config.isEnabledPassportLdap(config)) {
  807. crowi.passportService.setupLdapStrategy(true);
  808. }
  809. return;
  810. })
  811. .then(() => {
  812. res.json({status: true});
  813. });
  814. };
  815. actions.api.customizeSetting = function(req, res) {
  816. var form = req.form.settingForm;
  817. if (req.form.isValid) {
  818. debug('form content', form);
  819. return saveSetting(req, res, form);
  820. }
  821. else {
  822. return res.json({status: false, message: req.form.errors.join('\n')});
  823. }
  824. };
  825. // app.post('/_api/admin/notifications.add' , admin.api.notificationAdd);
  826. actions.api.notificationAdd = function(req, res) {
  827. var UpdatePost = crowi.model('UpdatePost');
  828. var pathPattern = req.body.pathPattern;
  829. var channel = req.body.channel;
  830. debug('notification.add', pathPattern, channel);
  831. UpdatePost.create(pathPattern, channel, req.user)
  832. .then(function(doc) {
  833. debug('Successfully save updatePost', doc);
  834. // fixme: うーん
  835. doc.creator = doc.creator._id.toString();
  836. return res.json(ApiResponse.success({updatePost: doc}));
  837. }).catch(function(err) {
  838. debug('Failed to save updatePost', err);
  839. return res.json(ApiResponse.error());
  840. });
  841. };
  842. // app.post('/_api/admin/notifications.remove' , admin.api.notificationRemove);
  843. actions.api.notificationRemove = function(req, res) {
  844. var UpdatePost = crowi.model('UpdatePost');
  845. var id = req.body.id;
  846. UpdatePost.remove(id)
  847. .then(function() {
  848. debug('Successfully remove updatePost');
  849. return res.json(ApiResponse.success({}));
  850. }).catch(function(err) {
  851. debug('Failed to remove updatePost', err);
  852. return res.json(ApiResponse.error());
  853. });
  854. };
  855. // app.get('/_api/admin/users.search' , admin.api.userSearch);
  856. actions.api.usersSearch = function(req, res) {
  857. const User = crowi.model('User');
  858. const email =req.query.email;
  859. User.findUsersByPartOfEmail(email, {})
  860. .then(users => {
  861. const result = {
  862. data: users
  863. };
  864. return res.json(ApiResponse.success(result));
  865. }).catch(err => {
  866. return res.json(ApiResponse.error());
  867. });
  868. };
  869. /**
  870. * save settings, update config cache, and response json
  871. *
  872. * @param {any} req
  873. * @param {any} res
  874. * @param {any} form
  875. */
  876. function saveSetting(req, res, form) {
  877. Config.updateNamespaceByArray('crowi', form, function(err, config) {
  878. Config.updateConfigCache('crowi', config);
  879. return res.json({status: true});
  880. });
  881. }
  882. /**
  883. * save settings, update config cache ONLY. (this method don't response json)
  884. *
  885. * @param {any} form
  886. * @returns
  887. */
  888. function saveSettingAsync(form) {
  889. return new Promise((resolve, reject) => {
  890. Config.updateNamespaceByArray('crowi', form, (err, config) => {
  891. if (err) {
  892. return reject(err);
  893. }
  894. Config.updateConfigCache('crowi', config);
  895. return resolve();
  896. });
  897. });
  898. }
  899. function validateMailSetting(req, form, callback) {
  900. var mailer = crowi.mailer;
  901. var option = {
  902. host: form['mail:smtpHost'],
  903. port: form['mail:smtpPort'],
  904. };
  905. if (form['mail:smtpUser'] && form['mail:smtpPassword']) {
  906. option.auth = {
  907. user: form['mail:smtpUser'],
  908. pass: form['mail:smtpPassword'],
  909. };
  910. }
  911. if (option.port === 465) {
  912. option.secure = true;
  913. }
  914. var smtpClient = mailer.createSMTPClient(option);
  915. debug('mailer setup for validate SMTP setting', smtpClient);
  916. smtpClient.sendMail({
  917. to: req.user.email,
  918. subject: 'Wiki管理設定のアップデートによるメール通知',
  919. text: 'このメールは、WikiのSMTP設定のアップデートにより送信されています。'
  920. }, callback);
  921. }
  922. return actions;
  923. };