diff --git a/assets/sass/app.scss b/assets/sass/app.scss index 2919b90..673d561 100644 --- a/assets/sass/app.scss +++ b/assets/sass/app.scss @@ -1,7 +1,26 @@ @import "layout"; td.actions { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + + > *:not(:first-child) { + margin-left: 8px; + } + form { padding: 0; + display: inline; + } + + button, .button { + margin: 0; + padding: 8px; + + .feather { + margin-right: 0; + } } } diff --git a/assets/sass/layout.scss b/assets/sass/layout.scss index ef19293..eb52dc4 100644 --- a/assets/sass/layout.scss +++ b/assets/sass/layout.scss @@ -21,6 +21,46 @@ body { background-color: $backgroundColor; } +@mixin tip { + position: relative; + + .tip { + visibility: hidden; + position: absolute; + z-index: 10000; + pointer-events: none; + display: block; + width: max-content; + height: 30px; + padding: 4px 8px; + line-height: 22px; + top: calc(100% + 8px); + left: 50%; + transform: translateX(-50%); + + text-align: center; + font-size: 18px; + color: $defaultTextColor; + opacity: 0; + transition: opacity ease-out 100ms, visibility step-end 150ms; + transition-delay: 0ms; + background-color: #000; + border-radius: 5px; + + text-transform: initial; + font-weight: initial; + } + + &:hover, &:active { + .tip { + visibility: visible; + opacity: 1; + transition: opacity ease-out 100ms; + transition-delay: 150ms; + } + } +} + header { display: flex; flex-direction: row; @@ -77,27 +117,11 @@ header { &:not(button) { background-color: rgba(255, 255, 255, 0.07); } - - .tip { - visibility: visible; - opacity: 1; - transition: opacity ease-out 100ms; - transition-delay: 150ms; - } } .feather { --icon-size: 24px; } - - &:hover { - .tip { - visibility: visible; - opacity: 1; - transition: opacity ease-out 100ms; - transition-delay: 150ms; - } - } } button { @@ -108,11 +132,6 @@ header { .feather { margin-right: 0; } - - .tip { - text-transform: initial; - font-weight: initial; - } } form { @@ -188,32 +207,12 @@ header { @media (min-width: $menuLayoutSwitchTreshold) { nav ul li { - a, button { - .tip { - visibility: hidden; - position: absolute; - display: block; - width: max-content; - height: 30px; - padding: 4px 8px; - line-height: 22px; - top: calc(100% + 8px); - left: 50%; - transform: translateX(-50%); - - text-align: center; - font-size: 18px; - color: $defaultTextColor; - opacity: 0; - transition: opacity ease-out 100ms, visibility step-end 150ms; - transition-delay: 0ms; - background-color: #000; - border-radius: 5px; - } + a, button, .button { + @include tip; } &:last-child { - a, button { + a, button, .button { .tip { left: unset; right: 4px; @@ -235,6 +234,10 @@ footer { main { flex: 1; padding: 8px; + + button, .button { + @include tip; + } } h1 { @@ -373,7 +376,7 @@ form { position: absolute; pointer-events: none; right: 8px; - bottom: 8px; + top: 30px; transition: transform 150ms ease-out; } @@ -565,6 +568,19 @@ button, .button { } } +.breadcrumb { + list-style: none; + display: flex; + flex-direction: row; + margin: 0; + padding: 8px; + + > *:not(:first-child)::before { + content: '›'; + padding: 0 8px; + } +} + // --- // --- Layout helpers // --- diff --git a/package.json b/package.json index df77a79..2d78558 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aldap", - "version": "1.0.1", + "version": "1.1.0", "description": "Authentication LDAP server", "repository": "git@gitlab.com:ArisuOngaku/aldap.git", "author": "Alice Gaudon ", diff --git a/src/controllers/MailboxBackendController.ts b/src/controllers/MailboxBackendController.ts index df71a42..3685e88 100644 --- a/src/controllers/MailboxBackendController.ts +++ b/src/controllers/MailboxBackendController.ts @@ -28,8 +28,10 @@ export default class MailboxBackendController extends Controller { this.get('/', this.getMailboxesBackend, 'backend-mailboxes', REQUIRE_AUTH_MIDDLEWARE, REQUIRE_ADMIN_MIDDLEWARE); this.get('/:id', this.getMailboxBackend, 'backend-mailbox', REQUIRE_AUTH_MIDDLEWARE, REQUIRE_ADMIN_MIDDLEWARE); - this.post('/add-domain', this.postAddDomain, 'backend-add-domain', REQUIRE_AUTH_MIDDLEWARE, REQUIRE_ADMIN_MIDDLEWARE) - this.post('/remove-domain', this.postRemoveDomain, 'backend-remove-domain', REQUIRE_AUTH_MIDDLEWARE, REQUIRE_ADMIN_MIDDLEWARE) + this.post('/add-domain', this.postAddDomain, 'backend-add-domain', REQUIRE_AUTH_MIDDLEWARE, REQUIRE_ADMIN_MIDDLEWARE); + this.get('/edit-domain/:id', this.getEditDomain, 'backend-edit-domain', REQUIRE_AUTH_MIDDLEWARE, REQUIRE_ADMIN_MIDDLEWARE); + this.post('/edit-domain/:id', this.postEditDomain, 'backend-edit-domain', REQUIRE_AUTH_MIDDLEWARE, REQUIRE_ADMIN_MIDDLEWARE); + this.post('/remove-domain', this.postRemoveDomain, 'backend-remove-domain', REQUIRE_AUTH_MIDDLEWARE, REQUIRE_ADMIN_MIDDLEWARE); this.post('/:id/create-mail-identity', this.postCreateMailIdentity, 'backend-create-mail-identity', REQUIRE_AUTH_MIDDLEWARE, REQUIRE_ADMIN_MIDDLEWARE); this.post('/delete-mail-identity', this.postDeleteMailIdentity, 'backend-delete-mail-identity', REQUIRE_AUTH_MIDDLEWARE, REQUIRE_ADMIN_MIDDLEWARE); @@ -104,6 +106,35 @@ export default class MailboxBackendController extends Controller { res.redirectBack(); } + protected async getEditDomain(req: Request, res: Response): Promise { + const domain = await MailDomain.getById(req.params.id); + if (!domain) throw new NotFoundHttpError('Domain', req.url); + + res.render('backend/mail_domain', { + domain: domain, + users: [{ + value: 0, + display: 'Public', + }, ...(await User.select().get()).map(u => ({ + value: u.id, + display: u.name, + }))], + }); + } + + protected async postEditDomain(req: Request, res: Response): Promise { + const domain = await MailDomain.select() + .where('id', req.params.id) + .first(); + if (!domain) throw new NotFoundHttpError('Domain', req.url); + + domain.updateWithData(req.body); + await domain.save(); + + req.flash('success', `Domain ${domain.name} updated successfully.`); + res.redirectBack(); + } + protected async postRemoveDomain(req: Request, res: Response): Promise { const domain = await MailDomain.select() .where('id', req.body.id) diff --git a/src/models/MailDomain.ts b/src/models/MailDomain.ts index b192224..5f40fbf 100644 --- a/src/models/MailDomain.ts +++ b/src/models/MailDomain.ts @@ -28,7 +28,10 @@ export default class MailDomain extends Model { } protected init(): void { - this.setValidation('name').defined().maxLength(252).regexp(/^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/) + this.setValidation('name').acceptUndefined() + .maxLength(252) + .regexp(/^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/) + .unique(this); this.setValidation('user_id').acceptUndefined().exists(User, 'id'); } diff --git a/views/account.njk b/views/account.njk index 31cf255..3d5fa1d 100644 --- a/views/account.njk +++ b/views/account.njk @@ -44,7 +44,7 @@ {{ macros.csrf(getCSRFToken) }} @@ -54,7 +54,7 @@ {{ macros.csrf(getCSRFToken) }} @@ -101,7 +101,7 @@ {{ macros.csrf(getCSRFToken) }} diff --git a/views/backend/mail_domain.njk b/views/backend/mail_domain.njk new file mode 100644 index 0000000..861bc85 --- /dev/null +++ b/views/backend/mail_domain.njk @@ -0,0 +1,25 @@ +{% extends 'layouts/base.njk' %} + +{% set title = app.name + ' - Backend' %} + +{% block body %} +
+ {{ macros.breadcrumb('Mail domain: ' + domain.name, [ + {title: 'Backend', link: route('backend')}, + {title: 'Mailboxes', link: route('backend-mailboxes')} + ]) }} + +
+ +

Mail domain: {{ domain.name }}

+ +
+ {{ macros.field(_locals, 'select', 'user_id', domain.user_id, 'Owner', null, 'required', users) }} + + + + {{ macros.csrf(getCSRFToken) }} +
+
+
+{% endblock %} \ No newline at end of file diff --git a/views/backend/mailbox.njk b/views/backend/mailbox.njk index fcdefea..773bd51 100644 --- a/views/backend/mailbox.njk +++ b/views/backend/mailbox.njk @@ -4,11 +4,14 @@ {% block body %}
-
- -

Mailbox

+ {{ macros.breadcrumb(mailbox.name, [ + {title: 'Backend', link: route('backend')}, + {title: 'Mailboxes', link: route('backend-mailboxes')} + ]) }} -

{{ mailbox.name }}

+
+ +

Mailbox: {{ mailbox.name }}

@@ -28,8 +31,9 @@ - {{ macros.csrf(getCSRFToken) }} @@ -53,6 +57,6 @@ {{ macros.csrf(getCSRFToken) }} - + {% endblock %} \ No newline at end of file diff --git a/views/backend/mailboxes.njk b/views/backend/mailboxes.njk index 2c8bd72..1f9dc20 100644 --- a/views/backend/mailboxes.njk +++ b/views/backend/mailboxes.njk @@ -3,11 +3,15 @@ {% set title = app.name + ' - Backend' %} {% block body %} -

Mailbox manager

-
+ {{ macros.breadcrumb('Mailboxes', [ + {title: 'Backend', link: route('backend')} + ]) }} + +

Mailbox manager

+
-

Domains

+

Domains

Add domain

@@ -39,11 +43,16 @@
{{ domain.owner_name | default('Public') }} {{ domain.identity_count }} + + Edit + + - {{ macros.csrf(getCSRFToken) }} @@ -56,7 +65,7 @@
-

Mailboxes

+

Mailboxes

@@ -66,6 +75,7 @@ + @@ -74,9 +84,14 @@ - + + {% endfor %} diff --git a/yarn.lock b/yarn.lock index 681fbbe..b4e735b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1539,7 +1539,7 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: resolved "https://registry.toot.party/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.5.5: +ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3: version "6.12.3" resolved "https://registry.toot.party/ajv/-/ajv-6.12.3.tgz#18c5af38a111ddeb4f2697bd78d68abc1cabd706" integrity sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA== @@ -4448,11 +4448,11 @@ har-schema@^2.0.0: integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= har-validator@~5.1.3: - version "5.1.3" - resolved "https://registry.toot.party/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" - integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== + version "5.1.5" + resolved "https://registry.toot.party/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== dependencies: - ajv "^6.5.5" + ajv "^6.12.3" har-schema "^2.0.0" has-ansi@^2.0.0: @@ -9886,9 +9886,9 @@ widest-line@^3.1.0: string-width "^4.0.0" wms-core@^0: - version "0.20.1" - resolved "https://registry.toot.party/wms-core/-/wms-core-0.20.1.tgz#c63a1b6b5cd4669e97fa0b651cd5fa6496f6305c" - integrity sha512-JZkrLgxoPBSOf8P5KB9Fc5IoEoWyw6qMoSukpCIi14iue/rm/rW5M9D0Y3dy6tio3w7ZRsrcQdYbIkXW228lzQ== + version "0.20.2" + resolved "https://registry.toot.party/wms-core/-/wms-core-0.20.2.tgz#b4d3d71a30b4d8bcbb2c422b3b89027ef5bb4b0e" + integrity sha512-MlK9J5VolObOFi+lJW8PFX4IrCi/vUvhC1FGzHCkzdeU5ODagaj7T1HV/7cOaJtzH/pHB+XD1U+oveajkVZSxA== dependencies: argon2 "^0.26.2" compression "^1.7.4"
Name Identities Owned domainsActions
{{ box.id }} {{ box.username }}{{ box.name }}{{ box.name }} {{ box.identity_count }} {{ box.domain_count }} + + Edit + +