Compare commits

..

No commits in common. "06752ebcec9213b73d416f5ca628af282b589df9" and "6d0405d129eee2faa38483c31028b62ec484f34a" have entirely different histories.

26 changed files with 92 additions and 535 deletions

View file

@ -210,23 +210,22 @@ Voici la liste des variables configurables :
``` ```
NODE_ENV # Environnement dans lequel exécuter le projet (development ou production) NODE_ENV # Environnement dans lequel exécuter le projet (development ou production)
PORT # Port sur lequel éxécuter le serveur (3001 par défaut) PORT # Port sur lequel éxécuter le serveur (par défaut 3001)
MONGODB_URI # Url du serveur mongo (mongodb://musictopus-db/musictopus par défaut) MONGODB_URI # Url du serveur mongo (par défaut mongodb://musictopus-db/musictopus)
SECRET # Hash utilisé pour pour sauvegardé les dessions (waemaeMe5ahc6ce1chaeKohKa6Io8Eik par défault) SECRET # Hash utilisé pour pour sauvegardé les dessions (par défaut waemaeMe5ahc6ce1chaeKohKa6Io8Eik)
DISCOGS_TOKEN # Token Discogs (vous devez créer un compte sur discogs afin d'en obtenir un gratuitement) DISCOGS_TOKEN # Token Discogs (vous devez créer un compte sur discogs afin d'en obtenir un gratuitement)
FORMSPREE_ID # Id du formulaire formspree pour la page "nous-contacter" FORMSPREE_ID # Id du formulaire formspree pour la page "nous-contacter"
MATOMO_URL # Url vers l'instance matomo (exemple: https://analytics.darkou.fr/) MATOMO_URL # Url vers l'instance matomo (exemple: https://analytics.darkou.fr/)
MATOMO_ID # Id du site sur votre instance matomo (exemple: 1) MATOMO_ID # Id du site sur votre instance matomo (exemple: 1)
SITE_NAME # Nom du site utilisé dans le titre des pages (MusicTopus par défaut) SITE_NAME # Nom du site (utilisé dans le titre des pages)
AWS_ACCESS_KEY_ID # Clé d'accès AWS AWS_ACCESS_KEY_ID # Clé d'accès AWS
AWS_SECRET_ACCESS_KEY # Clé secrète AWS AWS_SECRET_ACCESS_KEY # Clé secrète AWS
S3_ENDPOINT # Url de l'instance aws (s3.fr-par.scw.cloud par défaut) S3_ENDPOINT # Url de l'instance aws (s3.fr-par.scw.cloud pour scaleway france par exemple)
S3_SIGNATURE # Version de la signature AWS (s3v4 par défaut) S3_SIGNATURE # Version de la signature AWS (s3v4 pour scaleway par exemple)
S3_BASEFOLDER # Nom du sous dossier dans lequel seront mis les pochettes des albums (dev par défaut) S3_BASEFOLDER # Nom du sous dossier dans lequel seront mis les pochettes des albums
S3_BUCKET # Nom du bucket (musictopus par défaut, à changer impérativement si vous voulez que cela fonctionne) S3_BUCKET # Nom du bucket
JOBS_HEADER_KEY # Nom du header utilisé pour l'identification des tâches cron (musictopus par défaut) JOBS_HEADER_KEY # Nom du header utilisé pour l'identification des tâches cron (par exemple musictopus)
JOBS_HEADER_VALUE # Valeur de la clé (ooYee9xok7eigo2shiePohyoGh1eepew par défaut) JOBS_HEADER_VALUE # Valeur de la clé
REGISTRATION_OPEN # true/false en fonction de si vous souhaitez activer ou non l'inscription à votre instance (true par défaut)
``` ```
## Contributeurs ## Contributeurs

View file

@ -36,7 +36,6 @@ services:
S3_SIGNATURE: ${S3_SIGNATURE} S3_SIGNATURE: ${S3_SIGNATURE}
JOBS_HEADER_KEY: ${JOBS_HEADER_KEY} JOBS_HEADER_KEY: ${JOBS_HEADER_KEY}
JOBS_HEADER_VALUE: ${JOBS_HEADER_VALUE} JOBS_HEADER_VALUE: ${JOBS_HEADER_VALUE}
REGISTRATION_OPEN: ${REGISTRATION_OPEN}
networks: networks:
- musictopus - musictopus
musictopus-db: musictopus-db:

View file

@ -36,7 +36,6 @@ services:
S3_SIGNATURE: ${S3_SIGNATURE} S3_SIGNATURE: ${S3_SIGNATURE}
JOBS_HEADER_KEY: ${JOBS_HEADER_KEY} JOBS_HEADER_KEY: ${JOBS_HEADER_KEY}
JOBS_HEADER_VALUE: ${JOBS_HEADER_VALUE} JOBS_HEADER_VALUE: ${JOBS_HEADER_VALUE}
REGISTRATION_OPEN: ${REGISTRATION_OPEN}
networks: networks:
- musictopus - musictopus
musictopus-db: musictopus-db:

View file

@ -47,8 +47,6 @@
"connect-flash": "^0.1.1", "connect-flash": "^0.1.1",
"connect-mongo": "^4.6.0", "connect-mongo": "^4.6.0",
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
"date-fns": "^2.28.0",
"date-fns-tz": "^1.3.3",
"debug": "^4.3.3", "debug": "^4.3.3",
"disconnect": "^1.2.2", "disconnect": "^1.2.2",
"ejs": "^3.1.6", "ejs": "^3.1.6",
@ -57,6 +55,8 @@
"express-session": "^1.17.2", "express-session": "^1.17.2",
"joi": "^17.6.0", "joi": "^17.6.0",
"knacss": "^8.0.4", "knacss": "^8.0.4",
"moment": "^2.29.1",
"moment-timezone": "^0.5.34",
"mongoose": "^6.2.1", "mongoose": "^6.2.1",
"mongoose-unique-validator": "^3.0.0", "mongoose-unique-validator": "^3.0.0",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",

View file

View file

@ -17,7 +17,6 @@
} }
@include respond-to("small-up") { @include respond-to("small-up") {
width: 33%;
&:last-child { &:last-child {
padding-right: 0; padding-right: 0;
} }
@ -29,17 +28,6 @@
} }
} }
.showMoreFilters {
cursor: pointer;
.up::before {
transform: rotate(90deg);
}
.down::before {
transform: rotate(270deg);
}
}
.list{ .list{
.title { .title {
.icon-trash { .icon-trash {

View file

@ -11,12 +11,4 @@
.header { .header {
font-weight: 800; font-weight: 800;
} }
&.info {
background-color: $warning-color;
}
&.success {
background-color: $success-color;
}
} }

View file

@ -132,8 +132,6 @@
} }
&:hover { &:hover {
background-color: var(--default-color);
.navbar-link { .navbar-link {
background-color: var(--default-hl-color); background-color: var(--default-hl-color);
color: rgba(0,0,0,.7); color: rgba(0,0,0,.7);
@ -254,13 +252,6 @@
padding-bottom: .5rem; padding-bottom: .5rem;
padding-top: .5rem; padding-top: .5rem;
hr {
background-color: var(--font-color);
border: none;
height: 2px;
margin: .5rem 0;
}
.navbar-item { .navbar-item {
cursor: pointer; cursor: pointer;
padding-left: 1.5rem; padding-left: 1.5rem;

View file

@ -15,7 +15,6 @@ import { isXhr } from "./helpers";
import indexRouter from "./routes"; import indexRouter from "./routes";
import maCollectionRouter from "./routes/ma-collection"; import maCollectionRouter from "./routes/ma-collection";
import monCompteRouter from "./routes/mon-compte";
import collectionRouter from "./routes/collection"; import collectionRouter from "./routes/collection";
import importJobsRouter from "./routes/jobs"; import importJobsRouter from "./routes/jobs";
@ -84,7 +83,6 @@ app.use(
); );
app.use("/", indexRouter); app.use("/", indexRouter);
app.use("/mon-compte", monCompteRouter);
app.use("/ma-collection", maCollectionRouter); app.use("/ma-collection", maCollectionRouter);
app.use("/collection", collectionRouter); app.use("/collection", collectionRouter);
app.use("/jobs", importJobsRouter); app.use("/jobs", importJobsRouter);
@ -99,22 +97,15 @@ app.use((req, res) => {
} else { } else {
res.status(404).render("index", { res.status(404).render("index", {
page: { title: `404: Cette page n'existe pas.` }, page: { title: `404: Cette page n'existe pas.` },
viewname: "error",
session: req.session || null,
flash: {
info: req.flash("info"),
error: [
...req.flash("error"),
...(req.session?.flash?.error || []),
],
success: req.flash("success"),
},
query: req.query,
params: req.params,
user: req.user,
config,
getBaseUrl: null,
errorCode: 404, errorCode: 404,
viewname: "error",
user: req.user || null,
config,
session: req.session || null,
flashInfo: null,
query: null,
params: null,
error: null,
}); });
} }
}); });
@ -131,22 +122,15 @@ app.use((error, req, res, next) => {
title: error.title || "500: Oups… le serveur a crashé !", title: error.title || "500: Oups… le serveur a crashé !",
error, error,
}, },
viewname: "error",
session: req.session || null,
flash: {
info: req.flash("info"),
error: [
...req.flash("error"),
...(req.session?.flash?.error || []),
],
success: req.flash("success"),
},
query: req.query,
params: req.params,
user: req.user,
config,
getBaseUrl: null,
errorCode: error.errorCode || 500, errorCode: error.errorCode || 500,
viewname: "error",
user: req.user || null,
config,
session: req.session || null,
flashInfo: null,
query: null,
params: null,
error: null,
}); });
next(); next();

View file

@ -17,6 +17,4 @@ module.exports = {
jobsHeaderKey: process.env.JOBS_HEADER_KEY || "musictopus", jobsHeaderKey: process.env.JOBS_HEADER_KEY || "musictopus",
jobsHeaderValue: jobsHeaderValue:
process.env.JOBS_HEADER_VALUE || "ooYee9xok7eigo2shiePohyoGh1eepew", process.env.JOBS_HEADER_VALUE || "ooYee9xok7eigo2shiePohyoGh1eepew",
registrationOpen:
(process.env.REGISTRATION_OPEN || "true").toLowerCase() === "true",
}; };

View file

@ -1,4 +1,4 @@
import { format as formatDate } from "date-fns"; import moment from "moment";
import Pages from "./Pages"; import Pages from "./Pages";
import Export from "./Export"; import Export from "./Export";
@ -26,7 +26,7 @@ class Albums extends Pages {
User: user._id, User: user._id,
}; };
data.released = data.released data.released = data.released
? new Date(data.released.replace("-00", "-01")) ? moment(data.released.replace("-00", "-01"))
: null; : null;
delete data.id; delete data.id;
@ -81,9 +81,6 @@ class Albums extends Pages {
order = "asc", order = "asc",
artists_sort, artists_sort,
format, format,
year,
genre,
style,
userId: collectionUserId, userId: collectionUserId,
} = this.req.query; } = this.req.query;
@ -97,20 +94,11 @@ class Albums extends Pages {
if (format) { if (format) {
where["formats.name"] = format; where["formats.name"] = format;
} }
if (year) {
where.year = year;
}
if (genre) {
where.genres = genre;
}
if (style) {
where.styles = style;
}
if (!this.req.user && !collectionUserId) { if (!this.req.user && !collectionUserId) {
throw new ErrorEvent( throw new ErrorEvent(
401, 401,
"Collection", "Cette collection n'est pas publique",
"Cette collection n'est pas publique" "Cette collection n'est pas publique"
); );
} }
@ -126,7 +114,7 @@ class Albums extends Pages {
) { ) {
throw new ErrorEvent( throw new ErrorEvent(
401, 401,
"Collection", "Cette collection n'est pas publique",
"Cette collection n'est pas publique" "Cette collection n'est pas publique"
); );
} }
@ -211,21 +199,9 @@ class Albums extends Pages {
"formats.name", "formats.name",
this.req.user._id this.req.user._id
); );
const years = await Albums.getAllDistincts("year", this.req.user._id);
const genres = await Albums.getAllDistincts(
"genres",
this.req.user._id
);
const styles = await Albums.getAllDistincts(
"styles",
this.req.user._id
);
this.setPageContent("artists", artists); this.setPageContent("artists", artists);
this.setPageContent("formats", formats); this.setPageContent("formats", formats);
this.setPageContent("years", years);
this.setPageContent("genres", genres);
this.setPageContent("styles", styles);
this.setPageTitle("Ma collection"); this.setPageTitle("Ma collection");
} }
@ -235,16 +211,11 @@ class Albums extends Pages {
async loadItem() { async loadItem() {
const { itemId: _id } = this.req.params; const { itemId: _id } = this.req.params;
const { _id: User } = this.req.user; const { _id: User } = this.req.user;
const album = await AlbumsModel.findOne({ const item = await AlbumsModel.findOne({
_id, _id,
User, User,
}); });
const item = {
...album.toJSON(),
released: formatDate(album.released, "MM/dd/yyyy"),
};
this.setPageContent("item", item); this.setPageContent("item", item);
this.setPageTitle( this.setPageTitle(
`Détails de l'album ${item.title} de ${item.artists_sort}` `Détails de l'album ${item.title} de ${item.artists_sort}`
@ -262,24 +233,17 @@ class Albums extends Pages {
if (!user || !user.isPublicCollection) { if (!user || !user.isPublicCollection) {
throw new ErrorEvent( throw new ErrorEvent(
401, 401,
"Collection non partagée",
"Cet utilisateur ne souhaite pas partager sa collection" "Cet utilisateur ne souhaite pas partager sa collection"
); );
} }
const artists = await Albums.getAllDistincts("artists_sort", userId); const artists = await Albums.getAllDistincts("artists_sort", userId);
const formats = await Albums.getAllDistincts("formats.name", userId); const formats = await Albums.getAllDistincts("formats.name", userId);
const years = await Albums.getAllDistincts("year", userId);
const genres = await Albums.getAllDistincts("genres", userId);
const styles = await Albums.getAllDistincts("styles", userId);
this.setPageContent("username", user.username); this.setPageContent("username", user.username);
this.setPageTitle(`Collection publique de ${user.username}`); this.setPageTitle(`Collection publique de ${user.username}`);
this.setPageContent("artists", artists); this.setPageContent("artists", artists);
this.setPageContent("formats", formats); this.setPageContent("formats", formats);
this.setPageContent("years", years);
this.setPageContent("genres", genres);
this.setPageContent("styles", styles);
} }
} }

View file

@ -1,5 +1,4 @@
import { utcToZonedTime } from "date-fns-tz"; import momenttz from "moment-timezone";
import setHours from "date-fns/setHours";
import xl from "excel4node"; import xl from "excel4node";
class Export { class Export {
@ -133,9 +132,7 @@ class Export {
} }
if (released) { if (released) {
ws.cell(currentRow, 7) ws.cell(currentRow, 7)
.date( .date(momenttz.tz(released, "Europe/Paris").hour(12))
setHours(utcToZonedTime(released, "Europe/Paris"), 12)
)
.style({ numberFormat: "dd/mm/yyyy" }); .style({ numberFormat: "dd/mm/yyyy" });
} }
ws.cell(currentRow, 8).string(format).style(style); ws.cell(currentRow, 8).string(format).style(style);

View file

@ -1,12 +1,15 @@
import Joi from "joi"; import Joi from "joi";
import UsersModel from "../models/users"; import UsersModel from "../models/users";
import Pages from "./Pages";
/** /**
* Classe permettant la gestion de l'utilisateur connecté * Classe permettant la gestion de l'utilisateur connecté
*/ */
class Me extends Pages { class Me {
constructor(req) {
this.req = req;
}
/** /**
* Méthode permettant de modifier le profil d'un utilisateur * Méthode permettant de modifier le profil d'un utilisateur
* @return {Object} * @return {Object}
@ -37,33 +40,6 @@ class Me extends Pages {
return update; return update;
} }
/**
* Méthode permettant de modifier le mot de passe d'un utilisateur
*/
async updatePassword() {
const { body } = this.req;
const { _id } = this.req.user;
const schema = Joi.object({
oldPassword: Joi.string().required(),
password: Joi.string().required(),
passwordConfirm: Joi.ref("password"),
});
const value = await schema.validateAsync(body);
const user = await UsersModel.findById(_id);
if (!user.validPassword(value.oldPassword)) {
throw new Error("Votre ancien mot de passe n'est pas valide");
}
user.salt = value.password;
await user.save();
this.req.flash("success", "Profil correctement mis à jour");
}
} }
export default Me; export default Me;

View file

@ -52,20 +52,21 @@ class Pages {
*/ */
render() { render() {
this.pageContent.session = this.req.session; this.pageContent.session = this.req.session;
this.pageContent.flash = { this.pageContent.flashInfo = this.req.flash("info");
info: this.req.flash("info"), this.pageContent.error = this.req.flash("error") || null;
error: [
...this.req.flash("error"),
...(this.req.session?.flash?.error || []),
],
success: this.req.flash("success"),
};
this.pageContent.query = this.req.query; this.pageContent.query = this.req.query;
this.pageContent.params = this.req.params; this.pageContent.params = this.req.params;
this.pageContent.user = this.user; this.pageContent.user = this.user;
this.pageContent.config = config; this.pageContent.config = config;
this.pageContent.getBaseUrl = getBaseUrl(this.req); this.pageContent.getBaseUrl = getBaseUrl(this.req);
if (this.req.session.flash && this.req.session.flash.error) {
// eslint-disable-next-line prefer-destructuring
this.pageContent.page.failureFlash =
this.req.session.flash.error[0];
this.req.session.flash = null;
}
return this.pageContent; return this.pageContent;
} }
} }

View file

@ -7,8 +7,6 @@ import Auth from "../middleware/Auth";
import render from "../libs/format"; import render from "../libs/format";
import { registrationOpen } from "../config";
// eslint-disable-next-line new-cap // eslint-disable-next-line new-cap
const router = express.Router(); const router = express.Router();
@ -61,33 +59,11 @@ router
} }
); );
if (registrationOpen) { router
router .route("/inscription")
.route("/inscription") .get((req, res, next) => {
.get((req, res, next) => {
try {
const page = new Pages(req, "inscription/index");
page.setPageTitle("Inscription");
render(res, page);
} catch (err) {
next(err);
}
})
.post(async (req, res) => {
try {
await Auth.register(req);
res.redirect("/");
} catch (err) {
res.redirect("/inscription");
}
});
} else {
router.route("/inscription").get((req, res, next) => {
try { try {
const page = new Pages(req, "inscription/desactivee"); const page = new Pages(req, "inscription");
page.setPageTitle("Inscription"); page.setPageTitle("Inscription");
@ -95,8 +71,16 @@ if (registrationOpen) {
} catch (err) { } catch (err) {
next(err); next(err);
} }
})
.post(async (req, res) => {
try {
await Auth.register(req);
res.redirect("/");
} catch (err) {
res.redirect("/inscription");
}
}); });
}
router router
.route("/ajouter-un-album") .route("/ajouter-un-album")

View file

@ -1,35 +0,0 @@
import express from "express";
import { ensureLoggedIn } from "connect-ensure-login";
import Me from "../middleware/Me";
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 Me(req, "mon-compte/index");
page.setPageTitle("Mon compte");
render(res, page);
} catch (err) {
next(err);
}
})
.post(ensureLoggedIn("/connexion"), async (req, res) => {
try {
const page = new Me(req, "mon-compte/index");
await page.updatePassword();
} catch (err) {
req.flash("error", err.toString());
}
res.redirect("/mon-compte");
});
export default router;

View file

@ -1,8 +1,10 @@
<main class="layout-maxed error"> <main class="layout-maxed error">
<h1><%= page.title %></h1> <h1><%= page.title %></h1>
<% if ( errorCode && errorCode === 404 ) { %>
<p class="text-center"> <p class="text-center">
<img src="/img/404.svg" alt="Erreur 404" style="max-height: 400px;" /> <img src="/img/404.svg" alt="Erreur 404" style="max-height: 400px;" />
</p> </p>
<% } %>
<% if ( process.env.NODE_ENV !== 'production' ) { %> <% if ( process.env.NODE_ENV !== 'production' ) { %>
<div> <div>
<pre><%= page.error %></pre> <pre><%= page.error %></pre>

View file

@ -83,10 +83,6 @@
</a> </a>
<div class="navbar-dropdown"> <div class="navbar-dropdown">
<a class="navbar-item" href="/mon-compte">
Mon compte
</a>
<hr />
<a class="navbar-item" href="/ma-collection"> <a class="navbar-item" href="/ma-collection">
Ma collection Ma collection
</a> </a>
@ -129,61 +125,40 @@
<span></span> <span></span>
</div> </div>
<% <% if ( page.failureFlash || (error && error.length > 0 ) ) {%>
if ( flash.error.length > 0 ) { <div class="flash">
for ( let i = 0 ; i < flash.error.length ; i += 1 ) { <% if ( page.failureFlash ) {%>
%> <div class="header">
<div class="flash"> Erreur
<div class="header">
Erreur
</div>
<div class="body">
<%= flash.error[i].replace('Error: ', '') %>
</div>
</div> </div>
<% <div class="body">
} <%= page.failureFlash %>
}
if ( flash.info.length > 0 ) {
for ( let i = 0 ; i < flash.info.length ; i += 1 ) {
%>
<div class="flash info">
<div class="header">
Information
</div>
<div class="body">
<%= flash.info[i] %>
</div>
</div> </div>
<% <% } %>
} <%
} if (error && error.length > 0) {
if ( flash.success.length > 0 ) { for( let i = 0 ; i < error.length ; i += 1 ) {
for ( let i = 0 ; i < flash.success.length ; i += 1 ) { %>
%> <div class="header">
<div class="flash success"> Erreur
<div class="header"> </div>
Succès <div class="body">
</div> <%= error %>
<div class="body"> </div>
<%= flash.success[i] %> <%
</div> }
</div> }
<% %>
} </div>
} <% } %>
%>
<%- include(viewname) %> <%- include(viewname) %>
<footer class="footer layout-hero"> <footer class="footer layout-hero">
<p> <p>
<strong title="Merci Brunus ! 😜">MusicTopus</strong> par <a href="https://www.darkou.fr" target="_blank" rel="noopener noreferrer">Damien Broqua <i class="icon-link"></i></a>. <strong title="Merci Brunus ! 😜">MusicTopus</strong> par <a href="https://www.darkou.fr" target="_blank" rel="noopener noreferrer">Damien Broqua <i class="icon-link"></i></a>.
Logo réalisé par Brunus avec <a href="https://inkscape.org/fr/" target="_blank" rel="noopener noreferrer">Inkscape <i class="icon-link"></i></a>.
<br />
Le code source est sous licence <a href="https://www.gnu.org/licenses/gpl-3.0-standalone.html" target="_blank" rel="noopener noreferrer">GNU GPL-3.0-or-later <i class="icon-link"></i></a> et disponible sur <a href="https://git.darkou.fr/dbroqua/MusicTopus" target="_blank">git.darkou.fr <i class="icon-link"></i></a>.
<br />
Fait avec ❤️ à Bordeaux. Fait avec ❤️ à Bordeaux.
Le code source est sous licence <a href="https://www.gnu.org/licenses/gpl-3.0-standalone.html" target="_blank" rel="noopener noreferrer">GNU GPL-3.0-or-later <i class="icon-link"></i></a>.
</p> </p>
</footer> </footer>
</body> </body>

View file

@ -239,9 +239,9 @@
window.location.href = '/ma-collection'; window.location.href = '/ma-collection';
}) })
.catch((err) => { .catch((err) => {
showToastr(err.response?.data?.message || "Impossible d'ajouter cet album pour le moment…"); showToastr(err.response?.data?.message || "Impossible d'ajouter ce album pour le moment…");
}); });
}, },
} }
}).mount('#app'); }).mount('#app');
</script> </script>

View file

@ -40,50 +40,6 @@
</select> </select>
</div> </div>
</div> </div>
<div class="filters" v-if="moreFilters">
<div class="field">
<label for="format">Année</label>
<select id="format" v-model="year" @change="changeFilter">
<option value="">Toutes</option>
<%
for (let i = 0; i < page.years.length; i += 1 ) {
__append(`<option value="${page.years[i]}">${page.years[i]}</option>`);
}
%>
</select>
</div>
<div class="field">
<label for="genre">Genre</label>
<select id="genre" v-model="genre" @change="changeFilter">
<option value="">Tous</option>
<%
for (let i = 0; i < page.genres.length; i += 1 ) {
__append(`<option value="${page.genres[i]}">${page.genres[i]}</option>`);
}
%>
</select>
</div>
<div class="field">
<label for="style">Style</label>
<select id="style" v-model="style" @change="changeFilter">
<option value="">Tous</option>
<%
for (let i = 0; i < page.styles.length; i += 1 ) {
__append(`<option value="${page.styles[i]}">${page.styles[i]}</option>`);
}
%>
</select>
</div>
</div>
<span @click="showMoreFilters" class="showMoreFilters">
<template v-if="!moreFilters">Voir plus de filtres</template>
<template v-if="moreFilters">Voir moins de filtres</template>
<i class="icon-left-open down" v-if="!moreFilters"></i>
<i class="icon-left-open up" v-if="moreFilters"></i>
</span>
<div class="grid grid-cols-1 md:grid-cols-2 list"> <div class="grid grid-cols-1 md:grid-cols-2 list">
<div class="item" v-if="!loading" v-for="item in items"> <div class="item" v-if="!loading" v-for="item in items">
<span class="title"> <span class="title">
@ -149,7 +105,6 @@
data() { data() {
return { return {
loading: false, loading: false,
moreFilters: false,
items: [], items: [],
total: 0, total: 0,
page: 1, page: 1,
@ -157,9 +112,6 @@
limit: 16, limit: 16,
artist: '', artist: '',
format: '', format: '',
year: '',
genre: '',
style: '',
sortOrder: 'artists_sort-asc', sortOrder: 'artists_sort-asc',
sort: 'artists_sort', sort: 'artists_sort',
order: 'asc', order: 'asc',
@ -180,15 +132,6 @@
if ( this.format ) { if ( this.format ) {
url += `&format=${this.format}`; url += `&format=${this.format}`;
} }
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) axios.get(url)
.then( response => { .then( response => {
@ -236,9 +179,6 @@
this.fetch(); this.fetch();
}, },
showMoreFilters() {
this.moreFilters = !this.moreFilters;
}
} }
}).mount('#app'); }).mount('#app');
</script> </script>

View file

@ -274,22 +274,6 @@
Ceci est une erreur Ceci est une erreur
</div> </div>
</div> </div>
<div class="flash info">
<div class="header">
Information
</div>
<div class="body">
Ceci est une information
</div>
</div>
<div class="flash success">
<div class="header">
Succès
</div>
<div class="body">
Ceci est un succès
</div>
</div>
<pre> <pre>
&lt;div class="flash"&gt; &lt;div class="flash"&gt;
&lt;div class="header"&gt; &lt;div class="header"&gt;

View file

@ -12,11 +12,9 @@
<input type="password" name="password" id="password" placeholder="********"> <input type="password" name="password" id="password" placeholder="********">
</div> </div>
<% if ( config.registrationOpen === true ) { %>
<div class="text-right mt-10"> <div class="text-right mt-10">
<p>Pas encore inscrit ? <a href="/inscription">Inscrivez-vous</a></p> <p>Pas encore inscrit ? <a href="/inscription">Inscrivez-vous</a></p>
</div> </div>
<% } %>
<button type="submit" class="button is-primary">Connexion</button> <button type="submit" class="button is-primary">Connexion</button>
</form> </form>

View file

@ -1,19 +0,0 @@
<main class="layout-maxed">
<div class="header layout-hero"></div>
<h1>
Inscription
</h1>
<div class="container">
<div class="text">
<p class="text-justify">
Les inscriptions sur ce site sont fermées.
</p>
<p class="text-justify">
Vous avez cependant la possibilité d'héberger vous même une version de <a href="https://www.musictopus.fr" target="_blank">MusicTopus</a> en vous rendant directement sur le <a href="https://git.darkou.fr/dbroqua/MusicTopus" target="_blank">dépot du projet</a>.
</p>
</div>
<p class="text-center">
<img src="/img/404.svg" alt="Erreur 404" style="max-height: 400px;" />
</p>
</div>
</main>

View file

@ -1,99 +0,0 @@
<main class="layout-maxed collection" id="app">
<h1>
Mon compte
</h1>
<div class="grid grid-cols-1 md:grid-cols-2 gap-10">
<form method="POST" action="/mon-compte" @submit="updateProfil">
<div class="field">
<label for="email">Adresse e-mail</label>
<input
type="email"
readonly
disabled
name="email"
id="email"
v-model="email"
/>
</div>
<div class="field">
<label for="username">Nom d'utilisateur</label>
<input
type="string"
readonly
disabled
name="username"
id="username"
v-model="username"
/>
</div>
<div class="field">
<label for="oldPassword">Mot de passe actuel</label>
<input
type="password"
name="oldPassword"
id="oldPassword"
required
placeholder="Saisisssez votre mot de passe actuel"
v-model="oldPassword"
/>
</div>
<div></div>
<div class="field">
<label for="password">Nouveau mot de passe</label>
<input
type="password"
name="password"
id="password"
required
placeholder="Saisisssez votre nouveau mot de passe"
v-model="password"
/>
</div>
<div class="field">
<label for="passwordConfirm">Nouveau mot de passe (confirmation)</label>
<input
type="password"
name="passwordConfirm"
id="passwordConfirm"
required
placeholder="Confirmez votre nouveau mot de passe"
v-model="passwordConfirm"
/>
</div>
<button type="submit" class="button is-primary mt-10" :disabled="loading">
<span v-if="!loading">Mettre à jour</span>
<i class="icon-spin animate-spin" v-if="loading"></i>
</button>
</form>
</div>
</main>
<script>
Vue.createApp({
data() {
return {
email: '<%= user.email %>',
username: '<%= user.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('#app');
</script>

View file

@ -6,7 +6,6 @@
<a :href="shareLink" v-if="isPublicCollection" target="_blank"> <a :href="shareLink" v-if="isPublicCollection" target="_blank">
<i class="icon-share"></i> Voir ma collection partagée <i class="icon-share"></i> Voir ma collection partagée
</a> </a>
<div class="filters"> <div class="filters">
<div class="field"> <div class="field">
<label for="artist">Artiste</label> <label for="artist">Artiste</label>
@ -44,50 +43,6 @@
</select> </select>
</div> </div>
</div> </div>
<div class="filters" v-if="moreFilters">
<div class="field">
<label for="format">Année</label>
<select id="format" v-model="year" @change="changeFilter">
<option value="">Toutes</option>
<%
for (let i = 0; i < page.years.length; i += 1 ) {
__append(`<option value="${page.years[i]}">${page.years[i]}</option>`);
}
%>
</select>
</div>
<div class="field">
<label for="genre">Genre</label>
<select id="genre" v-model="genre" @change="changeFilter">
<option value="">Tous</option>
<%
for (let i = 0; i < page.genres.length; i += 1 ) {
__append(`<option value="${page.genres[i]}">${page.genres[i]}</option>`);
}
%>
</select>
</div>
<div class="field">
<label for="style">Style</label>
<select id="style" v-model="style" @change="changeFilter">
<option value="">Tous</option>
<%
for (let i = 0; i < page.styles.length; i += 1 ) {
__append(`<option value="${page.styles[i]}">${page.styles[i]}</option>`);
}
%>
</select>
</div>
</div>
<span @click="showMoreFilters" class="showMoreFilters">
<template v-if="!moreFilters">Voir plus de filtres</template>
<template v-if="moreFilters">Voir moins de filtres</template>
<i class="icon-left-open down" v-if="!moreFilters"></i>
<i class="icon-left-open up" v-if="moreFilters"></i>
</span>
<div class="grid grid-cols-1 md:grid-cols-2 list hover"> <div class="grid grid-cols-1 md:grid-cols-2 list hover">
<div class="item" v-if="!loading" v-for="item in items"> <div class="item" v-if="!loading" v-for="item in items">
<span class="title"> <span class="title">
@ -199,7 +154,6 @@
data() { data() {
return { return {
loading: false, loading: false,
moreFilters: false,
items: [], items: [],
total: 0, total: 0,
page: 1, page: 1,
@ -207,9 +161,6 @@
limit: 16, limit: 16,
artist: '', artist: '',
format: '', format: '',
year: '',
genre: '',
style: '',
sortOrder: 'artists_sort-asc', sortOrder: 'artists_sort-asc',
sort: 'artists_sort', sort: 'artists_sort',
order: 'asc', order: 'asc',
@ -234,15 +185,6 @@
if ( this.format ) { if ( this.format ) {
url += `&format=${this.format}`; url += `&format=${this.format}`;
} }
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) axios.get(url)
.then( response => { .then( response => {
@ -290,9 +232,6 @@
this.fetch(); this.fetch();
}, },
showMoreFilters() {
this.moreFilters = !this.moreFilters;
},
toggleModal() { toggleModal() {
this.showModalDelete = !this.showModalDelete; this.showModalDelete = !this.showModalDelete;
}, },
@ -334,7 +273,7 @@
.finally(() => { .finally(() => {
this.toggleModalShare(); this.toggleModalShare();
}); });
}, }
} }
}).mount('#app'); }).mount('#app');
</script> </script>