From b27a81a0b6e8aaa4a53474290b3137296415d700 Mon Sep 17 00:00:00 2001 From: Damien Broqua Date: Fri, 4 Mar 2022 16:33:45 +0100 Subject: [PATCH] issue/2 (#28) Co-authored-by: dbroqua Reviewed-on: https://git.darkou.fr/dbroqua/MusicTopus/pulls/28 Co-authored-by: Damien Broqua Co-committed-by: Damien Broqua --- .eslintrc.js | 2 +- README.md | 2 + package.json | 2 + public/font/icon.eot | Bin 9132 -> 9752 bytes public/font/icon.svg | 6 + public/font/icon.ttf | Bin 8980 -> 9600 bytes public/font/icon.woff | Bin 5688 -> 6060 bytes public/font/icon.woff2 | Bin 4716 -> 5052 bytes public/js/main.js | 19 +- sass/forms.scss | 26 + sass/global.scss | 13 + sass/icons.scss | 3 + sass/index.scss | 7 +- sass/ma-collection-details.scss | 55 ++ sass/modal.scss | 82 +-- src/app.js | 2 + src/middleware/Albums.js | 512 +++++++++++++++++- src/routes/api/v1/albums.js | 18 +- src/routes/index.js | 19 - src/routes/ma-collection.js | 53 ++ views/index.ejs | 3 + views/pages/ajouter-un-album.ejs | 8 +- views/pages/composants.ejs | 2 + views/pages/mon-compte/ma-collection.ejs | 7 +- .../mon-compte/ma-collection/details.ejs | 282 ++++++++++ .../mon-compte/ma-collection/exporter.ejs | 68 +++ 26 files changed, 1094 insertions(+), 97 deletions(-) create mode 100644 sass/ma-collection-details.scss create mode 100644 src/routes/ma-collection.js create mode 100644 views/pages/mon-compte/ma-collection/details.ejs create mode 100644 views/pages/mon-compte/ma-collection/exporter.ejs diff --git a/.eslintrc.js b/.eslintrc.js index c8f8a9d..143bf10 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -16,7 +16,7 @@ module.exports = { 'no-underscore-dangle': [ 'error', { - allow: ['_id', 'artists_sort'], + allow: ['_id', 'artists_sort', 'type_'], }, ], 'camelcase': [ diff --git a/README.md b/README.md index c787da0..fdc754d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # MusicTopus +![MusicTopus](public/img/logo-large.png) + MusicTopus est une application Web (que vous pouvez auto-héberger) et un site Web (sur lequel vous pouvez créer un compte) permettant de gérer votre liste des CDs et Vinyles et de l'utiliser facilement n'importe où. Le code source est publié sous licence libre [GNU GPL-3.0-or-later](LICENSE) et est disponible sur [git.darkou.fr](https://git.darkou.fr/dbroqua/MusicTopus). diff --git a/package.json b/package.json index 77f10de..8c53d5a 100644 --- a/package.json +++ b/package.json @@ -51,10 +51,12 @@ "debug": "^4.3.3", "disconnect": "^1.2.2", "ejs": "^3.1.6", + "excel4node": "^1.7.2", "express": "^4.17.2", "express-session": "^1.17.2", "knacss": "^8.0.4", "moment": "^2.29.1", + "moment-timezone": "^0.5.34", "mongoose": "^6.2.1", "mongoose-unique-validator": "^3.0.0", "passport": "^0.5.2", diff --git a/public/font/icon.eot b/public/font/icon.eot index 1609d7351cbe182bc43e2b003fee916409870f0a..5a5d395769630de6dfb612635456a30fcfc3b97b 100644 GIT binary patch delta 1174 zcmXw0O>7%g5T1E&_dV~f?aexiZHn_#Cw5};LzFl+C2g^iRt1srFQN*P;s!fO6USC+ zN+_rAn(bKA(A~@;(4Q4*;)E7P6&#`loXgUj<;tCr_4JDNn!P z&vf|nrK!TP-n*wL-lWhwl|50Sd;`U6WIa=JZ|6s5eLn#x5xOr==dy3KeDq!#Ah1mL zscA~cKAxr6J zvv|9Q;xaXOsZ=~szP{(pAE*JF?!PWAr1`;g2Snlg#qK#bnKZpMuJzhU!#mr2 ztRexn2*d+h4*(kZ58$e8?WqF{fSXHo0CC+qR|ojWeozN>1h?+gLHcm}QXOt5?gaPm z@&GrW4HB6+C|p_nO!-f(P=1YF;0M5lP{u|C#8Ik75`dGT5EJ1pjRkEJMYIFs$uy?= zTQF#|rRoh#==!)3F{;N516TBBeOzZ>RX3|QYOao#$>NG(jO)!c{WVtJ(b0BvDs`bq zpe95jkGUcm9Z!-{$W=N{{1IcEG&-xBH8x_^ai*HB%VPpN>jiG1yJ1!v_6zcWR?(Y8ApAGDIG^gw@5+EPf-g;60bI~dyCpW27XbSj++HcHVJ?TGKgwx|@Lpouh8 zFYie1m+65)DjBG4kOwA4s(qD_iHVUV=|!`vMOF64l9+18^;lAGFg>BO8@efF@W{8B z;fI+|YN}VXh6AX01MQue(frVnpp<@N!@x(ET^d{;o@7o;eoaSx z!{AzY_UXgl`vgi$+uQ$a(<6OJtGBx|vdhC}!x}d>=%HZsV?8G#&7mvZN6o$eg2}hs zmHu&P<<}kSG+1f0oTsc-o&7@FCh*UlywwwWoX;y>WkI>4zN`KzK9okBhe({)$m7E8s2vh$GnRX-8pabHs_SPmFfax%Ffb$}=Oz}+YVJ`4@;5LrFgYZb zl_&tkfj}e%NbscZzDh=JNrmCZu>Ws>0zeBHEOPRb6Gig`p8@$V zfPA^!#EJq&Lq4FKAP11Ike8U7I=SMLE|9MQbl}{A{NfVnGTrwK48kXX@|^`msRec0 zthX^RC{;|Jz!=Rqaq=6+e9b8gj6hiihCT*nFiQtWa{)a$g@J`32`J9Tz`?-8!1jV| zato6#rxXJS$}q@G-o+#f)WEj+Arlj$00R%$j5T1Afq{4OC+2WhO9qA)Kn&E+@SzWa z88{gjKDI(I$d-?75X`_0q<=s#$gEFuA((-8^A;8^w$0~wlGtR7HCXS(^V@u7;AegT zvgn@mGanc|*+?06x`tA^-pY diff --git a/public/font/icon.svg b/public/font/icon.svg index 67c8d09..51f7015 100644 --- a/public/font/icon.svg +++ b/public/font/icon.svg @@ -20,6 +20,12 @@ + + + + + + diff --git a/public/font/icon.ttf b/public/font/icon.ttf index cbdbe4d0dbb6c65bbc17942cfc0fe99d48b55f95..488fa0b950dfb203d41f048e34aa311fcb6ee045 100644 GIT binary patch delta 1125 zcmXw0O>7%Q6n<}KcgDMGdu?a2Lva$2-kbNn zZ|1$L$1WXT@2-6UfUg3;yYt0t`9JNeWwJK`q_gwqD-rV32>wEgccGjwo*8<0f$SYJ z-TCafGWmUE-=^J_U;c1mYRU66fE=d#N+Fkhuj`ACGXQ~gx=$C#A(AXhw$0a5C|0Ut zv9})GBL@MFE|=!B2j1!U4WKP)2Nbi_GN!znWKWYFS;-c2R`tOk*%f-=^>XQ4<<_D1 zf1(GNbpL&MHCMiPbL=Li{&i*VZM`Mux*33x-b z?%`k}0``p3q!98mic)}speU8{bZ~=*at!d*$0ZM{TQ)ZhpEb>9mIOe9K*7*>3`&E> zW7xutLk@@v?yor@QrNxhfaqcOGY2FDyL%2u5*}Q4U>Ci9pJ}`X@aVSmB|8cxgeFWx zKs-SYM`i4bJ=}#sQ(9}@#ki);>S4WkM%Qse>(pj7>AU7m z^KR>?;n%d|hOW1MN~uB=(Sf!(N9t%)kp1li)(vWsnuF$ovv;rf_!A?vB8kv`DQ{ zWTE7#RlsI|F*r>Jc;$`PP9BfN!lUunh{`(>_=L&34@P@O{i$?jWH=~H!39rXlncKZ z3>_R!AHh^6ok<7V1n-Xa^&Y|QsNkWXf%K^%)|VO=nUPTn8EI9J1?Hxj$=cN1+|-(I zqmk%R=HqOKCpC75a2rGlU49`e9=7Ny#Hr#myt_0hHZ=p8kmw3uC8(LU{FU z9iLveX>e_SN4sZ+ZU***@K6 delta 537 zcmXv}OKTHR7(L(2phE`*dW6?ZQD0fNwF$s%A<5ULwJ)4_W=_q*qu?{V*o>#eDGC!W6tG4kA^>y>nKCr>`Y2d?|IrTU>p{wI+BO8>3@xa2?EoZJMCH&=J>%Br50+Gq-6IFiycnNxZ#bGrRG^TqiGsKs&G diff --git a/public/font/icon.woff b/public/font/icon.woff index 4bed2250d5963c8db111617e457a68efddb52d4a..7c3262dd2702cebd300972a85c85e47075a2dd86 100644 GIT binary patch delta 3615 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 f6>o>5N} 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 diff --git a/public/font/icon.woff2 b/public/font/icon.woff2 index b39dcc983d5e8f168d5e1d77acf8205406d5bdf9..ac6af3aaf6acd895465567c30e86af1c399292a1 100644 GIT binary patch literal 5052 zcmV;t6GQBGPew8T0RR910290b4*&oF040C`025>Y0RR9100000000000000000000 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 S!>haXIZX>+@S6g2<^TY&bBksG literal 4716 zcmV-y5|izBPew8T0RR9101|8f4*&oF03#Ft01_1d0RR9100000000000000000000 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 diff --git a/public/js/main.js b/public/js/main.js index 710d84b..994b2ae 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -1,7 +1,7 @@ /** - * Fonction permettant d'afficher un message dans un toastr - * @param {String} message - */ + * Fonction permettant d'afficher un message dans un toastr + * @param {String} message + */ function showToastr(message) { let x = document.getElementById("toastr"); if ( message ) { @@ -114,17 +114,22 @@ document.addEventListener('DOMContentLoaded', () => { } const switchAriaThemeBtn = document.querySelector("#switchAriaTheme"); - switchAriaThemeBtn.addEventListener("click", switchAriaTheme); + if ( switchAriaThemeBtn ) { + switchAriaThemeBtn.addEventListener("click", switchAriaTheme); + } setAriaTheme(getCookie('ariatheme')); const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]'); - toggleSwitch.addEventListener('change', switchTheme, false); + if ( toggleSwitch ) { + toggleSwitch.addEventListener('change', switchTheme, false); + } let currentThemeIsDark = getCookie('theme'); if ( currentThemeIsDark === 'false' && window.matchMedia ) { currentThemeIsDark = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; } - console.log('currentThemeIsDark:', currentThemeIsDark); switchTheme({target: {checked: currentThemeIsDark === 'dark'}}); - toggleSwitch.checked = currentThemeIsDark === 'dark'; + if ( toggleSwitch) { + toggleSwitch.checked = currentThemeIsDark === 'dark'; + } }); \ No newline at end of file diff --git a/sass/forms.scss b/sass/forms.scss index 0f4aab7..fd302e0 100644 --- a/sass/forms.scss +++ b/sass/forms.scss @@ -3,6 +3,10 @@ display: flex; flex-direction: column; + &.inline { + flex-direction: row; + } + &.has-addons { display: flex; justify-content: flex-start; @@ -40,6 +44,28 @@ } } + input[type="radio"] { + border-radius: 50%; + appearance: none; + width: 1.2rem; + height: 1.2rem; + vertical-align: text-bottom; + outline: 0; + box-shadow: inset 0 0 0 1px var(--input-active-color); + background-color: #fff; + transition: background-size .15s; + cursor: pointer; + + &:checked { + box-shadow: inset 0 0 0 4px var(--input-active-color); + } + } + + input[type="radio"] + label, + label + input[type="radio"] { + margin-left: 12px; + } + select { appearance: none; background-image: url("data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20standalone%3D%22no%22%3F%3E%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20xmlns%3Axlink%3D%22http%3A//www.w3.org/1999/xlink%22%20style%3D%22isolation%3Aisolate%22%20viewBox%3D%220%200%2020%2020%22%20width%3D%2220%22%20height%3D%2220%22%3E%3Cpath%20d%3D%22%20M%209.96%2011.966%20L%203.523%205.589%20C%202.464%204.627%200.495%206.842%201.505%207.771%20L%201.505%207.771%20L%208.494%2014.763%20C%209.138%2015.35%2010.655%2015.369%2011.29%2014.763%20L%2011.29%2014.763%20L%2018.49%207.771%20C%2019.557%206.752%2017.364%204.68%2016.262%205.725%20L%2016.262%205.725%20L%209.96%2011.966%20Z%20%22%20fill%3D%22inherit%22/%3E%3C/svg%3E"); diff --git a/sass/global.scss b/sass/global.scss index 2165147..491b9ca 100644 --- a/sass/global.scss +++ b/sass/global.scss @@ -1,5 +1,6 @@ html { min-height: 100vh; + scroll-behavior: smooth; body { background-color: var(--bg-color); @@ -69,4 +70,16 @@ html { .is-hidden { display: none; +} + +.ml-4 { + margin-left: 1rem; +} + +.sm-hidden { + display: none; + + @include respond-to("small-up") { + display: initial; + } } \ No newline at end of file diff --git a/sass/icons.scss b/sass/icons.scss index ec219a8..ca297ea 100644 --- a/sass/icons.scss +++ b/sass/icons.scss @@ -39,6 +39,9 @@ .icon-link:before { content: '\e804'; } /* '' */ .icon-heart:before { content: '\e805'; } /* '' */ .icon-eye:before { content: '\e806'; } /* '' */ +.icon-left-open:before { content: '\e807'; } /* '' */ +.icon-right-open:before { content: '\e808'; } /* '' */ +.icon-export:before { content: '\e809'; } /* '' */ .icon-spin:before { content: '\e839'; } /* '' */ .icon-link-ext:before { content: '\f08e'; } /* '' */ .icon-sun:before { content: '\f185'; } /* '' */ diff --git a/sass/index.scss b/sass/index.scss index a2d268e..aa7557a 100644 --- a/sass/index.scss +++ b/sass/index.scss @@ -19,8 +19,8 @@ // COMPOSANTS (à ajouter au besoin) // @import "../node_modules/knacss/sass/components/button"; // @import "components/burger"; -// @import "components/checkbox"; -// @import "components/radio"; +// @import "../node_modules/knacss/sass/components/checkbox"; +@import "../node_modules/knacss/sass/components/radio"; // @import "../node_modules/knacss/sass/components/select"; // @import "components/quote"; @@ -43,4 +43,5 @@ @import './error'; @import './home'; @import './ajouter-un-album'; -@import './ma-collection'; \ No newline at end of file +@import './ma-collection'; +@import './ma-collection-details'; \ No newline at end of file diff --git a/sass/ma-collection-details.scss b/sass/ma-collection-details.scss new file mode 100644 index 0000000..6d4ba32 --- /dev/null +++ b/sass/ma-collection-details.scss @@ -0,0 +1,55 @@ +.ma-collection-details { + .galerie { + display: flex; + flex-wrap: wrap; + + div { + width: 80px; + height: 80px; + margin: 0.25rem; + padding: 0.25rem; + border: 2px solid var(--font-color); + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + + img { + max-width: 90%; + } + } + } + + .modal { + button.close { + height: 36px; + max-height: 36px; + max-width: 36px; + min-height: 36px; + min-width: 36px; + width: 36px; + position: absolute; + background-color: rgba(10,10,10,.6); + right: 12px; + top: 12px; + } + + .navigation { + position: absolute; + top: 50%; + cursor: pointer; + z-index: 10; + + &.previous { + left: 12px; + } + &.next { + right: 12px; + } + i { + font-size: 2rem; + color: $nord4; + } + } + } +} \ No newline at end of file diff --git a/sass/modal.scss b/sass/modal.scss index ffe0571..e44de67 100644 --- a/sass/modal.scss +++ b/sass/modal.scss @@ -24,6 +24,47 @@ top: 0; } + button.close { + user-select: none; + background-color: rgba(10,10,10,.2); + border: none; + border-radius: 9999px; + cursor: pointer; + pointer-events: auto; + display: inline-block; + flex-grow: 0; + flex-shrink: 0; + font-size: 0; + height: 20px; + max-height: 20px; + max-width: 20px; + min-height: 20px; + min-width: 20px; + outline: none; + position: relative; + width: 20px; + + &::before, + &::after { + background-color: var(--default-color); + content: ""; + display: block; + left: 50%; + position: absolute; + top: 50%; + transform: translateX(-50%) translateY(-50%) rotate(45deg); + transform-origin: center center; + } + &::before { + height: 2px; + width: 50%; + } + &::after { + height: 50%; + width: 2px; + } + } + .modal-card { position: relative; width: 300px; @@ -62,47 +103,6 @@ justify-content: space-between; font-size: 1.5rem; @include transition() {} - - button { - user-select: none; - background-color: rgba(10,10,10,.2); - border: none; - border-radius: 9999px; - cursor: pointer; - pointer-events: auto; - display: inline-block; - flex-grow: 0; - flex-shrink: 0; - font-size: 0; - height: 20px; - max-height: 20px; - max-width: 20px; - min-height: 20px; - min-width: 20px; - outline: none; - position: relative; - width: 20px; - - &::before, - &::after { - background-color: var(--default-color); - content: ""; - display: block; - left: 50%; - position: absolute; - top: 50%; - transform: translateX(-50%) translateY(-50%) rotate(45deg); - transform-origin: center center; - } - &::before { - height: 2px; - width: 50%; - } - &::after { - height: 50%; - width: 2px; - } - } } section { background-color: var(--default-color); diff --git a/src/app.js b/src/app.js index d64e63c..0b32d82 100644 --- a/src/app.js +++ b/src/app.js @@ -12,6 +12,7 @@ import config, { env, mongoDbUri, secret } from "./config"; import { isXhr } from "./helpers"; import indexRouter from "./routes"; +import maCollectionRouter from "./routes/ma-collection"; import importAlbumRouterApiV1 from "./routes/api/v1/albums"; import importSearchRouterApiV1 from "./routes/api/v1/search"; @@ -80,6 +81,7 @@ app.use( ); app.use("/", indexRouter); +app.use("/ma-collection", maCollectionRouter); app.use("/api/v1/albums", importAlbumRouterApiV1); app.use("/api/v1/search", importSearchRouterApiV1); diff --git a/src/middleware/Albums.js b/src/middleware/Albums.js index fe795c3..4796207 100644 --- a/src/middleware/Albums.js +++ b/src/middleware/Albums.js @@ -1,4 +1,6 @@ import moment from "moment"; +import momenttz from "moment-timezone"; +import xl from "excel4node"; import Pages from "./Pages"; @@ -9,6 +11,451 @@ import ErrorEvent from "../libs/error"; * Classe permettant la gestion des albums d'un utilisateur */ class Albums extends Pages { + static replaceSpecialChars(str) { + if (!str) { + return ""; + } + let final = str.toString(); + const find = ["&", "<", ">"]; + const replace = ["&", "<", ">"]; + + for (let i = 0; i < find.length; i += 1) { + final = final.replace(new RegExp(find[i], "g"), replace[i]); + } + + return final; + } + + /** + * Méthode permettant de convertir les rows en csv + * @param {Array} rows + * + * @return {string} + */ + static async convertToCsv(rows) { + let data = + "Artiste;Titre;Genre;Styles;Pays;Année;Date de sortie;Format\n\r"; + + for (let i = 0; i < rows.length; i += 1) { + const { + artists_sort, + title, + genres, + styles, + country, + year, + released, + formats, + } = rows[i]; + + let format = ""; + for (let j = 0; j < formats.length; j += 1) { + format += `${format !== "" ? ", " : ""}${formats[j].name}`; + } + + data += `${artists_sort};${title};${genres.join()};${styles.join()};${country};${year};${released};${format}\n\r`; + } + + return data; + } + + /** + * Méthode permettant de convertir les rows en fichier xls + * @param {Array} rows + * + * @return {Object} + */ + static async convertToXls(rows) { + const wb = new xl.Workbook(); + const ws = wb.addWorksheet("MusicTopus"); + + const headerStyle = wb.createStyle({ + font: { + color: "#FFFFFF", + size: 11, + }, + fill: { + type: "pattern", + patternType: "solid", + bgColor: "#595959", + fgColor: "#595959", + }, + }); + const style = wb.createStyle({ + font: { + color: "#000000", + size: 11, + }, + numberFormat: "0000", + }); + + const header = [ + "Artiste", + "Titre", + "Genre", + "Styles", + "Pays", + "Année", + "Date de sortie", + "Format", + ]; + for (let i = 0; i < header.length; i += 1) { + ws.cell(1, i + 1) + .string(header[i]) + .style(headerStyle); + } + + for (let i = 0; i < rows.length; i += 1) { + const currentRow = i + 2; + const { + artists_sort, + title, + genres, + styles, + country, + year, + released, + formats, + } = rows[i]; + + let format = ""; + for (let j = 0; j < formats.length; j += 1) { + format += `${format !== "" ? ", " : ""}${formats[j].name}`; + } + + ws.cell(currentRow, 1).string(artists_sort).style(style); + ws.cell(currentRow, 2).string(title).style(style); + ws.cell(currentRow, 3).string(genres.join()).style(style); + ws.cell(currentRow, 4).string(styles.join()).style(style); + if (country) { + ws.cell(currentRow, 5).string(country).style(style); + } + if (year) { + ws.cell(currentRow, 6).number(year).style(style); + } + if (released) { + ws.cell(currentRow, 7) + .date(momenttz.tz(released, "Europe/Paris").hour(12)) + .style({ numberFormat: "dd/mm/yyyy" }); + } + ws.cell(currentRow, 8).string(format).style(style); + } + + return wb; + } + + /** + * Méthode permettant de convertir les rows en csv pour importer dans MusicTopus + * @param {Array} rows + * + * @return {string} + */ + static async convertToXml(rows) { + let data = '\n\r'; + + for (let i = 0; i < rows.length; i += 1) { + const { + discogsId, + year, + released, + uri, + artists, + artists_sort, + labels, + series, + companies, + formats, + title, + country, + notes, + identifiers, + videos, + genres, + styles, + tracklist, + extraartists, + images, + thumb, + } = rows[i]; + + let artistsList = ""; + let labelList = ""; + let serieList = ""; + let companiesList = ""; + let formatsList = ""; + let identifiersList = ""; + let videosList = ""; + let genresList = ""; + let stylesList = ""; + let tracklistList = ""; + let extraartistsList = ""; + let imagesList = ""; + + for (let j = 0; j < artists.length; j += 1) { + artistsList += ` + ${Albums.replaceSpecialChars(artists[j].name)} + ${Albums.replaceSpecialChars(artists[j].anv)} + ${Albums.replaceSpecialChars(artists[j].join)} + ${Albums.replaceSpecialChars(artists[j].role)} + ${Albums.replaceSpecialChars( + artists[j].tracks + )} + ${Albums.replaceSpecialChars(artists[j].id)} + ${Albums.replaceSpecialChars( + artists[j].resource_url + )} + ${Albums.replaceSpecialChars( + artists[j].thumbnail_url + )} + `; + } + + for (let j = 0; j < labels.length; j += 1) { + labelList += ` + `; + } + + for (let j = 0; j < series.length; j += 1) { + serieList += ` + ${Albums.replaceSpecialChars(series[j].name)} + ${Albums.replaceSpecialChars(series[j].catno)} + ${Albums.replaceSpecialChars( + series[j].entity_type + )} + ${Albums.replaceSpecialChars( + series[j].entity_type_name + )} + ${Albums.replaceSpecialChars(series[j].id)} + ${Albums.replaceSpecialChars( + series[j].resource_url + )} + ${Albums.replaceSpecialChars( + series[j].thumbnail_url + )} + + `; + } + + for (let j = 0; j < companies.length; j += 1) { + companiesList += ` + ${Albums.replaceSpecialChars(companies[j].name)} + ${Albums.replaceSpecialChars(companies[j].catno)} + ${Albums.replaceSpecialChars( + companies[j].entity_type + )} + ${Albums.replaceSpecialChars( + companies[j].entity_type_name + )} + ${Albums.replaceSpecialChars(companies[j].id)} + ${Albums.replaceSpecialChars( + companies[j].resource_url + )} + ${Albums.replaceSpecialChars( + companies[j].thumbnail_url + )} + + `; + } + + for (let j = 0; j < formats.length; j += 1) { + let descriptions = ""; + if (formats[j].descriptions) { + for ( + let k = 0; + k < formats[j].descriptions.length; + k += 1 + ) { + descriptions += `${formats[j].descriptions[k]} + `; + } + } + formatsList += ` + ${Albums.replaceSpecialChars(formats[j].name)} + ${Albums.replaceSpecialChars(formats[j].qty)} + ${Albums.replaceSpecialChars(formats[j].text)} + + ${descriptions} + + + `; + } + + for (let j = 0; j < identifiers.length; j += 1) { + identifiersList += ` + ${Albums.replaceSpecialChars(identifiers[j].type)} + ${Albums.replaceSpecialChars( + identifiers[j].value + )} + ${Albums.replaceSpecialChars( + identifiers[j].description + )} + + `; + } + + for (let j = 0; j < videos.length; j += 1) { + videosList += ` + `; + } + + for (let j = 0; j < genres.length; j += 1) { + genresList += `${Albums.replaceSpecialChars( + genres[j] + )} + `; + } + + for (let j = 0; j < styles.length; j += 1) { + stylesList += ` + `; + } + + for (let j = 0; j < tracklist.length; j += 1) { + tracklistList += ` + ${Albums.replaceSpecialChars(tracklist[j].title)} + + `; + } + + for (let j = 0; j < extraartists.length; j += 1) { + extraartistsList += ` + ${Albums.replaceSpecialChars(extraartists[j].name)} + ${Albums.replaceSpecialChars(extraartists[j].anv)} + ${Albums.replaceSpecialChars(extraartists[j].join)} + ${Albums.replaceSpecialChars(extraartists[j].role)} + ${Albums.replaceSpecialChars( + extraartists[j].tracks + )} + ${Albums.replaceSpecialChars(extraartists[j].id)} + ${Albums.replaceSpecialChars( + extraartists[j].resource_url + )} + ${Albums.replaceSpecialChars( + extraartists[j].thumbnail_url + )} + + `; + } + + for (let j = 0; j < images.length; j += 1) { + imagesList += ` + ${Albums.replaceSpecialChars(images[j].uri)} + ${Albums.replaceSpecialChars( + images[j].resource_url + )} + ${Albums.replaceSpecialChars( + images[j].resource_url + )} + + `; + } + + data += ` + + ${discogsId} + ${Albums.replaceSpecialChars(title)} + ${Albums.replaceSpecialChars(artists_sort)} + + ${artistsList} + + ${year} + ${Albums.replaceSpecialChars(country)} + ${released} + ${uri} + ${thumb} + + ${labelList} + + + ${serieList} + + + ${companiesList} + + + ${formatsList} + + ${Albums.replaceSpecialChars(notes)} + + ${identifiersList} + + + ${videosList} + + + ${genresList} + + + ${stylesList} + + + ${tracklistList} + + + ${extraartistsList} + + + ${imagesList} + + `; + } + + return `${data}`; + } + + /** + * Méthode permettant de convertir les rows en csv pour importer dans MusicTopus + * @param {Array} rows + * + * @return {string} + */ + static async convertToMusicTopus(rows) { + let data = "itemId;createdAt;updatedAt\n\r"; + + for (let i = 0; i < rows.length; i += 1) { + const { discogsId, createdAt, updatedAt } = rows[i]; + + data += `${discogsId};${createdAt};${updatedAt}\n\r`; + } + + data += "v1.0"; + + return data; + } + /** * Méthode permettant d'ajouter un album dans une collection * @param {Object} req @@ -59,16 +506,15 @@ class Albums extends Pages { */ async getAll() { const { - page = 1, - limit = 4, + page, + limit, + exportFormat = "json", sort = "artists_sort", order = "asc", artists_sort, format, } = this.req.query; - const skip = (page - 1) * limit; - const where = {}; if (artists_sort) { @@ -83,25 +529,47 @@ class Albums extends Pages { ...where, }); + let params = { + sort: { + [sort]: order.toLowerCase() === "asc" ? 1 : -1, + }, + }; + + if (page && limit) { + const skip = (page - 1) * limit; + + params = { + ...params, + skip, + limit, + }; + } + const rows = await AlbumsModel.find( { user: this.req.user._id, ...where, }, [], - { - skip, - limit, - sort: { - [sort]: order.toLowerCase() === "asc" ? 1 : -1, - }, - } + params ); - return { - rows, - count, - }; + switch (exportFormat) { + case "csv": + return Albums.convertToCsv(rows); + case "xls": + return Albums.convertToXls(rows); + case "xml": + return Albums.convertToXml(rows); + case "musictopus": + return Albums.convertToMusicTopus(rows); + case "json": + default: + return { + rows, + count, + }; + } } /** @@ -137,6 +605,20 @@ class Albums extends Pages { this.setPageContent("artists", artists); this.setPageContent("formats", formats); } + + /** + * Méthode permettant d'afficher le détails d'un album + */ + async loadItem() { + const { itemId: _id } = this.req.params; + const { _id: User } = this.req.user; + const item = await AlbumsModel.findOne({ + _id, + User, + }); + + this.setPageContent("item", item); + } } export default Albums; diff --git a/src/routes/api/v1/albums.js b/src/routes/api/v1/albums.js index 5d0c68c..164ea40 100644 --- a/src/routes/api/v1/albums.js +++ b/src/routes/api/v1/albums.js @@ -13,10 +13,24 @@ router try { const albums = new Albums(req); const data = await albums.getAll(); + const { exportFormat = "json" } = req.query; - sendResponse(req, res, data); + switch (exportFormat) { + case "csv": + case "musictopus": + res.header("Content-Type", "text/csv"); + return res.status(200).send(data); + case "xml": + res.header("Content-type", "text/xml"); + return res.status(200).send(data); + case "xls": + return data.write("musictopus.xls", res); + case "json": + default: + return sendResponse(req, res, data); + } } catch (err) { - next(err); + return next(err); } }) .post(ensureLoggedIn("/connexion"), async (req, res, next) => { diff --git a/src/routes/index.js b/src/routes/index.js index 8a340a0..95c9a45 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -4,7 +4,6 @@ import { ensureLoggedIn } from "connect-ensure-login"; import Pages from "../middleware/Pages"; import Auth from "../middleware/Auth"; -import Albums from "../middleware/Albums"; import render from "../libs/format"; @@ -89,24 +88,6 @@ router } }); -router - .route("/ma-collection") - .get(ensureLoggedIn("/connexion"), async (req, res, next) => { - try { - const page = new Albums(req, "mon-compte/ma-collection"); - - await page.loadMyCollection(); - - if (page.getPageContent("artists").length > 0) { - render(res, page); - } else { - res.redirect("/ajouter-un-album"); - } - } catch (err) { - next(err); - } - }); - router.route("/nous-contacter").get(async (req, res, next) => { try { const page = new Pages(req, "nous-contacter"); diff --git a/src/routes/ma-collection.js b/src/routes/ma-collection.js new file mode 100644 index 0000000..51ff0ee --- /dev/null +++ b/src/routes/ma-collection.js @@ -0,0 +1,53 @@ +import express from "express"; +import { ensureLoggedIn } from "connect-ensure-login"; + +import Albums from "../middleware/Albums"; + +import render from "../libs/format"; + +// eslint-disable-next-line new-cap +const router = express.Router(); + +router.route("/").get(ensureLoggedIn("/connexion"), async (req, res, next) => { + try { + const page = new Albums(req, "mon-compte/ma-collection"); + + await page.loadMyCollection(); + + if (page.getPageContent("artists").length > 0) { + render(res, page); + } else { + res.redirect("/ajouter-un-album"); + } + } catch (err) { + next(err); + } +}); + +router + .route("/exporter") + .get(ensureLoggedIn("/connexion"), async (req, res, next) => { + try { + const page = new Albums(req, "mon-compte/ma-collection/exporter"); + + render(res, page); + } catch (err) { + next(err); + } + }); + +router + .route("/:itemId") + .get(ensureLoggedIn("/connexion"), async (req, res, next) => { + try { + const page = new Albums(req, "mon-compte/ma-collection/details"); + + await page.loadItem(); + + render(res, page); + } catch (err) { + next(err); + } + }); + +export default router; diff --git a/views/index.ejs b/views/index.ejs index 8d2364f..ddf0b8c 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -68,6 +68,9 @@ Ma collection + + Exporter ma collection + <% } %> diff --git a/views/pages/ajouter-un-album.ejs b/views/pages/ajouter-un-album.ejs index 8a26488..bdebb4e 100644 --- a/views/pages/ajouter-un-album.ejs +++ b/views/pages/ajouter-un-album.ejs @@ -52,12 +52,12 @@ -