manage.tpl 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005
  1. <div id="oxSuccess" class="alert alert-success w-hidden">
  2. <i class="fas fa-check fa-fw"></i>
  3. <span></span>
  4. </div>
  5. <div id="oxLoadError" class="alert alert-danger w-hidden">
  6. <i class="fas fa-times fa-fw"></i>
  7. <span></span>
  8. </div>
  9. <div class="card mb-4">
  10. <div class="card-body">
  11. <h3 class="card-title">{lang key='store.ox.manage'}</h3>
  12. <p>{lang key='ox.intro'}</p>
  13. <p>{lang key='ox.alias.intro'}</p>
  14. <div class="btn-toolbar justify-content-between mb-1" role="toolbar">
  15. <div class="btn-group" role="group">
  16. <form action="{$upgradeUrl}" method="post">
  17. <input type="hidden" name="isproduct" value="{$isService}">
  18. <input type="hidden" name="serviceid" value="{$model->id}">
  19. <button type="submit" class="btn btn-secondary btn-sm">
  20. <i class="fal fa-arrow-circle-up fa-fw"></i>
  21. {lang key='upgrade'}
  22. </button>
  23. </form>
  24. </div>
  25. <div class="btn-group btn-group-sm" role="group">
  26. <button id="btnAddAccount" type="button" class="btn btn-primary btn-sm">
  27. <i class="fal fa-plus"></i>
  28. {lang key='ox.createUser'}
  29. </button>
  30. <button id="btnRefresh" type="button" class="btn btn-sm btn-default disabled" disabled="disabled">
  31. <i class="fas fa-sync-alt fa-spin" aria-hidden="true"></i>
  32. {lang key='ox.refresh'}
  33. </button>
  34. </div>
  35. </div>
  36. <div id="accountCount">{lang key='ox.accountCount' number='-' limit=$model->qty}</div>
  37. <table class="ox-table-accounts table" style="display: table">
  38. <thead>
  39. <tr>
  40. <th>{lang key='ox.emailAddress'}</th>
  41. <th class="w-25">{lang key='ox.mailboxSize'}</th>
  42. <th class="w-50 invisible"></th>
  43. </tr>
  44. </thead>
  45. <tbody>
  46. <tr class="loading">
  47. <td class="text-center" colspan="4">
  48. <i class="fas fa-sync-alt fa-spin" aria-hidden="true"></i>
  49. {lang key='loading'}
  50. </td>
  51. </tr>
  52. <tr class="no-accounts w-hidden">
  53. <td class="text-center" colspan="4">{lang key='ox.noAccounts'}</td>
  54. </tr>
  55. </tbody>
  56. <tbody class="cloneAccountsBody w-hidden"
  57. data-account-id=""
  58. data-account=""
  59. data-first-name=""
  60. data-last-name=""
  61. data-display-name=""
  62. >
  63. <tr>
  64. <td>
  65. <span class="account"></span>@{$domain}<br/>
  66. <i class="fas fa-chevron-right fa-xs fa-fw"></i>
  67. <span class="email-aliases">
  68. {lang key='ox.alias.emailAliases'}: <span class="alias-count"></span>
  69. </span>
  70. </td>
  71. <td><span class="limit"></span>GB</td>
  72. <td class="text-right">
  73. <div class="d-inline d-md-none">
  74. <div class="btn-group" role="group">
  75. <button type="button" class="btn btn-default btn-sm dropdown-toggle dropdown-hamburger" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
  76. <i class="fas fa-bars"></i>
  77. </button>
  78. <ul class="dropdown-menu">
  79. <li><a href="#" class="dropdown-item manage-account">{lang key='ox.manageAccount'}</a></li>
  80. <li><a href="#" class="dropdown-item set-ox-password">{lang key='ox.setPassword'}</a></li>
  81. <li><a href="#" class="dropdown-item list-group-item-action list-group-item-danger ox-delete">{lang key='ox.delete'}</a></li>
  82. </ul>
  83. </div>
  84. </div>
  85. <div class="d-none d-md-inline d-xl-none">
  86. <div class="btn-group" role="group">
  87. <button class="btn btn-default btn-sm manage-account">{lang key='ox.manageAccount'}</button>
  88. <button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
  89. <ul class="dropdown-menu">
  90. <li><a href="#" class="dropdown-item set-ox-password">{lang key='ox.setPassword'}</a></li>
  91. <li><a href="#" class="dropdown-item list-group-item-action list-group-item-danger ox-delete">{lang key='ox.delete'}</a></li>
  92. </ul>
  93. </div>
  94. </div>
  95. <div class="d-none d-xl-inline">
  96. <div class="btn-group" role="group">
  97. <button class="btn btn-default btn-sm manage-account">{lang key='ox.manageAccount'}</button>
  98. <button class="btn btn-default btn-sm set-ox-password">{lang key='ox.setPassword'}</button>
  99. <button class="btn btn-danger btn-sm ox-delete">{lang key='ox.delete'}</button>
  100. </div>
  101. </div>
  102. </td>
  103. </tr>
  104. </tbody>
  105. <tbody class="cloneAliasesBody w-hidden" data-account-id="">
  106. <tr>
  107. <td colspan="3">
  108. <table class="table">
  109. <tr class="create-alias" data-alias="">
  110. <td>
  111. <div class="input-group input-group-xs">
  112. <input type="text" name="alias" class="form-control">
  113. <div class="input-group-append">
  114. <span class="input-group-text">
  115. @{$domain}
  116. </span>
  117. <button class="btn btn-primary btn-xs ox-create-alias">
  118. <span class="loader w-hidden">
  119. <i class="far fa-sync-alt fa-spin" aria-hidden="true"></i>
  120. </span>
  121. <span class="create-string">
  122. {lang key='ox.alias.createButton'}
  123. </span>
  124. </button>
  125. </div>
  126. </div>
  127. </td>
  128. <td colspan="2">&nbsp;</td>
  129. </tr>
  130. </table>
  131. </td>
  132. </tr>
  133. </tbody>
  134. <tr class="cloneAliasRow w-hidden"
  135. data-alias=""
  136. >
  137. <td colspan="2"><span class="alias"></span>@{$domain}</td>
  138. <td class="text-right">
  139. <div class="btn-group" role="group">
  140. <button class="btn btn-default btn-xs ox-delete-alias">{lang key='ox.delete'}</button>
  141. </div>
  142. </td>
  143. </tr>
  144. </table>
  145. </div>
  146. </div>
  147. <ul class="nav nav-tabs responsive-tabs-sm">
  148. <li class="nav-item">
  149. <a href="#retrievalsettings" class="nav-link active" data-toggle="tab">
  150. <i class="far fa-envelope fa-fw"></i>
  151. {lang key='ox.settings.retrieval'}
  152. </a>
  153. </li>
  154. <li class="nav-item">
  155. <a href="#davsettings" class="nav-link" data-toggle="tab">
  156. <i class="far fa-calendar-alt fa-fw"></i>
  157. {lang key='ox.settings.davSettings'}
  158. </a>
  159. </li>
  160. <li class="nav-item">
  161. <a href="#usage" class="nav-link" data-toggle="tab">
  162. <i class="far fa-file-alt fa-fw"></i>
  163. {lang key='ox.settings.usageInstructions'}
  164. </a>
  165. </li>
  166. <li class="nav-item w-hidden" id="migrationNav">
  167. <a href="#migration" class="nav-link" data-toggle="tab">
  168. <i class="far fa-file-alt fa-fw"></i>
  169. {lang key='ox.settings.migrationTitle'}
  170. </a>
  171. </li>
  172. </ul>
  173. <div class="responsive-tabs-sm-connector"><div class="channel"></div><div class="bottom-border"></div></div>
  174. <div class="tab-content product-details-tab-container w-text-09 mb-4">
  175. <div class="tab-pane fade show active" id="retrievalsettings">
  176. <p>{lang key='ox.settings.retrievalIntro'}</p>
  177. <table class="table">
  178. <tbody>
  179. <tr>
  180. <td>{lang key='ox.settings.username'}</td>
  181. <td>{lang key='ox.settings.email' domain=$domain}</td>
  182. </tr>
  183. <tr>
  184. <td>{lang key='clientareapassword'}</td>
  185. <td>{lang key='ox.settings.password'}</td>
  186. </tr>
  187. <tr>
  188. <td>{lang key='ox.settings.pop'}</td>
  189. <td>
  190. <span class="pop-hostname">
  191. <i class="fas fa-sync-alt fa-spin" aria-hidden="true"></i>
  192. {lang key='loading'}
  193. </span><br>
  194. <span class="pop-port"></span>
  195. </td>
  196. </tr>
  197. <tr>
  198. <td>{lang key='ox.settings.incoming'}</td>
  199. <td>
  200. <span class="incoming-hostname">
  201. <i class="fas fa-sync-alt fa-spin" aria-hidden="true"></i>
  202. {lang key='loading'}
  203. </span><br>
  204. <span class="incoming-port"></span>
  205. </td>
  206. </tr>
  207. <tr>
  208. <td>{lang key='ox.settings.outgoing'}</td>
  209. <td>
  210. <span class="outgoing-hostname">
  211. <i class="fas fa-sync-alt fa-spin" aria-hidden="true"></i>
  212. {lang key='loading'}
  213. </span><br>
  214. <span class="outgoing-port"></span>
  215. </td>
  216. </tr>
  217. </tbody>
  218. </table>
  219. </div>
  220. <div class="tab-pane fade" id="davsettings">
  221. <p>{lang key='ox.settings.davSettingsIntro'}</p>
  222. <table class="table">
  223. <tbody>
  224. <tr>
  225. <td>{lang key='ox.settings.username'}</td>
  226. <td>{lang key='ox.settings.email' domain=$domain}</td>
  227. </tr>
  228. <tr>
  229. <td>{lang key='clientareapassword'}</td>
  230. <td>{lang key='ox.settings.caldavPassword'}</td>
  231. </tr>
  232. <tr>
  233. <td>{lang key='ox.settings.serverUrl'}</td>
  234. <td>
  235. <span class="calendar-server">
  236. <i class="fas fa-sync-alt fa-spin" aria-hidden="true"></i>
  237. {lang key='loading'}
  238. </span>
  239. </td>
  240. </tr>
  241. </tbody>
  242. </table>
  243. </div>
  244. <div class="tab-pane fade" id="usage">
  245. <table class="table">
  246. <thead id="tablePanelUsageHead">
  247. <tr class="loading">
  248. <td>
  249. <i class="fas fa-sync-alt fa-spin" aria-hidden="true"></i>
  250. {lang key='loading'}
  251. </td>
  252. </tr>
  253. </thead>
  254. <tbody id="tablePanelUsage">
  255. </tbody>
  256. </table>
  257. </div>
  258. <div class="tab-pane fade w-hidden" id="migration">
  259. <p>{lang key="ox.settings.migrationIntro"}</p>
  260. <div>
  261. <a class="migration-url btn btn-info btn-block btn-lg" href="#" target="_blank"
  262. >{lang key="ox.settings.migrationLaunch"}</a>
  263. </div>
  264. </div>
  265. </div>
  266. <form id="frmOxAddAccount" action="{$addAccountUrl}" method="post">
  267. <div class="modal fade" id="modalAddAccount">
  268. <div class="modal-dialog modal-lg">
  269. <div class="modal-content">
  270. <div class="modal-header card-header bg-primary text-light">
  271. <h4 class="modal-title" id="modalAddAccountTitle">
  272. {lang key='ox.createUser'}
  273. </h4>
  274. <button type="button" class="close" data-dismiss="modal" aria-label="{lang key='close'}"><span aria-hidden="true">&times;</span></button>
  275. </div>
  276. <div class="modal-body card-body" id="modalSetPasswordBody">
  277. <div class="alert alert-danger modal-error w-hidden">
  278. <i class="fas fa-times fa-fw"></i>
  279. <span></span>
  280. </div>
  281. <div class="row">
  282. <div class="col-md-6">
  283. <div class="form-group">
  284. <label for="inputFirstName">{lang key='orderForm.firstName'}</label>
  285. <input id="inputFirstName" type="text" autocomplete="off" class="form-control required" name="first">
  286. <span class="field-error-msg">{lang key='ox.required.firstName'}</span>
  287. </div>
  288. </div>
  289. <div class="col-md-6">
  290. <div class="form-group">
  291. <label for="inputLastName">{lang key='orderForm.lastName'}</label>
  292. <input id="inputLastName" type="text" autocomplete="off" class="form-control required" name="last">
  293. <span class="field-error-msg">{lang key='ox.required.lastName'}</span>
  294. </div>
  295. </div>
  296. </div>
  297. <div class="row">
  298. <div class="col-md-6">
  299. <div class="form-group">
  300. <label for="inputDisplayName">{lang key='ox.displayName'}</label>
  301. <input id="inputDisplayName" type="text" autocomplete="off" class="form-control required" name="display">
  302. <span class="field-error-msg">{lang key='ox.required.displayName'}</span>
  303. </div>
  304. </div>
  305. <div class="col-md-6">
  306. <div class="form-group">
  307. <label for="inputEmailAddress">{lang key='orderForm.emailAddress'}</label>
  308. <div class="input-group">
  309. <input id="inputEmailAddress" type="text" autocomplete="off" class="form-control required" name="email">
  310. <div class="input-group-append"><span class="input-group-text">@{$domain}</span></div>
  311. </div>
  312. <span class="field-error-msg">{lang key='ox.required.email'}</span>
  313. </div>
  314. </div>
  315. </div>
  316. <div class="row">
  317. <div class="col-md-6">
  318. <div class="form-group">
  319. <label for="inputPassword">{lang key='clientareapassword'}</label>
  320. <div class="input-group">
  321. <input type="password" class="form-control required" name="password" id="inputPassword" data-error-threshold="{$pwStrengthErrorThreshold}" data-warning-threshold="{$pwStrengthWarningThreshold}" placeholder="{lang key="loginpassword"}" autocomplete="off" />
  322. <div class="input-group-append">
  323. <button type="button" class="btn btn-default generate-password" data-targetfields="inputPassword,inputConfirmPassword">
  324. {lang key="generatePassword.btnShort"}
  325. </button>
  326. </div>
  327. </div>
  328. <span class="field-error-msg">{lang key='ox.required.password'}</span>
  329. <div class="password-strength-meter">
  330. <div class="progress" style="height: 10px; margin-top: 10px">
  331. <div class="progress-bar bg-success bg-striped" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" id="passwordStrengthMeterBar">
  332. </div>
  333. </div>
  334. <p class="text-center small text-muted" id="passwordStrengthTextLabel">{lang key="pwstrength"}: {lang key="pwstrengthenter"}</p>
  335. </div>
  336. </div>
  337. </div>
  338. <div class="col-md-6">
  339. <div class="form-group">
  340. <label for="inputConfirmPassword">{lang key='clientareaconfirmpassword'}</label>
  341. <input id="inputConfirmPassword" type="password" autocomplete="off" class="form-control required match" data-match-field="inputPassword" name="password2">
  342. <span class="field-error-msg">{lang key='ox.required.passwordMatch'}</span>
  343. </div>
  344. </div>
  345. </div>
  346. </div>
  347. <div class="modal-footer card-footer">
  348. <div class="float-left loader w-hidden">
  349. <i class="fas fa-sync-alt fa-spin"></i>
  350. {lang key='loading'}
  351. </div>
  352. <button type="button" class="btn btn-default" data-dismiss="modal">
  353. {lang key='close'}
  354. </button>
  355. <button type="button" class="btn btn-primary ox-submit-button">
  356. {lang key='orderForm.add'}
  357. </button>
  358. </div>
  359. </div>
  360. </div>
  361. </div>
  362. </form>
  363. <form id="frmOxManageAccount" action="{$manageAccountUrl}" method="post">
  364. <div class="modal fade" id="modalManageAccount">
  365. <input type="hidden" value="" name="account">
  366. <div class="modal-dialog">
  367. <div class="modal-content">
  368. <div class="modal-header card-header bg-primary text-light">
  369. <h4 class="modal-title" id="modalManageAccountTitle">
  370. {lang key='ox.manageAccount'}
  371. </h4>
  372. <button type="button" class="close" data-dismiss="modal" aria-label="{lang key='close'}"><span aria-hidden="true">&times;</span></button>
  373. </div>
  374. <div class="modal-body card-body" id="modalSetPasswordBody">
  375. <div class="alert alert-danger modal-error w-hidden">
  376. <i class="fas fa-times fa-fw"></i>
  377. <span></span>
  378. </div>
  379. <div class="form-group">
  380. <label for="inputManageDisplayName">{lang key='ox.displayName'}</label>
  381. <input id="inputManageDisplayName" type="text" autocomplete="off" class="form-control required" name="display">
  382. <span class="field-error-msg">{lang key='ox.required.displayName'}</span>
  383. </div>
  384. <div class="form-group">
  385. <label for="inputManageFirstName">{lang key='orderForm.firstName'}</label>
  386. <input id="inputManageFirstName" type="text" autocomplete="off" class="form-control required" name="first">
  387. <span class="field-error-msg">{lang key='ox.required.firstName'}</span>
  388. </div>
  389. <div class="form-group">
  390. <label for="inputManageLastName">{lang key='orderForm.lastName'}</label>
  391. <input id="inputManageLastName" type="text" autocomplete="off" class="form-control required" name="last">
  392. <span class="field-error-msg">{lang key='ox.required.lastName'}</span>
  393. </div>
  394. </div>
  395. <div class="modal-footer card-footer">
  396. <div class="float-left loader w-hidden">
  397. <i class="fas fa-sync-alt fa-spin"></i>
  398. {lang key='loading'}
  399. </div>
  400. <button type="button" class="btn btn-default" data-dismiss="modal">
  401. {lang key='close'}
  402. </button>
  403. <button type="button" class="btn btn-primary ox-submit-button">
  404. {lang key='clientareasavechanges'}
  405. </button>
  406. </div>
  407. </div>
  408. </div>
  409. </div>
  410. </form>
  411. <form id="frmOxSetPassword" action="{$setPasswordUrl}" method="post">
  412. <div class="modal fade" id="modalSetPassword">
  413. <input type="hidden" value="" name="account">
  414. <div class="modal-dialog">
  415. <div class="modal-content">
  416. <div class="modal-header card-header bg-primary text-light">
  417. <h4 class="modal-title" id="modalSetPasswordTitle">
  418. {lang key='ox.setPasswordFor'}
  419. </h4>
  420. <button type="button" class="close" data-dismiss="modal" aria-label="{lang key='close'}"><span aria-hidden="true">&times;</span></button>
  421. </div>
  422. <div class="modal-body card-body" id="modalSetPasswordBody">
  423. <div class="alert alert-danger modal-error w-hidden">
  424. <i class="fas fa-times fa-fw"></i>
  425. <span></span>
  426. </div>
  427. <div class="form-group">
  428. <label for="inputChangePassword">{lang key='clientareapassword'}</label>
  429. <div class="input-group">
  430. <input type="password" class="form-control required" name="password" id="inputChangePassword" data-error-threshold="{$pwStrengthErrorThreshold}" data-warning-threshold="{$pwStrengthWarningThreshold}" placeholder="{lang key="loginpassword"}" autocomplete="off" />
  431. <div class="input-group-append">
  432. <button type="button" class="btn btn-default generate-password" data-targetfields="inputChangePassword,inputChangePasswordConfirm">
  433. {lang key="generatePassword.btnShort"}
  434. </button>
  435. </div>
  436. </div>
  437. <div class="password-strength-meter">
  438. <div class="progress" style="height: 10px; margin-top: 10px">
  439. <div class="progress-bar bg-success bg-striped" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" id="passwordStrengthMeterBar">
  440. </div>
  441. </div>
  442. <p class="text-center small text-muted" id="passwordStrengthTextLabel">{lang key="pwstrength"}: {lang key="pwstrengthenter"}</p>
  443. </div>
  444. <span class="field-error-msg">{lang key='ox.required.password'}</span>
  445. </div>
  446. <div class="form-group">
  447. <label for="inputChangePasswordConfirm">{lang key='clientareaconfirmpassword'}</label>
  448. <input id="inputChangePasswordConfirm" type="password" autocomplete="off" class="form-control required match" data-match-field="inputChangePassword" name="password2">
  449. <span class="field-error-msg">{lang key='ox.required.passwordMatch'}</span>
  450. </div>
  451. </div>
  452. <div class="modal-footer card-footer">
  453. <div class="float-left loader w-hidden">
  454. <i class="fas fa-sync-alt fa-spin"></i>
  455. {lang key='loading'}
  456. </div>
  457. <button type="button" class="btn btn-default" data-dismiss="modal">
  458. {lang key='close'}
  459. </button>
  460. <button type="button" class="btn btn-primary ox-submit-button">
  461. {lang key='ox.setPassword'}
  462. </button>
  463. </div>
  464. </div>
  465. </div>
  466. </div>
  467. </form>
  468. <form id="frmOxDeleteAccount" action="{$deleteAccountUrl}" method="post">
  469. <div class="modal fade" id="modalDeleteAccount">
  470. <input type="hidden" value="" name="account">
  471. <div class="modal-dialog">
  472. <div class="modal-content">
  473. <div class="modal-header card-header bg-primary text-light">
  474. <h4 class="modal-title" id="modalDeleteAccountTitle">
  475. {lang key='ox.deleteAccount'}
  476. </h4>
  477. <button type="button" class="close" data-dismiss="modal" aria-label="{lang key='close'}"><span aria-hidden="true">&times;</span></button>
  478. </div>
  479. <div class="modal-body card-body" id="modalDeleteAccountBody">
  480. <div class="alert alert-danger modal-error w-hidden">
  481. <i class="fas fa-times fa-fw"></i>
  482. <span></span>
  483. </div>
  484. <span class="delete-question" aria-hidden="true">{lang key='ox.deleteAccountQuestion'}</span>
  485. <span class="delete-aliases-question" aria-hidden="true">{lang key='ox.deleteAccountWithAliasesQuestion'}</span>
  486. </div>
  487. <div class="modal-footer card-footer">
  488. <div class="float-left loader w-hidden">
  489. <i class="fas fa-sync-alt fa-spin"></i>
  490. {lang key='loading'}
  491. </div>
  492. <button type="button" class="btn btn-default" data-dismiss="modal">
  493. {lang key='close'}
  494. </button>
  495. <button type="button" class="btn btn-danger ox-submit-button" id="btnDoDelete">
  496. {lang key='ox.delete'}
  497. </button>
  498. </div>
  499. </div>
  500. </div>
  501. </div>
  502. </form>
  503. <form id="frmOxDeleteAlias" action="{$modifyAliasesUrl}" method="post">
  504. <div class="modal fade" id="modalDeleteAlias">
  505. <input type="hidden" value="" name="aliasAction">
  506. <input type="hidden" value="" name="account">
  507. <input type="hidden" value="" name="alias">
  508. <input type="hidden" value="" name="aliases">
  509. <div class="modal-dialog">
  510. <div class="modal-content">
  511. <div class="modal-header card-header bg-primary text-light">
  512. <h4 class="modal-title" id="modalDeleteAccountTitle">
  513. {lang key='ox.alias.deleteTitle' domain=$domain}
  514. </h4>
  515. <button type="button" class="close" data-dismiss="modal" aria-label="{lang key='close'}"><span aria-hidden="true">&times;</span></button>
  516. </div>
  517. <div class="modal-body card-body" id="modalDeleteAccountBody">
  518. <div class="alert alert-danger modal-error w-hidden">
  519. <i class="fas fa-times fa-fw"></i>
  520. <span></span>
  521. </div>
  522. {lang key='ox.alias.deleteQuestion'}
  523. </div>
  524. <div class="modal-footer card-footer">
  525. <div class="float-left loader w-hidden">
  526. <i class="fas fa-sync-alt fa-spin"></i>
  527. {lang key='loading'}
  528. </div>
  529. <button type="button" class="btn btn-default" data-dismiss="modal">
  530. {lang key='close'}
  531. </button>
  532. <button type="button" class="btn btn-danger ox-submit-button" id="btnDoDelete">
  533. {lang key='ox.delete'}
  534. </button>
  535. </div>
  536. </div>
  537. </div>
  538. </div>
  539. </form>
  540. <script src="{$BASE_PATH_JS}/PasswordStrength.js"></script>
  541. <script>
  542. function loadAccounts(force) {
  543. var trLoading = jQuery('tr.loading'),
  544. divError = jQuery('#oxLoadError'),
  545. trNoAccounts = jQuery('tr.no-accounts');
  546. force = force || 0;
  547. if (divError.is(':visible')) {
  548. divError.hide();
  549. }
  550. jQuery('#btnRefresh').addClass('disabled')
  551. .prop('disabled', 'disabled')
  552. .find('i')
  553. .addClass('fa-spin');
  554. clearAccounts();
  555. clearAliases();
  556. trNoAccounts.hide();
  557. if (trLoading.not(':visible')) {
  558. trLoading.show();
  559. }
  560. WHMCS.http.jqClient.jsonPost({
  561. url: '{$listAccountsUrl}',
  562. data: {
  563. token: csrfToken,
  564. force: force
  565. },
  566. success: function(data) {
  567. if (data.failedMessage) {
  568. jQuery('#oxLoadError').text(data.failedMessage).show();
  569. } else {
  570. if (typeof data.accounts.length === "undefined" || data.accounts.length === 0) {
  571. trNoAccounts.show();
  572. } else {
  573. jQuery.each(data.accounts, function(index, account) {
  574. addAccountToTable(account);
  575. addAliasesToTable(account);
  576. updateAliasesDisplay(account.id);
  577. });
  578. }
  579. }
  580. },
  581. always: function() {
  582. trLoading.hide();
  583. updateAccountsDisplay();
  584. jQuery('#btnRefresh').removeClass('disabled').prop('disabled', false)
  585. .find('i').removeClass('fa-spin');
  586. }
  587. });
  588. }
  589. function addAccountToTable(data) {
  590. var table = jQuery('table.ox-table-accounts'),
  591. clone = jQuery('tbody.cloneAccountsBody').clone();
  592. clone.attr('id', '');
  593. clone.find('.account')
  594. .text(data.username);
  595. clone.find('.limit')
  596. .text(data.quota);
  597. clone.attr('data-account-id', data.id)
  598. .attr('data-first-name', data.first_name)
  599. .attr('data-last-name', data.last_name)
  600. .attr('data-display-name', data.display_name)
  601. .attr('data-account', data.username + '@{$domain}');
  602. clone.addClass('account-entry').removeClass('cloneAccountsBody').show();
  603. table.append(clone);
  604. }
  605. function addAliasesToTable(data) {
  606. var table = jQuery('table.ox-table-accounts'),
  607. cloneTbody = jQuery('tbody.cloneAliasesBody').clone();
  608. if (typeof data.aliases !== 'undefined' && data.aliases.length > 0) {
  609. data.aliases.forEach(function(alias) {
  610. var isPrimary = false;
  611. if (data.username.localeCompare(alias, 'en', { sensitivity: 'base' }) === 0) {
  612. isPrimary = true;
  613. }
  614. addAliasTr(alias, cloneTbody.find('table'), isPrimary);
  615. })
  616. }
  617. cloneTbody.attr('data-account-id', data.id).attr('id', 'tbodyAlias' + data.id);
  618. cloneTbody.removeClass('cloneAliasesBody w-hidden').addClass('aliases-body').hide();
  619. table.append(cloneTbody);
  620. }
  621. function addAliasTr(alias, cloneTbody, isPrimary = false) {
  622. var cloneTr = jQuery('tr.cloneAliasRow').clone();
  623. cloneTr.find('.alias')
  624. .text(alias);
  625. cloneTr.attr('data-alias', alias);
  626. cloneTr.addClass('alias-entry').removeClass('cloneAliasRow');
  627. if (isPrimary === false) {
  628. cloneTr.removeClass('w-hidden');
  629. }
  630. cloneTbody.prepend(cloneTr);
  631. }
  632. function clearAccounts() {
  633. jQuery('.ox-table-accounts tbody.account-entry').remove();
  634. }
  635. function clearAliases() {
  636. jQuery('.ox-table-accounts tbody.aliases-body').remove();
  637. }
  638. function updateAccountsDisplay() {
  639. var trNoAccounts = jQuery('table.ox-table-accounts tbody tr.no-accounts'),
  640. accountsCount = jQuery('#accountCount').find('.number');
  641. var count = jQuery('table.ox-table-accounts tbody.account-entry').length;
  642. if (count == 0) {
  643. trNoAccounts.show();
  644. } else {
  645. trNoAccounts.hide();
  646. }
  647. accountsCount.text(count);
  648. }
  649. function updateAliasesDisplay(accountId) {
  650. var aliasesCount = jQuery('tbody.account-entry[data-account-id="' + accountId + '"] span.alias-count');
  651. count = jQuery('tbody.aliases-body[data-account-id="' + accountId + '"] tr.alias-entry')
  652. .not('.w-hidden')
  653. .length;
  654. aliasesCount.text(count);
  655. }
  656. function loadConfiguration(force) {
  657. var tabNames = ['Usage'];
  658. var tabs = [];
  659. tabNames.forEach(function(tabName) {
  660. var tabSpec = {
  661. 'panel': jQuery('#tablePanel' + tabName),
  662. 'header': jQuery('#tablePanel' + tabName + 'Head'),
  663. };
  664. tabs.push(tabSpec);
  665. if (tabSpec.header.find('tr.loading').not(':visible')) {
  666. tabSpec.header.find('tr.loading').show();
  667. }
  668. });
  669. force = force || 0;
  670. WHMCS.http.jqClient.jsonPost({
  671. url: '{$configurationUrl}',
  672. data: {
  673. force: force,
  674. token: csrfToken
  675. },
  676. success: function(data) {
  677. var error = '{lang|addslashes key="unavailable"}';
  678. if (data.settings) {
  679. jQuery('.incoming-hostname').text(data.settings.incoming.hostname);
  680. jQuery('.incoming-port').text(data.settings.incoming.port);
  681. jQuery('.pop-hostname').text(data.settings.pop.hostname);
  682. jQuery('.pop-port').text(data.settings.pop.port);
  683. jQuery('.outgoing-hostname').text(data.settings.outgoing.hostname);
  684. jQuery('.outgoing-port').text(data.settings.outgoing.port);
  685. jQuery('.calendar-server').text(data.settings.calendar.hostname);
  686. } else {
  687. jQuery('.incoming-hostname').text(error);
  688. jQuery('.incoming-port').text(error);
  689. jQuery('.pop-hostname').text(error);
  690. jQuery('.pop-port').text(error);
  691. jQuery('.outgoing-hostname').text(error);
  692. jQuery('.outgoing-port').text(error);
  693. jQuery('.calendar-server').text(error);
  694. }
  695. if (data.usage) {
  696. var tabSpec = tabs[tabNames.indexOf('Usage')];
  697. tabSpec.panel.find('tr').remove();
  698. jQuery.each(data.usage, function (index, value) {
  699. tabSpec.panel.append(
  700. '<tr><td>' + value + '</td></tr>'
  701. );
  702. });
  703. }
  704. if (data.migration_tool) {
  705. jQuery('.migration-url').attr('href', data.migration_tool.url);
  706. jQuery('#migration, #migrationNav').removeClass('w-hidden');
  707. }
  708. },
  709. always: function() {
  710. tabs.forEach(function(tabSpec) {
  711. tabSpec.header.find('tr.loading').hide();
  712. });
  713. }
  714. });
  715. }
  716. jQuery(document).on('ready', function() {
  717. loadAccounts();
  718. loadConfiguration();
  719. window.langPasswordStrength = "{lang key="pwstrength"}";
  720. window.langPasswordWeak = "{lang key="pwstrengthweak"}";
  721. window.langPasswordModerate = "{lang key="pwstrengthmoderate"}";
  722. window.langPasswordStrong = "{lang key="pwstrengthstrong"}";
  723. jQuery("#inputPassword,#inputChangePassword").keyup(registerFormPasswordStrengthFeedback);
  724. jQuery('button.btn-add').width(function() {
  725. return jQuery(this).outerWidth(true);
  726. }).find('span.loading').toggleClass('loading').toggle();
  727. jQuery('#modalAddAccount,#modalSetPassword').on('shown.bs.modal', function() {
  728. jQuery('.modal-backdrop.fade.show').css('zIndex', 1030);
  729. jQuery(this).css('zIndex', 1035);
  730. });
  731. jQuery(document)
  732. .on('click', '#btnRefresh', function() {
  733. jQuery(this).addClass('disabled').prop('disabled', true);
  734. loadAccounts(true);
  735. loadConfiguration(true);
  736. })
  737. .on('click', '#btnAddAccount', function() {
  738. jQuery('#modalAddAccount').modal('show');
  739. })
  740. .on('click', 'tbody.account-entry', function() {
  741. var accountId = jQuery(this).data('account-id'),
  742. aliasesBody = jQuery('#tbodyAlias' + accountId);
  743. if (aliasesBody.is(':visible')) {
  744. jQuery(this).find('i.fa-chevron-right').attr(
  745. 'style',
  746. 'transform: rotate(0deg); transition: .1s transform ease-in-out;',
  747. );
  748. aliasesBody.hide();
  749. } else {
  750. jQuery(this).find('i.fa-chevron-right').attr(
  751. 'style',
  752. 'transform: rotate(90deg); transition: .1s transform ease-in-out;',
  753. );
  754. aliasesBody.show();
  755. }
  756. })
  757. .on('click', '.manage-account', function(e) {
  758. var tbody = jQuery(this).closest('tbody'),
  759. accountId = tbody.data('account-id'),
  760. account = tbody.data('account'),
  761. modalManageAccount = jQuery('#modalManageAccount');
  762. e.stopPropagation();
  763. modalManageAccount.find('input[name="account"]').val(accountId);
  764. modalManageAccount.find('span.email').text(account);
  765. jQuery('#inputManageDisplayName').val(tbody.attr('data-display-name'));
  766. jQuery('#inputManageFirstName').val(tbody.attr('data-first-name'));
  767. jQuery('#inputManageLastName').val(tbody.attr('data-last-name'));
  768. modalManageAccount.modal('show');
  769. })
  770. .on('click', '.set-ox-password', function(e) {
  771. var accountId = jQuery(this).closest('tbody').data('account-id'),
  772. account = jQuery(this).closest('tbody').data('account'),
  773. modalSetPassword = jQuery('#modalSetPassword');
  774. e.stopPropagation();
  775. modalSetPassword.find('input[name="account"]').val(accountId);
  776. modalSetPassword.find('span.email').text(account);
  777. modalSetPassword.modal('show');
  778. })
  779. .on('click', '.ox-delete', function(e) {
  780. var accountId = jQuery(this).closest('tbody.account-entry').data('account-id'),
  781. account = jQuery(this).closest('tbody.account-entry').data('account'),
  782. modalDeleteAccount = jQuery('#modalDeleteAccount'),
  783. aliasCount = jQuery(this).closest('tbody.account-entry').find('span.alias-count').text(),
  784. deleteAliasesSpan = jQuery('span.delete-aliases-question'),
  785. deleteAccountSpan = jQuery('span.delete-question');
  786. deleteAccountSpan.hide();
  787. deleteAliasesSpan.hide();
  788. if (aliasCount > 0) {
  789. deleteAliasesSpan.show();
  790. } else {
  791. deleteAccountSpan.show();
  792. }
  793. e.stopPropagation();
  794. modalDeleteAccount.find('input[name="account"]').val(accountId);
  795. modalDeleteAccount.find('span.count').text(aliasCount);
  796. modalDeleteAccount.find('span.email').text(account);
  797. modalDeleteAccount.modal('show');
  798. })
  799. .on('click', '.ox-create-alias', function() {
  800. var element = jQuery(this),
  801. oxError = jQuery('#oxLoadError'),
  802. oxSuccess = jQuery('#oxSuccess'),
  803. accountId = element.closest('tbody.aliases-body').data('account-id'),
  804. aliasTableTbody = element.closest('tbody'),
  805. alias = element.closest('tr.create-alias')
  806. .find('input[name="alias"]').val(),
  807. aliases = element.closest('tbody.aliases-body')
  808. .find('tr.alias-entry')
  809. .map(function(index, element) {
  810. return jQuery(element).data('alias') + '@{$domain}';
  811. }).get();
  812. if (typeof alias === 'undefined' && alias.length == 0) {
  813. return false;
  814. }
  815. element.css('min-width', element.outerWidth());
  816. element.find('span.create-string').hide();
  817. element.attr('disabled', 'disabled').addClass('disabled');
  818. element.find('span.loader').show();
  819. aliases.push(alias + '@{$domain}');
  820. oxError.hide().find('span').text('');
  821. oxSuccess.hide().find('span').text('');
  822. WHMCS.http.jqClient.jsonPost({
  823. url: '{$modifyAliasesUrl}',
  824. data: {
  825. token: csrfToken,
  826. aliasAction: 'create',
  827. account: accountId,
  828. aliases: JSON.stringify(aliases),
  829. },
  830. success: function(data) {
  831. if (data.success === true) {
  832. oxSuccess.find('span').text(data.successMessage);
  833. oxSuccess.show();
  834. addAliasTr(
  835. alias,
  836. aliasTableTbody
  837. );
  838. element.closest('tr.create-alias')
  839. .find('input[name="alias"]')
  840. .val('');
  841. } else {
  842. oxError.find('span').text(data.failedMessage);
  843. oxError.show();
  844. }
  845. },
  846. always: function() {
  847. element.find('span.loader').hide();
  848. element.find('span.create-string').show();
  849. element.removeAttr('disabled').removeClass('disabled');
  850. updateAliasesDisplay(accountId);
  851. }
  852. });
  853. })
  854. .on('click', '.ox-delete-alias', function() {
  855. var accountId = jQuery(this).closest('tbody.aliases-body').data('account-id'),
  856. alias = jQuery(this).closest('tr.alias-entry').data('alias'),
  857. aliases = jQuery(this).closest('tr.alias-entry')
  858. .siblings('tr.alias-entry')
  859. .map(function(index, element) {
  860. return jQuery(element).data('alias') + '@{$domain}';
  861. }).get(),
  862. modalDeleteAlias = jQuery('#modalDeleteAlias');
  863. modalDeleteAlias.find('.modal-error').hide().text(''),
  864. modalDeleteAlias.find('input[name="aliasAction"]').val('delete');
  865. modalDeleteAlias.find('input[name="account"]').val(accountId);
  866. modalDeleteAlias.find('input[name="alias"]').val(alias);
  867. modalDeleteAlias.find('input[name="aliases"]').val(JSON.stringify(aliases));
  868. modalDeleteAlias.find('span.alias').text(alias);
  869. modalDeleteAlias.modal('show');
  870. })
  871. .on('click', '.ox-submit-button', function() {
  872. var frm = jQuery(this).closest('form'),
  873. formId = frm.attr('id'),
  874. modal = frm.find('div.modal'),
  875. formData = frm.serialize(),
  876. accountId = frm.find('input[name="account"]').val(),
  877. oxError = modal.find('.modal-error'),
  878. oxLoadError = jQuery('#oxLoadError'),
  879. oxSuccess = jQuery('#oxSuccess'),
  880. err = false;
  881. frm.find('.form-group').removeClass('has-error');
  882. frm.find('.field-error-msg').hide();
  883. frm.find('.required').each(function() {
  884. if (jQuery(this).val() === '') {
  885. if (jQuery(this).hasClass('match')) {
  886. jQuery(this).setInputError("{lang key='ox.required.passwordMatchMissing'}");
  887. }
  888. jQuery(this).showInputError();
  889. err = true;
  890. }
  891. });
  892. frm.find('.match').each(function() {
  893. var matchInput = jQuery(this).data('match-field');
  894. if (jQuery('#' + matchInput).val() !== jQuery(this).val()) {
  895. jQuery(this).setInputError("{lang key='ox.required.passwordMatch'}").showInputError();
  896. err = true;
  897. }
  898. });
  899. if (formId === 'frmOxAddAccount') {
  900. var verify = jQuery('#inputConfirmPassword');
  901. if (jQuery('#inputPassword').val() !== verify.val()) {
  902. verify.showInputError();
  903. err = true;
  904. }
  905. }
  906. if (formId === 'frmOxSetPassword') {
  907. var verify2 = jQuery('#inputChangePasswordConfirm');
  908. if (jQuery('#inputChangePassword').val() !== verify2.val()) {
  909. verify2.showInputError();
  910. err = true;
  911. }
  912. }
  913. if (!err) {
  914. oxError.hide().find('span').text('');
  915. oxLoadError.hide();
  916. oxSuccess.hide();
  917. modal.find('.loader').show();
  918. WHMCS.http.jqClient.jsonPost({
  919. url: frm.attr('action'),
  920. data: formData,
  921. success: function(data) {
  922. if (data.success === true) {
  923. var tbody = jQuery('tbody[data-account-id="' + accountId + '"]');
  924. oxSuccess.find('span').text(data.successMessage);
  925. oxSuccess.show();
  926. switch (formId) {
  927. case 'frmOxDeleteAccount':
  928. tbody.slideUp().remove();
  929. break;
  930. case 'frmOxAddAccount':
  931. data.aliases = [data.username];
  932. addAccountToTable(data);
  933. addAliasesToTable(data);
  934. updateAliasesDisplay(data.id);
  935. break;
  936. case 'frmOxManageAccount':
  937. tbody.attr('data-display-name', data.display_name);
  938. tbody.attr('data-first-name', data.first_name);
  939. tbody.attr('data-last-name', data.last_name);
  940. break;
  941. case 'frmOxDeleteAlias':
  942. var alias = frm.find('input[name="alias"]').val(),
  943. tr = jQuery('tr.alias-entry[data-alias="' + alias + '"]');
  944. tr.slideUp().remove();
  945. break;
  946. }
  947. frm.find('input').not('[name="token"]').each(function() {
  948. jQuery(this).val('');
  949. });
  950. modal.modal('hide');
  951. } else {
  952. oxError.find('span').text(data.failedMessage);
  953. oxError.show();
  954. }
  955. },
  956. always: function() {
  957. updateAccountsDisplay();
  958. updateAliasesDisplay(accountId);
  959. modal.find('.loader').hide();
  960. }
  961. });
  962. }
  963. });
  964. });
  965. </script>