diff --git a/package.json b/package.json
index 8c53d5a..b5aea27 100644
--- a/package.json
+++ b/package.json
@@ -27,9 +27,6 @@
},
"license": "GPL-3.0-or-later",
"devDependencies": {
- "@babel/cli": "^7.17.0",
- "@babel/core": "^7.17.2",
- "@babel/preset-env": "^7.16.11",
"eslint": "^8.9.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-prettier": "^8.3.0",
@@ -38,11 +35,12 @@
"husky": "^7.0.4",
"lint-staged": "^12.3.3",
"nodemon": "^2.0.15",
- "npm-run-all": "^4.1.5",
- "prettier": "^2.5.1",
- "rimraf": "^3.0.2"
+ "prettier": "^2.5.1"
},
"dependencies": {
+ "@babel/cli": "^7.17.0",
+ "@babel/core": "^7.17.2",
+ "@babel/preset-env": "^7.16.11",
"axios": "^0.26.0",
"connect-ensure-login": "^0.1.1",
"connect-flash": "^0.1.1",
@@ -54,14 +52,17 @@
"excel4node": "^1.7.2",
"express": "^4.17.2",
"express-session": "^1.17.2",
+ "joi": "^17.6.0",
"knacss": "^8.0.4",
"moment": "^2.29.1",
"moment-timezone": "^0.5.34",
"mongoose": "^6.2.1",
"mongoose-unique-validator": "^3.0.0",
+ "npm-run-all": "^4.1.5",
"passport": "^0.5.2",
"passport-http": "^0.3.0",
"passport-local": "^1.0.0",
+ "rimraf": "^3.0.2",
"sass": "^1.49.7",
"vue": "^3.2.31"
},
diff --git a/public/font/icon.eot b/public/font/icon.eot
index 8664abe..a17c0f6 100644
Binary files a/public/font/icon.eot and b/public/font/icon.eot differ
diff --git a/public/font/icon.svg b/public/font/icon.svg
index 51f7015..d5ac2f6 100644
--- a/public/font/icon.svg
+++ b/public/font/icon.svg
@@ -34,6 +34,8 @@
+
+
diff --git a/public/font/icon.ttf b/public/font/icon.ttf
index 1f0de7e..4ccfa6d 100644
Binary files a/public/font/icon.ttf and b/public/font/icon.ttf differ
diff --git a/public/font/icon.woff b/public/font/icon.woff
index 6474d86..3262198 100644
Binary files a/public/font/icon.woff and b/public/font/icon.woff differ
diff --git a/public/font/icon.woff2 b/public/font/icon.woff2
index 72207f3..f73baf4 100644
Binary files a/public/font/icon.woff2 and b/public/font/icon.woff2 differ
diff --git a/public/js/main.js b/public/js/main.js
index 994b2ae..895683a 100644
--- a/public/js/main.js
+++ b/public/js/main.js
@@ -2,13 +2,16 @@
* Fonction permettant d'afficher un message dans un toastr
* @param {String} message
*/
- function showToastr(message) {
+ function showToastr(message, success = false) {
let x = document.getElementById("toastr");
if ( message ) {
x.getElementsByTagName("SPAN")[0].innerHTML = message;
}
- x.className = `${x.className} show`;
+ x.className = `${x.className} show`.replace("sucess", "");
+ if ( success ) {
+ x.className = `${x.className} success`;
+ }
setTimeout(function(){ x.className = x.className.replace("show", ""); }, 3000);
};
diff --git a/sass/ma-collection.scss b/sass/collection.scss
similarity index 90%
rename from sass/ma-collection.scss
rename to sass/collection.scss
index af22f2a..6412fe1 100644
--- a/sass/ma-collection.scss
+++ b/sass/collection.scss
@@ -1,4 +1,9 @@
-.ma-collection {
+.collection {
+ h1 {
+ i {
+ cursor: pointer;
+ }
+ }
.filters {
display: flex;
justify-content: end;
diff --git a/sass/colors.scss b/sass/colors.scss
index 5759f90..f100c6b 100644
--- a/sass/colors.scss
+++ b/sass/colors.scss
@@ -52,6 +52,23 @@ $pagination-hover-color: rgb(115, 151, 186);
--box-shadow-color: #{rgba($nord4, 0.35)};
--border-color: #{$nord4};
+
+ --nord0: #{$nord0};
+ --nord1: #{$nord1};
+ --nord2: #{$nord2};
+ --nord3: #{$nord3};
+ --nord4: #{$nord4};
+ --nord5: #{$nord5};
+ --nord6: #{$nord6};
+ --nord7: #{$nord7};
+ --nord8: #{$nord8};
+ --nord9: #{$nord9};
+ --nord10: #{$nord10};
+ --nord11: #{$nord11};
+ --nord12: #{$nord12};
+ --nord13: #{$nord13};
+ --nord14: #{$nord14};
+ --nord15: #{$nord15};
}
[data-theme="dark"] {
diff --git a/sass/composants.scss b/sass/composants.scss
new file mode 100644
index 0000000..b9d4e65
--- /dev/null
+++ b/sass/composants.scss
@@ -0,0 +1,13 @@
+.composants {
+ .couleur {
+ margin-bottom: 1rem;
+ text-align: center;
+
+ border: 1px solid var(--input-active-color);
+ box-shadow: var(--box-shadow-color) 0px 3px 6px 0px;
+
+ div {
+ height: 56px;
+ }
+ }
+}
\ No newline at end of file
diff --git a/sass/global.scss b/sass/global.scss
index 491b9ca..454cd14 100644
--- a/sass/global.scss
+++ b/sass/global.scss
@@ -82,4 +82,8 @@ html {
@include respond-to("small-up") {
display: initial;
}
+}
+
+.is-danger {
+ color: $nord12;
}
\ No newline at end of file
diff --git a/sass/icons.scss b/sass/icons.scss
index 70ce584..4250307 100644
--- a/sass/icons.scss
+++ b/sass/icons.scss
@@ -46,6 +46,7 @@
.icon-link-ext:before { content: '\f08e'; } /* '' */
.icon-sun:before { content: '\f185'; } /* '' */
.icon-moon:before { content: '\f186'; } /* '' */
+.icon-share:before { content: '\f1e0'; } /* '' */
.icon-trash:before { content: '\f1f8'; } /* '' */
.icon-blind:before { content: '\f29d'; } /* '' */
diff --git a/sass/index.scss b/sass/index.scss
index aa7557a..c54fc3c 100644
--- a/sass/index.scss
+++ b/sass/index.scss
@@ -43,5 +43,6 @@
@import './error';
@import './home';
@import './ajouter-un-album';
-@import './ma-collection';
-@import './ma-collection-details';
\ No newline at end of file
+@import './collection';
+@import './ma-collection-details';
+@import './composants';
\ No newline at end of file
diff --git a/sass/toast.scss b/sass/toast.scss
index 21d1730..46777e7 100644
--- a/sass/toast.scss
+++ b/sass/toast.scss
@@ -13,6 +13,11 @@
color: $button-alternate-color;
border-radius: 6px;
+ &.success {
+ background-color: $success-color;
+ color: $button-font-color;
+ }
+
&.show {
visibility: visible;
animation: toastrFadein 0.5s, toastrFadeout 0.5s 2.5s;
diff --git a/src/app.js b/src/app.js
index 0b32d82..6d1e507 100644
--- a/src/app.js
+++ b/src/app.js
@@ -13,9 +13,11 @@ import { isXhr } from "./helpers";
import indexRouter from "./routes";
import maCollectionRouter from "./routes/ma-collection";
+import collectionRouter from "./routes/collection";
import importAlbumRouterApiV1 from "./routes/api/v1/albums";
import importSearchRouterApiV1 from "./routes/api/v1/search";
+import importMeRouterApiV1 from "./routes/api/v1/me";
// Mongoose schema init
require("./models/users");
@@ -82,8 +84,10 @@ app.use(
app.use("/", indexRouter);
app.use("/ma-collection", maCollectionRouter);
+app.use("/collection", collectionRouter);
app.use("/api/v1/albums", importAlbumRouterApiV1);
app.use("/api/v1/search", importSearchRouterApiV1);
+app.use("/api/v1/me", importMeRouterApiV1);
// Handle 404
app.use((req, res) => {
@@ -113,7 +117,10 @@ app.use((error, req, res, next) => {
} else {
res.status(error.errorCode || 500);
res.render("index", {
- page: { title: "500: Oups… le serveur a crashé !", error },
+ page: {
+ title: error.title || "500: Oups… le serveur a crashé !",
+ error,
+ },
errorCode: error.errorCode || 500,
viewname: "error",
user: req.user || null,
diff --git a/src/libs/error.js b/src/libs/error.js
index 562265f..13470f2 100644
--- a/src/libs/error.js
+++ b/src/libs/error.js
@@ -4,9 +4,10 @@
class ErrorEvent extends Error {
/**
* @param {Number} errorCode
+ * @param {String} title
* @param {Mixed} ...params
*/
- constructor(errorCode, ...params) {
+ constructor(errorCode, title, ...params) {
super(...params);
if (Error.captureStackTrace) {
@@ -14,6 +15,7 @@ class ErrorEvent extends Error {
}
this.errorCode = parseInt(errorCode, 10);
+ this.title = title;
this.date = new Date();
}
}
diff --git a/src/middleware/Albums.js b/src/middleware/Albums.js
index 4796207..8025973 100644
--- a/src/middleware/Albums.js
+++ b/src/middleware/Albums.js
@@ -5,12 +5,19 @@ import xl from "excel4node";
import Pages from "./Pages";
import AlbumsModel from "../models/albums";
+import UsersModel from "../models/users";
import ErrorEvent from "../libs/error";
/**
* Classe permettant la gestion des albums d'un utilisateur
*/
class Albums extends Pages {
+ /**
+ * Méthode permettant de remplacer certains cartactères par leur équivalents html
+ * @param {String} str
+ *
+ * @return {String}
+ */
static replaceSpecialChars(str) {
if (!str) {
return "";
@@ -487,7 +494,7 @@ class Albums extends Pages {
static async getAllDistincts(field, user) {
const distincts = await AlbumsModel.find(
{
- user,
+ User: user,
},
[],
{
@@ -513,8 +520,11 @@ class Albums extends Pages {
order = "asc",
artists_sort,
format,
+ userId: collectionUserId,
} = this.req.query;
+ let userId = this.req.user?._id;
+
const where = {};
if (artists_sort) {
@@ -524,8 +534,35 @@ class Albums extends Pages {
where["formats.name"] = format;
}
+ if (!this.req.user && !collectionUserId) {
+ throw new ErrorEvent(
+ 401,
+ "Cette collection n'est pas publique",
+ "Cette collection n'est pas publique"
+ );
+ }
+
+ if (collectionUserId) {
+ const userIsSharingCollection = await UsersModel.findById(
+ collectionUserId
+ );
+
+ if (
+ !userIsSharingCollection ||
+ !userIsSharingCollection.isPublicCollection
+ ) {
+ throw new ErrorEvent(
+ 401,
+ "Cette collection n'est pas publique",
+ "Cette collection n'est pas publique"
+ );
+ }
+
+ userId = userIsSharingCollection._id;
+ }
+
const count = await AlbumsModel.count({
- user: this.req.user._id,
+ User: userId,
...where,
});
@@ -547,7 +584,7 @@ class Albums extends Pages {
const rows = await AlbumsModel.find(
{
- user: this.req.user._id,
+ User: userId,
...where,
},
[],
@@ -619,6 +656,29 @@ class Albums extends Pages {
this.setPageContent("item", item);
}
+
+ /**
+ * Méthode permettant de créer la page "collection/:userId"
+ */
+ async loadPublicCollection() {
+ const { userId } = this.req.params;
+
+ const user = await UsersModel.findById(userId);
+
+ if (!user || !user.isPublicCollection) {
+ throw new ErrorEvent(
+ 401,
+ "Cet utilisateur ne souhaite pas partager sa collection"
+ );
+ }
+
+ const artists = await Albums.getAllDistincts("artists_sort", userId);
+ const formats = await Albums.getAllDistincts("formats.name", userId);
+
+ this.setPageContent("username", user.username);
+ this.setPageContent("artists", artists);
+ this.setPageContent("formats", formats);
+ }
}
export default Albums;
diff --git a/src/middleware/Me.js b/src/middleware/Me.js
new file mode 100644
index 0000000..ae2712c
--- /dev/null
+++ b/src/middleware/Me.js
@@ -0,0 +1,45 @@
+import Joi from "joi";
+
+import UsersModel from "../models/users";
+
+/**
+ * Classe permettant la gestion de l'utilisateur connecté
+ */
+class Me {
+ constructor(req) {
+ this.req = req;
+ }
+
+ /**
+ * Méthode permettant de modifier le profil d'un utilisateur
+ * @return {Object}
+ */
+ async patchMe() {
+ const { body, user } = this.req;
+
+ const schema = Joi.object({
+ isPublicCollection: Joi.boolean(),
+ });
+
+ const value = await schema.validateAsync(body);
+ const update = await UsersModel.findByIdAndUpdate(
+ user._id,
+ { $set: value },
+ { new: true }
+ );
+
+ await new Promise((resolve, reject) => {
+ this.req.login(update, (err) => {
+ if (err) {
+ return reject(err);
+ }
+
+ return resolve(null);
+ });
+ });
+
+ return update;
+ }
+}
+
+export default Me;
diff --git a/src/models/users.js b/src/models/users.js
index 0807ccb..96629c0 100644
--- a/src/models/users.js
+++ b/src/models/users.js
@@ -1,5 +1,7 @@
/* eslint-disable func-names */
/* eslint-disable no-invalid-this */
+/* eslint-disable no-param-reassign */
+
import mongoose from "mongoose";
import uniqueValidator from "mongoose-unique-validator";
import crypto from "crypto";
@@ -23,8 +25,20 @@ const UserSchema = new mongoose.Schema(
},
hash: String,
salt: String,
+ isPublicCollection: {
+ type: Boolean,
+ default: false,
+ },
},
- { timestamps: true }
+ {
+ timestamps: true,
+ toJSON: {
+ transform(doc, ret) {
+ delete ret.hash;
+ delete ret.salt;
+ },
+ },
+ }
);
UserSchema.plugin(uniqueValidator, { message: "est déjà utilisé" });
diff --git a/src/routes/api/v1/albums.js b/src/routes/api/v1/albums.js
index c8f0f1a..e1caa1f 100644
--- a/src/routes/api/v1/albums.js
+++ b/src/routes/api/v1/albums.js
@@ -9,7 +9,7 @@ const router = express.Router();
router
.route("/")
- .get(ensureLoggedIn("/connexion"), async (req, res, next) => {
+ .get(async (req, res, next) => {
try {
const albums = new Albums(req);
const data = await albums.getAll();
diff --git a/src/routes/api/v1/me.js b/src/routes/api/v1/me.js
new file mode 100644
index 0000000..42c46b9
--- /dev/null
+++ b/src/routes/api/v1/me.js
@@ -0,0 +1,24 @@
+import express from "express";
+import { ensureLoggedIn } from "connect-ensure-login";
+
+import { sendResponse } from "../../../libs/format";
+
+import Me from "../../../middleware/Me";
+
+// eslint-disable-next-line new-cap
+const router = express.Router();
+
+router
+ .route("/")
+ .patch(ensureLoggedIn("/connexion"), async (req, res, next) => {
+ try {
+ const me = new Me(req);
+ const data = await me.patchMe();
+
+ return sendResponse(req, res, data);
+ } catch (err) {
+ return next(err);
+ }
+ });
+
+export default router;
diff --git a/src/routes/collection.js b/src/routes/collection.js
new file mode 100644
index 0000000..6584992
--- /dev/null
+++ b/src/routes/collection.js
@@ -0,0 +1,22 @@
+import express from "express";
+
+import Albums from "../middleware/Albums";
+
+import render from "../libs/format";
+
+// eslint-disable-next-line new-cap
+const router = express.Router();
+
+router.route("/:userId").get(async (req, res, next) => {
+ try {
+ const page = new Albums(req, "collection");
+
+ await page.loadPublicCollection();
+
+ render(res, page);
+ } catch (err) {
+ next(err);
+ }
+});
+
+export default router;
diff --git a/src/routes/ma-collection.js b/src/routes/ma-collection.js
index 51ff0ee..653c844 100644
--- a/src/routes/ma-collection.js
+++ b/src/routes/ma-collection.js
@@ -10,7 +10,7 @@ const router = express.Router();
router.route("/").get(ensureLoggedIn("/connexion"), async (req, res, next) => {
try {
- const page = new Albums(req, "mon-compte/ma-collection");
+ const page = new Albums(req, "mon-compte/ma-collection/index");
await page.loadMyCollection();
diff --git a/views/error.ejs b/views/error.ejs
index b0e5112..8f80430 100644
--- a/views/error.ejs
+++ b/views/error.ejs
@@ -5,7 +5,9 @@
<% } %>
+ <% if ( process.env.NODE_ENV !== 'production' ) { %>
+ <% } %>
\ No newline at end of file
diff --git a/views/pages/mon-compte/ma-collection.ejs b/views/pages/collection.ejs
similarity index 78%
rename from views/pages/mon-compte/ma-collection.ejs
rename to views/pages/collection.ejs
index b754ccf..da22489 100644
--- a/views/pages/mon-compte/ma-collection.ejs
+++ b/views/pages/collection.ejs
@@ -1,5 +1,8 @@
-
- Ma collection
+
+
+ Collection de <%= page.username %>
+
+
Artiste
@@ -40,12 +43,11 @@
- {{ item.artists_sort}} - {{ item.title }}
-
+ {{ item.artists_sort}} - {{ item.title }}
-
+
Année : {{ item.year }}
@@ -91,23 +93,14 @@
-
-
-
-
-
-
- Êtes-vous sûr de vouloir supprimer cet album ?
-
-
-
-
diff --git a/views/pages/composants.ejs b/views/pages/composants.ejs
index 7c17b38..dc4f8f2 100644
--- a/views/pages/composants.ejs
+++ b/views/pages/composants.ejs
@@ -1,8 +1,9 @@
-
+
Les composants
Les titres
+ Les couleurs
Les grilles
Les boutons
Les formulaires
@@ -24,6 +25,87 @@
Titre de niveau 5
Titre de niveau 6
+ Les couleurs
+ Polar Night
+
+ Snow Storm
+
+ Frost
+
+ Aurora
+
+
+ Vous pourrez trouver plus d'informations sur le site offciel du projet nord.
+
+
Les grilles
Se référer à la documentation de Knacss .
@@ -225,13 +307,15 @@
.icon-link-ext
.icon-heart
.icon-eye
+ .icon-left-open
+ .icon-right-open
+ .icon-export
+ .icon-share
.icon-spin
.icon-sun
.icon-moon
.icon-trash
.icon-blind
- .icon-left-open
- .icon-right-open
Les listes
-
+
Exporter
diff --git a/views/pages/mon-compte/ma-collection/index.ejs b/views/pages/mon-compte/ma-collection/index.ejs
new file mode 100644
index 0000000..4f401b7
--- /dev/null
+++ b/views/pages/mon-compte/ma-collection/index.ejs
@@ -0,0 +1,279 @@
+
+
+ Ma collection
+
+
+
+ Voir ma collection partagée
+
+
+
+ Artiste
+
+ Tous
+ <%
+ for (let i = 0; i < page.artists.length; i += 1 ) {
+ __append(`${page.artists[i]} `);
+ }
+ %>
+
+
+
+ Format
+
+ Tous
+ <%
+ for (let i = 0; i < page.formats.length; i += 1 ) {
+ __append(`${page.formats[i]} `);
+ }
+ %>
+
+
+
+ Trier par
+
+ Artiste (A-Z)
+ Artiste (Z-A)
+ Année (A-Z)
+ Année (Z-A)
+ Pays (A-Z)
+ Pays (Z-A)
+ Format (A-Z)
+ Format (Z-A)
+
+
+
+
+
+
+ {{ item.artists_sort}} - {{ item.title }}
+
+
+
+
+
+
+
+ Année : {{ item.year }}
+
+ Pays : {{ item.country }}
+
+
+ Format :
+
+ {{ format.name }}
+
+ (
+ {{description}},
+ )
+
+ ,
+
+
+
+ Genre : {{ genre }},
+
+ Style : {{ style }},
+
+
+
+
+
+ Nombre total d'éléments : {{total}}
+
+
+
+
+
+
+
+
+ Êtes-vous sûr de vouloir supprimer cet album ?
+
+
+
+
+
+
+
+
+ Partager ma collection
+
+
+
+ Votre collection sera visible de toute personne disposant du lien suivant :
+
+ {{shareLink}}
+
+ Ce lien permet uniquement de visualiser l'ensemble de votre collection mais ne perment en aucun cas de la modifier.
+
+ Vous pourrez à tout moment supprimer le lien de partage en cliquant à nouveau sur l'icône sur votre collection.
+
+
+ Vous êtes sur le point de rendre votre collection privée.
+
+ Toute les personnes ayant le lien partagé ne pourront plus accéder à votre collection.
+
+ Vous pourrez à tout moment rendre à nouveau votre collection publique en cliquant sur l'icône .
+
+
+
+ Partager
+ Supprimer
+ Annuler
+
+
+
+
+
+