diff --git a/.gitignore b/.gitignore index a8dcba6..3054b59 100644 --- a/.gitignore +++ b/.gitignore @@ -121,6 +121,6 @@ dist dist yarn.lock public/css -public/css +public/js docker-compose.yml dump diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..8e76f38 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,46 @@ +const { parallel, src, dest } = require("gulp"); + +const sourcemaps = require("gulp-sourcemaps"); +const concat = require("gulp-concat"); + +const gulp = require("gulp"); +const uglify = require("gulp-uglify"); +const babel = require("gulp-babel"); + +const sourceJs = "javascripts/**/*.js"; +const sourceRemoteJS = [ + "./node_modules/vue/dist/vue.global.prod.js", + "./node_modules/axios/dist/axios.min.js", +]; + +const destination = "public/js"; + +// TASKS ---------------------------------------------------------------------- + +const compileJs = function () { + return gulp + .src(sourceJs) + .pipe(sourcemaps.init()) + .pipe(concat("main.js")) + .pipe( + babel({ + presets: ["@babel/env"], + }) + ) + .pipe(uglify()) + .pipe(sourcemaps.write(".")) + .pipe(gulp.dest(destination)); +}; +const compileRemoteJs = function () { + return gulp + .src(sourceRemoteJS) + .pipe(sourcemaps.init()) + .pipe(concat("libs.js")) + .pipe(sourcemaps.write(".")) + .pipe(gulp.dest(destination)); +}; +// ---------------------------------------------------------------------------- + +// COMMANDS ------------------------------------------------------------------- +exports.default = parallel(compileJs, compileRemoteJs); +// ---------------------------------------------------------------------------- diff --git a/javascripts/ajouter-un-album.js b/javascripts/ajouter-un-album.js new file mode 100644 index 0000000..d46d5d4 --- /dev/null +++ b/javascripts/ajouter-un-album.js @@ -0,0 +1,171 @@ +Vue.createApp({ + data() { + return { + q: '', + year: '', + country: '', + format: '', + loading: false, + items: [], + details: {}, + modalIsVisible: false, + formats: [ + 'Vinyl', + 'Acetate', + 'Flexi-disc', + 'Lathe Cut', + 'Mighty Tiny', + 'Shellac', + 'Sopic', + 'Pathé Disc', + 'Edison Disc', + 'Cylinder', + 'CD', + 'CDr', + 'CDV', + 'DVD', + 'DVDr', + 'HD DVD', + 'HD DVD-R', + 'Blu-ray', + 'Blu-ray-R', + 'Ultra HD Blu-ray', + 'SACD', + '4-Track Cartridge', + '8-Track Cartridge', + 'Cassette', + 'DC-International', + 'Elcaset', + 'PlayTape', + 'RCA Tape Cartridge', + 'DAT', + 'DCC', + 'Microcassette', + 'NT Cassette', + 'Pocket Rocker', + 'Revere Magnetic Stereo Tape Ca', + 'Tefifon', + 'Reel-To-Reel', + 'Sabamobil', + 'Betacam', + 'Betacam SP', + 'Betamax', + 'Cartrivision', + 'MiniDV', + 'Super VHS', + 'U-matic', + 'VHS', + 'Video 2000', + 'Video8', + 'Film Reel', + 'HitClips', + 'Laserdisc', + 'SelectaVision', + 'VHD', + 'Wire Recording', + 'Minidisc', + 'MVD', + 'UMD', + 'Floppy Disk', + 'File', + 'Memory Stick', + 'Hybrid', + 'All Media', + 'Box Set', + ] + } + }, + methods: { + search(event) { + event.preventDefault(); + + if ( this.loading ) { + return false; + } + + this.loading = true; + let url = `/api/v1/search?q=${this.q}`; + + if ( this.year ) { + url += `&year=${this.year}`; + } + if ( this.country ) { + url += `&country=${this.country}`; + } + if ( this.format ) { + url += `&format=${this.format}`; + } + + axios.get(url) + .then( response => { + const { + results, + } = response.data; + let items = []; + + for (let i = 0 ; i < results.length ; i += 1 ) { + const { + id, + title, + thumb, + year, + country, + format, + genre, + style, + } = results[i]; + items.push({ + id, + title, + thumb, + year, + country, + format, + genre, + style, + }); + } + + this.items = items; + }) + .catch((err) => { + showToastr(err.response?.data?.message || "Aucun résultat trouvé :/"); + }) + .finally(() => { + this.loading = false; + }); + }, + toggleModal() { + this.modalIsVisible = !this.modalIsVisible; + }, + loadDetails(discogsId) { + axios.get(`/api/v1/search/${discogsId}`) + .then( response => { + const { + data, + } = response; + + this.details = data; + this.toggleModal(); + }) + .catch((err) => { + showToastr(err.response?.data?.message || "Impossible de charger les détails de cet album"); + }) + .finally(() => { + this.loading = false; + }); + }, + add() { + axios.post('/api/v1/albums', this.details) + .then(() => { + window.location.href = '/ma-collection'; + }) + .catch((err) => { + showToastr(err.response?.data?.message || "Impossible d'ajouter cet album pour le moment…"); + }); + }, + orderedItems(items) { + return items.sort(); + } + } +}).mount('#ajouter-album'); \ No newline at end of file diff --git a/javascripts/collection.js b/javascripts/collection.js new file mode 100644 index 0000000..c3b3561 --- /dev/null +++ b/javascripts/collection.js @@ -0,0 +1,133 @@ +if ( typeof userId !== 'undefined' ) { + Vue.createApp({ + data() { + return { + loading: false, + moreFilters: false, + items: [], + total: 0, + page: 1, + totalPages: 1, + limit: 16, + artist: '', + format: '', + year: '', + genre: '', + style: '', + sortOrder: 'artists_sort-asc', + sort: 'artists_sort', + order: 'asc', + userId, + } + }, + created() { + this.fetch(); + }, + methods: { + fetch() { + this.loading = true; + this.total = 0; + + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + const entries = urlParams.entries(); + + for(const entry of entries) { + switch(entry[0]) { + case 'artists_sort': + this.artist = entry[1]; + break; + default: + this[entry[0]] = entry[1]; + } + } + + let url = `/api/v1/albums?userId=${this.userId}&page=${this.page}&limit=${this.limit}&sort=${this.sort}&order=${this.order}`; + if ( this.artist ) { + url += `&artists_sort=${this.artist.replace('&', '%26')}`; + } + if ( this.format ) { + url += `&format=${this.format.replace('&', '%26')}`; + } + if ( this.year ) { + url += `&year=${this.year}`; + } + if ( this.genre ) { + url += `&genre=${this.genre.replace('&', '%26')}`; + } + if ( this.style ) { + url += `&style=${this.style.replace('&', '%26')}`; + } + + axios.get(url) + .then( response => { + this.items = response.data.rows; + this.total = response.data.count || 0; + this.totalPages = parseInt(response.data.count / this.limit) + (response.data.count % this.limit > 0 ? 1 : 0); + + }) + .catch((err) => { + showToastr(err.response?.data?.message || "Impossible de charger cette collection"); + }) + .finally(() => { + this.loading = false; + }); + }, + changeUrl() { + let url = `?page=${this.page}&limit=${this.limit}&sort=${this.sort}&order=${this.order}`; + if ( this.artist ) { + url += `&artists_sort=${this.artist.replace('&', '%26')}`; + } + if ( this.format ) { + url += `&format=${this.format.replace('&', '%26')}`; + } + if ( this.year ) { + url += `&year=${this.year}`; + } + if ( this.genre ) { + url += `&genre=${this.genre.replace('&', '%26')}`; + } + if ( this.style ) { + url += `&style=${this.style.replace('&', '%26')}`; + } + + location.href = url; + }, + next(event) { + event.preventDefault(); + + this.page += 1; + + this.changeUrl(); + }, + previous(event) { + event.preventDefault(); + + this.page -= 1; + + this.changeUrl(); + }, + goTo(page) { + this.page = page; + + this.changeUrl(); + }, + changeSort() { + const [sort,order] = this.sortOrder.split('-'); + this.sort = sort; + this.order = order; + this.page = 1; + + this.changeUrl(); + }, + changeFilter() { + this.page = 1; + + this.changeUrl(); + }, + showMoreFilters() { + this.moreFilters = !this.moreFilters; + } + } + }).mount('#collection-publique'); +} \ No newline at end of file diff --git a/javascripts/conctact.js b/javascripts/conctact.js new file mode 100644 index 0000000..51ed1cb --- /dev/null +++ b/javascripts/conctact.js @@ -0,0 +1,42 @@ +if ( typeof contactMethod !== 'undefined' && contactMethod === 'smtp' ) { + Vue.createApp({ + data() { + return { + email: '', + name: '', + message: '', + captcha: '', + loading: false, + } + }, + methods: { + send(event) { + event.preventDefault(); + + if ( this.loading ) { + return false; + } + + this.loading = true; + + const { + email, + message, + name, + captcha, + } = this; + + axios.post('/api/v1/contact', {email, name, message, captcha}) + .then( () => { + showToastr("Message correctement envoyé", true); + }) + .catch((err) => { + showToastr(err.response?.data?.message || "Impossible d'envoyer votre message", false); + }) + .finally(() => { + this.loading = false; + }) + }, + }, + }).mount('#contact'); +} \ No newline at end of file diff --git a/public/js/main.js b/javascripts/main.js similarity index 97% rename from public/js/main.js rename to javascripts/main.js index f203a63..0b0592e 100644 --- a/public/js/main.js +++ b/javascripts/main.js @@ -1,8 +1,13 @@ -/** +const { + protocol, + host +} = window.location; + + /** * Fonction permettant d'afficher un message dans un toastr * @param {String} message */ - function showToastr(message, success = false) { +function showToastr(message, success = false) { let x = document.getElementById("toastr"); if ( message ) { x.getElementsByTagName("SPAN")[0].innerHTML = message; diff --git a/javascripts/mon-compte/index.js b/javascripts/mon-compte/index.js new file mode 100644 index 0000000..ccc1d02 --- /dev/null +++ b/javascripts/mon-compte/index.js @@ -0,0 +1,26 @@ +if ( typeof email !== 'undefined' && typeof username !== 'undefined' ) { + Vue.createApp({ + data() { + return { + email, + username, + oldPassword: '', + password: '', + passwordConfirm: '', + loading: false, + } + }, + methods: { + async updateProfil(event) { + // try { + // if ( this.password !== this.passwordConfirm ) { + // throw "La confirnation du mot de passe ne correspond pas"; + // } + // } catch(err) { + // event.preventDefault(); + // showToastr(err); + // } + }, + } + }).mount('#mon-compte'); +} \ No newline at end of file diff --git a/javascripts/mon-compte/ma-collection/details.js b/javascripts/mon-compte/ma-collection/details.js new file mode 100644 index 0000000..26f069f --- /dev/null +++ b/javascripts/mon-compte/ma-collection/details.js @@ -0,0 +1,158 @@ +if ( typeof item !== 'undefined' ) { + Vue.createApp({ + data() { + return { + item, + tracklist: [], + identifiers: [], + modalIsVisible: false, + identifiersMode: 'preview', + identifiersPreviewLength: 16, + preview: null, + index: null, + showModalDelete: false, + } + }, + created() { + this.setTrackList(); + this.setIdentifiers(); + + window.addEventListener("keydown", this.changeImage); + }, + destroyed() { + window.removeEventListener('keydown', this.changeImage); + }, + methods: { + setIdentifiers() { + this.identifiers = []; + + let max = this.identifiersMode == 'preview' && this.item.identifiers.length > this.identifiersPreviewLength ? this.identifiersPreviewLength : this.item.identifiers.length; + + for ( let i = 0 ; i < max ; i += 1 ) { + this.identifiers.push(this.item.identifiers[i]); + } + }, + setTrackList() { + let subTrack = { + type: null, + title: null, + tracks: [], + }; + for (let i = 0 ; i < this.item.tracklist.length ; i += 1 ) { + const { + type_, + title, + position, + duration, + extraartists, + } = this.item.tracklist[i]; + + if ( type_ === 'heading' ) { + if ( subTrack.type ) { + this.tracklist.push(subTrack); + subTrack = { + type: null, + title: null, + tracks: [], + }; + } + + subTrack.type = type_; + subTrack.title = title; + } else { + subTrack.tracks.push({ + title, + position, + duration, + extraartists + }); + } + } + this.tracklist.push(subTrack); + }, + setImage() { + this.preview = this.item.images[this.index].uri; + }, + showGallery(event) { + const item = event.target.tagName === 'IMG' ? event.target.parentElement : event.target; + + const { + index, + } = item.dataset; + + this.index = Number(index); + this.modalIsVisible = true; + + this.setImage(); + }, + toggleModal() { + this.modalIsVisible = !this.modalIsVisible; + }, + previous() { + this.index = this.index > 0 ? this.index - 1 : this.item.images.length -1; + this.setImage(); + }, + next() { + this.index = (this.index +1) === this.item.images.length ? 0 : this.index + 1; + this.setImage(); + }, + changeImage(event) { + const direction = event.code; + + if ( this.modalIsVisible && ['ArrowRight', 'ArrowLeft', 'Escape'].indexOf(direction) !== -1 ) { + switch (direction) { + case 'ArrowRight': + return this.next(); + case 'ArrowLeft': + return this.previous(); + default: + this.modalIsVisible = false; + return true; + } + } + }, + showAllIdentifiers() { + this.identifiersMode = 'all'; + this.setIdentifiers(); + }, + showLessIdentifiers() { + this.identifiersMode = 'preview'; + this.setIdentifiers(); + + document.querySelector('#identifiers').scrollIntoView({ behavior: 'smooth' }); + }, + showConfirmDelete() { + this.toggleModal(); + }, + toggleModal() { + this.showModalDelete = !this.showModalDelete; + }, + updateItem() { + showToastr("Mise à jour en cours…", true); + axios.patch(`/api/v1/albums/${this.item._id}`) + .then( (res) => { + showToastr("Mise à jour réalisée avec succès", true); + this.item = res.data; + }) + .catch((err) => { + showToastr(err.response?.data?.message || "Impossible de mettre à jour cet album", false); + }); + }, + deleteItem() { + axios.delete(`/api/v1/albums/${this.item._id}`) + .then( () => { + window.location.href = "/ma-collection"; + }) + .catch((err) => { + showToastr(err.response?.data?.message || "Impossible de supprimer cet album"); + }) + .finally(() => { + this.toggleModal(); + }); + }, + goToArtist() { + return ""; + }, + }, + }).mount('#ma-collection-details'); +} \ No newline at end of file diff --git a/javascripts/mon-compte/ma-collection/exporter.js b/javascripts/mon-compte/ma-collection/exporter.js new file mode 100644 index 0000000..f5dcbcb --- /dev/null +++ b/javascripts/mon-compte/ma-collection/exporter.js @@ -0,0 +1,18 @@ +Vue.createApp({ + data() { + return { + format: 'xml', + } + }, + created() { + }, + destroyed() { + }, + methods: { + exportCollection(event) { + event.preventDefault(); + + window.open(`/api/v1/albums?exportFormat=${this.format}`, '_blank'); + } + }, +}).mount('#exporter'); \ No newline at end of file diff --git a/javascripts/mon-compte/ma-collection/index.js b/javascripts/mon-compte/ma-collection/index.js new file mode 100644 index 0000000..7e2bdf5 --- /dev/null +++ b/javascripts/mon-compte/ma-collection/index.js @@ -0,0 +1,178 @@ +if ( typeof isPublicCollection !== 'undefined' ) { + Vue.createApp({ + data() { + return { + loading: false, + moreFilters: false, + items: [], + total: 0, + page: 1, + totalPages: 1, + limit: 16, + artist: '', + format: '', + year: '', + genre: '', + style: '', + sortOrder: 'artists_sort-asc', + sort: 'artists_sort', + order: 'asc', + itemId: null, + showModalDelete: false, + showModalShare: false, + shareLink: `${protocol}//${host}/collection/<%= user._id %>`, + isPublicCollection, + } + }, + created() { + this.fetch(); + }, + methods: { + fetch() { + this.loading = true; + this.total = 0; + + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + const entries = urlParams.entries(); + + for(const entry of entries) { + switch(entry[0]) { + case 'artists_sort': + this.artist = entry[1]; + break; + default: + this[entry[0]] = entry[1]; + } + } + + let url = `/api/v1/albums?page=${this.page}&limit=${this.limit}&sort=${this.sort}&order=${this.order}`; + if ( this.artist ) { + url += `&artists_sort=${this.artist.replace('&', '%26')}`; + } + if ( this.format ) { + url += `&format=${this.format.replace('&', '%26')}`; + } + if ( this.year ) { + url += `&year=${this.year}`; + } + if ( this.genre ) { + url += `&genre=${this.genre.replace('&', '%26')}`; + } + if ( this.style ) { + url += `&style=${this.style.replace('&', '%26')}`; + } + + axios.get(url) + .then( response => { + this.items = response.data.rows; + this.total = response.data.count || 0; + this.totalPages = parseInt(response.data.count / this.limit) + (response.data.count % this.limit > 0 ? 1 : 0); + }) + .catch((err) => { + showToastr(err.response?.data?.message || "Impossible de charger votre collection"); + }) + .finally(() => { + this.loading = false; + }); + }, + changeUrl() { + let url = `?page=${this.page}&limit=${this.limit}&sort=${this.sort}&order=${this.order}`; + if ( this.artist ) { + url += `&artists_sort=${this.artist.replace('&', '%26')}`; + } + if ( this.format ) { + url += `&format=${this.format.replace('&', '%26')}`; + } + if ( this.year ) { + url += `&year=${this.year}`; + } + if ( this.genre ) { + url += `&genre=${this.genre.replace('&', '%26')}`; + } + if ( this.style ) { + url += `&style=${this.style.replace('&', '%26')}`; + } + + location.href = url; + }, + next(event) { + event.preventDefault(); + + this.page += 1; + + this.changeUrl(); + }, + previous(event) { + event.preventDefault(); + + this.page -= 1; + + this.changeUrl(); + }, + goTo(page) { + this.page = page; + + this.changeUrl(); + }, + changeSort() { + const [sort,order] = this.sortOrder.split('-'); + this.sort = sort; + this.order = order; + this.page = 1; + + this.changeUrl(); + }, + changeFilter() { + this.page = 1; + + this.changeUrl(); + }, + showMoreFilters() { + this.moreFilters = !this.moreFilters; + }, + toggleModal() { + this.showModalDelete = !this.showModalDelete; + }, + toggleModalShare() { + this.showModalShare = !this.showModalShare; + }, + showConfirmDelete(itemId) { + this.itemId = itemId; + this.toggleModal(); + }, + deleteItem() { + axios.delete(`/api/v1/albums/${this.itemId}`) + .then( () => { + this.fetch(); + }) + .catch((err) => { + showToastr(err.response?.data?.message || "Impossible de supprimer cet album"); + }) + .finally(() => { + this.toggleModal(); + }); + }, + shareCollection() { + axios.patch(`/api/v1/me`, { + isPublicCollection: !this.isPublicCollection, + }) + .then( (res) => { + this.isPublicCollection = res.data.isPublicCollection; + + if ( this.isPublicCollection ) { + showToastr("Votre collection est désormais publique", true); + } else { + showToastr("Votre collection n'est plus partagée", true); + } + }) + .catch((err) => { + showToastr(err.response?.data?.message || "Impossible de supprimer cet album"); + }) + .finally(() => { + this.toggleModalShare(); + }); + }, + } + }).mount('#ma-collection'); +} \ No newline at end of file diff --git a/package.json b/package.json index 2ee4492..b3dffed 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,10 @@ "description": "Simple application to manage your CD/Vinyl collection", "scripts": { "start": "node ./dist/bin/www", - "run:all": "npm-run-all build sass start", + "run:all": "npm-run-all build sass uglify start", "watch": "nodemon -e js,scss", "sass": "npx sass sass/index.scss public/css/main.css -s compressed --color", + "uglify": "npx gulp", "prebuild": "rimraf dist", "build": "babel ./src --out-dir dist --copy-files", "test": "jest", @@ -55,6 +56,11 @@ "excel4node": "^1.7.2", "express": "^4.17.2", "express-session": "^1.17.2", + "gulp": "^4.0.2", + "gulp-babel": "^8.0.0", + "gulp-concat": "^2.6.1", + "gulp-sourcemaps": "^3.0.0", + "gulp-uglify": "^3.0.2", "joi": "^17.6.0", "knacss": "^8.0.4", "mongoose": "^6.2.1", @@ -75,7 +81,8 @@ "exec": "yarn run:all", "watch": [ "src/*", - "sass/*" + "sass/*", + "javascripts/*" ], "ignore": [ "**/__tests__/**", diff --git a/src/app.js b/src/app.js index 7c40a27..6fa4688 100644 --- a/src/app.js +++ b/src/app.js @@ -75,14 +75,6 @@ app.set("views", path.join(__dirname, "../views")); app.set("view engine", "ejs"); app.use(express.static(path.join(__dirname, "../public"))); -app.use( - "/libs/vue", - express.static(path.join(__dirname, "../node_modules/vue/dist")) -); -app.use( - "/libs/axios", - express.static(path.join(__dirname, "../node_modules/axios/dist")) -); app.use("/", indexRouter); app.use("/mon-compte", monCompteRouter); diff --git a/views/index.ejs b/views/index.ejs index 2386a83..a310984 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -16,9 +16,8 @@ - - - + + <% if ( config.matomoUrl ) { %> diff --git a/views/pages/ajouter-un-album.ejs b/views/pages/ajouter-un-album.ejs index a7306ff..f32ea67 100644 --- a/views/pages/ajouter-un-album.ejs +++ b/views/pages/ajouter-un-album.ejs @@ -1,4 +1,4 @@ -
+

Ajouter un album

@@ -169,178 +169,4 @@
-
- - +
\ No newline at end of file diff --git a/views/pages/collection.ejs b/views/pages/collection.ejs index 9960983..3272d8b 100644 --- a/views/pages/collection.ejs +++ b/views/pages/collection.ejs @@ -1,4 +1,4 @@ -
+

Collection de <%= page.username %>

@@ -146,140 +146,5 @@
diff --git a/views/pages/mon-compte/index.ejs b/views/pages/mon-compte/index.ejs index d8f7f6a..af7b634 100644 --- a/views/pages/mon-compte/index.ejs +++ b/views/pages/mon-compte/index.ejs @@ -1,4 +1,4 @@ -
+

Mon compte

@@ -72,28 +72,6 @@
diff --git a/views/pages/mon-compte/ma-collection/details.ejs b/views/pages/mon-compte/ma-collection/details.ejs index 8f5b0ef..e8615b4 100644 --- a/views/pages/mon-compte/ma-collection/details.ejs +++ b/views/pages/mon-compte/ma-collection/details.ejs @@ -1,4 +1,4 @@ -
+

{{item.artists_sort}} - {{item.title}} @@ -176,160 +176,5 @@

\ No newline at end of file diff --git a/views/pages/mon-compte/ma-collection/exporter.ejs b/views/pages/mon-compte/ma-collection/exporter.ejs index 13e3f98..28cc559 100644 --- a/views/pages/mon-compte/ma-collection/exporter.ejs +++ b/views/pages/mon-compte/ma-collection/exporter.ejs @@ -1,4 +1,4 @@ -
+

Exporter ma collection

Les formats CSV et Excel sont facilement lisiblent par un humain. Dans ces 2 formats vous trouverez seulement les informations principales de vos albums, à savoir : @@ -44,25 +44,4 @@ Exporter -

- - \ No newline at end of file +
\ No newline at end of file diff --git a/views/pages/mon-compte/ma-collection/index.ejs b/views/pages/mon-compte/ma-collection/index.ejs index fb67cd3..38993c8 100644 --- a/views/pages/mon-compte/ma-collection/index.ejs +++ b/views/pages/mon-compte/ma-collection/index.ejs @@ -1,4 +1,4 @@ -
+

Ma collection @@ -196,185 +196,5 @@

+ const isPublicCollection = <%= user.isPublicCollection ? 'true' : 'false' %>; + \ No newline at end of file diff --git a/views/pages/nous-contacter.ejs b/views/pages/nous-contacter.ejs index 276b6f4..f179135 100644 --- a/views/pages/nous-contacter.ejs +++ b/views/pages/nous-contacter.ejs @@ -1,4 +1,4 @@ -
+

Nous contacter

id="contact" method="POST" action="https://formspree.io/f/<%= config.formspreeId %>" <% } %>>
@@ -34,47 +34,6 @@
-<% if (config.mailMethod === 'smtp' ) { %> - -<% } %> \ No newline at end of file + \ No newline at end of file