Compare commits

...
This repository has been archived on 2026-05-01. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.

4 commits

Author SHA1 Message Date
Yûki VACHOT
1419e97b2f Update: Ajout de NodeJS 2021-12-26 17:11:40 +01:00
Yûki VACHOT
92edacb59d Update: Lancement en Local et Production en ligne sur Heroku 2021-12-26 17:03:49 +01:00
Yûki VACHOT
d1b3829450 Update: README 2021-12-25 00:59:53 +01:00
Yûki VACHOT
f91febf919 Update: Add parameters to not update the timestamps of the document 2021-12-22 10:59:10 +01:00
263 changed files with 28 additions and 15503 deletions

View file

@ -1,17 +0,0 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For the full list of supported browsers by the Angular framework, please see:
# https://angular.io/guide/browser-support
# You can see what browsers were selected by your queries by running:
# npx browserslist
last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.

View file

@ -1,16 +0,0 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false

52
.gitignore vendored
View file

@ -1,52 +0,0 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events*.json
speed-measure-plugin*.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
*.env
# System Files
.DS_Store
Thumbs.db
/backend/database/
/backend/node_modules/
package-lock.json

View file

@ -1,6 +0,0 @@
FROM node:current-slim
WORKDIR /app-frontend
COPY ["package.json", "package-lock.json*", "./"]
RUN npm install --NODE_ENV
RUN npm install -g @angular/cli
COPY . .

View file

@ -1 +0,0 @@
web: npm start

View file

@ -1,27 +1,40 @@
# Frontend
# PolyNotFound
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 11.2.7.
Le projet est séparé en 3 parties :
## Development server
- 2 Frontend :
- 1 partie Administrateur
- 1 partie pour les utilisateurs et les publicitaires
- 1 Backend
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
Nous avons décidé que chaque partie du projet a sa propre branche git et non leur propre répertoire git.
## Code scaffolding
à
Run `ng generate component component-title` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
En effet, les branches concernées par le projet est :
- Frontend partie Administrateur : `front-admin`
- Frontend partie pour les utilisateurs et les publicitaires : `front-user-advertiser`
- Backend : `backend`
## Build
Nous pouvons récupérer une branche git avec `git checkout <nom_branche>`.
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
# Lancer le projet en Local
## Running unit tests
Dans un premier temps, il est obligatoire d'avoir NodeJS (version >12 au minimum), [téléchargeable sur cette page](https://nodejs.org/en/download/).
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
Pour lancer le projet en local dans son ensemble, il faut impérativement avoir les branches concernées dans leur propre dossier.
Il faudra donc __clone__ le projet 3 fois.
## Running end-to-end tests
Dans un dossier nommé par exemple `Polynotfound`:
- frontend-admin : `git clone --branch frontend-admin https://github.com/NyxiumYuuki/PolyNotFound.git polynotfound-frontend-admin`
- front-user-advertiser : `git clone --branch front-user-advertiser https://github.com/NyxiumYuuki/PolyNotFound.git polynotfound-front-user-advertiser`
- backend : `git clone --branch backend https://github.com/NyxiumYuuki/PolyNotFound.git polynotfound-backend`
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
Un README est disponible pour chaque branche pour lancer le projet en local soit en mode **production** soit en mode **développement**.
## Further help
# Lancer le projet en ligne avec Heroku
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
Nous avons déployé le projet en ligne avec Heroku.
Le projet est disponible sur ces URL :
- Partie Utilisateurs et Publicitaires : https://polynotfound.herokuapp.com/
- Partie Administrateur : https://admin-polynotfound.herokuapp.com/
- API : https://api-polynotfound.herokuapp.com/

View file

@ -1,134 +0,0 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"frontend": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/frontend",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": true,
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.scss",
"node_modules/bootstrap/scss/bootstrap.scss"
],
"scripts": [
"node_modules/jquery/dist/jquery.js",
"node_modules/bootstrap/dist/js/bootstrap.js"
]
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "frontend:build"
},
"configurations": {
"production": {
"browserTarget": "frontend:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "frontend:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.scss"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "frontend:serve"
},
"configurations": {
"production": {
"devServerTarget": "frontend:serve:production"
}
}
}
}
}
},
"defaultProject": "frontend"
}

View file

@ -1,26 +0,0 @@
const request = require("request");
const VideoCategories = require("../models/objects/video.categories.model");
function asyncRequest(uri, option){
return new Promise(function(resolve){
request(uri, option,function (error, response, body){
resolve({response: response, body: JSON.parse(body)});
});
});
}
module.exports.asyncRequest = asyncRequest;
function asyncInterest(interest, source){
return new Promise(function(resolve){
for(const i in VideoCategories){
for(const j in VideoCategories[i].categories){
if((VideoCategories[i].categories[j].name === interest || VideoCategories[i].categories[j].id === interest)
&& VideoCategories[i].categories[j].source === source){
resolve(VideoCategories[i].interest);
}
}
}
resolve(null);
});
}
module.exports.asyncInterest = asyncInterest;

View file

@ -1,26 +0,0 @@
if(process.env.YOUTUBE_API_KEY === undefined ||
process.env.YOUTUBE_API_KEY === '' ||
process.env.DAILYMOTION_API_KEY === undefined ||
process.env.DAILYMOTION_API_KEY === ''){
console.log('Error Env YOUTUBE_API_KEY & DAILYMOTION_API_KEY Variables');
process.exit();
}
console.log('Env variables YOUTUBE_API_KEY & DAILYMOTION_API_KEY received');
module.exports = {
youtube: {
name: "Youtube",
shortname: "yt",
baseAPIUrl: 'https://youtube.googleapis.com/youtube/v3',
baseChannelUrl: 'https://www.youtube.com/channel/',
YOUTUBE_API_KEY: process.env.YOUTUBE_API_KEY
},
dailymotion: {
name: "Dailymotion",
shortname: "dm",
baseAPIUrl: 'https://api.dailymotion.com',
baseChannelUrl: 'https://www.dailymotion.com/',
DAILYMOTION_API_KEY: process.env.DAILYMOTION_API_KEY
}
};

View file

@ -1,4 +0,0 @@
module.exports = {
prodUrl: process.env.DATABASE,
devUrl: "mongodb://127.0.0.1:27017/polynotfound"
};

View file

@ -1,9 +0,0 @@
function sendMessage (res, successCode, data, token=null) {
res.status(200).json({ status: 'success', successCode: successCode, token: token, data: data });
}
function sendError (res, statusCode, errorCode, reason, token=null) {
res.status(statusCode).json({ status: 'error', errorCode: errorCode, token: token, reason: reason});
}
module.exports = { sendMessage, sendError };

View file

@ -1,109 +0,0 @@
const sessionJWTConfig = require ('jsonwebtoken');
require('dotenv').config({ path: './app-backend/.env' });
const {sendError} = require ("./response.config");
if(process.env.JWTRS256_PRIVATE_KEY === undefined || process.env.JWTRS256_PUBLIC_KEY === undefined){
console.log('Error Env JWTRS256_PRIVATE_KEY & JWTRS256_PUBLIC_KEY Variables');
process.exit();
}
console.log('Env variables JWTRS256_PRIVATE_KEY & JWTRS256_PUBLIC_KEY received');
const JWTRS256_PRIVATE_KEY = Buffer.from(process.env.JWTRS256_PRIVATE_KEY, 'base64').toString('utf-8');
const JWTRS256_PUBLIC_KEY = Buffer.from(process.env.JWTRS256_PUBLIC_KEY, 'base64').toString('utf-8');
function createSessionJWT (id, email, profileImageUrl, role) {
return sessionJWTConfig.sign(
{
id: id,
email: email,
profileImageUrl: profileImageUrl,
role: role,
midExp: Math.floor(Date.now() / 1000) + 1800
},
JWTRS256_PRIVATE_KEY,
{
algorithm: 'RS256',
expiresIn: '1h'
}
);
}
function createSessionCookie(req, res, payload) {
let jwtToken;
if (typeof payload.id !== 'undefined' &&
typeof payload.email !== 'undefined' &&
typeof payload.profileImageUrl !== 'undefined' &&
typeof payload.role !== 'undefined' &&
typeof payload.midExp !== 'undefined' &&
(Math.floor(Date.now() / 1000) <= payload.midExp)) {
jwtToken = req.headers.cookie;
}
else {
jwtToken = createSessionJWT(payload.id, payload.email, payload.profileImageUrl, payload.role);
}
res.cookie('SESSIONID', jwtToken, {httpOnly:true, secure:false});
}
function decodeSessionCookie(sessionid) {
if (typeof sessionid === 'undefined') {
return {id: -1, email: -1, profileImageUrl: -1, role: -1};
}
try {
const token = sessionJWTConfig.verify(
sessionid,
JWTRS256_PUBLIC_KEY,
{algorithms: ['RS256']});
return {token: token};
}
catch (err) {
return {id: -1, email: -1, profileImageUrl: -1, role: -1};
}
}
function getSession(sessionid) {
return decodeSessionCookie(sessionid);
}
module.exports.getSession = getSession
function setSessionCookie (req, res, session) {
createSessionCookie(req, res, session);
}
module.exports.setSessionCookie = setSessionCookie;
function getToken(session) {
if (typeof session === 'undefined' || typeof session.token === 'undefined') return -1;
return session.token;
}
module.exports.getToken = getToken;
function checkLogin(req, res, role=null){
if(typeof req.cookies !== 'undefined'){
const session = getSession(req.cookies.SESSIONID);
const token = getToken(session);
if(typeof token.email === 'undefined' ||
token.email === -1 ||
typeof token.id === 'undefined' ||
token.id === -1){
return sendError(res, 500, 102, "User not authenticated.");
} else {
token.midExp = new Date(token.midExp*1000);
token.iat = new Date(token.iat*1000);
token.exp = new Date(token.exp*1000);
if(role === null){
return token;
} else {
if(typeof token.role !== 'undefined' &&
((Array.isArray(role) && role.includes(token.role)) ||
( typeof role === 'object' && typeof token.role.permission !== 'undefined' && token.role.permission >= role.permission && token.role.isAccepted === true))){
return token;
} else {
return sendError(res, 500, 106, "User doesn't have permission.", token);
}
}
}
} else {
return sendError(res, 500, -1, "Cookies don't exist.");
}
}
module.exports.checkLogin = checkLogin;

View file

@ -1,290 +0,0 @@
const db = require("../models/mongodb.model");
const {sendError, sendMessage} = require ("../config/response.config");
const {checkLogin} = require("../config/sessionJWT.config");
const ObjectId = require('mongoose').Types.ObjectId;
const roles = require("../models/objects/role.model");
const Ad = db.ads;
// Create a new Ad
exports.create = (req, res) => {
const token = checkLogin(req, res, roles.Advertiser);
if(token && req.body.title){
Ad.exists({title: req.body.title, userId: token.id, isActive: true}, function (err, docs){
if(err){
sendError(res, 500,100,err.message || "Some error occurred while checking if the Ad already exists.", token);
} else{
if(docs === null) {
let ad;
ad = new Ad({
userId: token.id,
title: req.body.title,
images: req.body.images ? req.body.images : undefined,
url: req.body.url ? req.body.url : undefined,
interests: req.body.interests ? req.body.interests : undefined,
comment: req.body.comment ? req.body.comment : undefined,
isVisible: req.body.isVisible ? req.body.isVisible : undefined,
isActive: req.body.isActive ? req.body.isActive : undefined
});
// Save User in the database
ad
.save(ad)
.then(data => {
return sendMessage(res, 41, data, token)
})
.catch(err => {
return sendError(res, 500,100,err.message || "Some error occurred while creating the Ad.", token);
});
} else{
return sendError(res, 500, 104, err || `Ad ${req.body.title} already exists.`, token);
}
}
});
} else {
return sendError(res, 500, -1, `No title given`, token);
}
};
// Retrieve all Ad from id if admin or session id
exports.findAll = (req, res) => {
const token = checkLogin(req, res, roles.Advertiser);
if(token){
let query = {};
let condition;
const adId = req.query.adId;
condition = adId ? adId : undefined;
query._id = condition;
let userId;
if(typeof token.role !== 'undefined' &&
typeof token.role.permission !== 'undefined' &&
typeof token.role.isAccepted !== 'undefined' &&
token.role.isAccepted === true &&
token.role.permission >= roles.Admin.permission) {
userId = req.query.userId;
} else {
userId = token.id;
}
condition = userId ? userId : undefined;
query.userId = condition;
const title = req.query.title;
condition = title ? { $regex: new RegExp(title), $options: "i" } : undefined;
query.title = condition;
const url = req.query.url;
condition = url ? { $regex: new RegExp(url), $options: "i" } : undefined;
query.url = condition;
const interests = req.query.interests;
condition = interests ? {$in: interests.split(',')} : undefined;
query["interests.interest"] = condition
const comment = req.query.comment;
condition = comment ? { $regex: new RegExp(comment), $options: "i" } : undefined;
query.comment = condition;
const isVisible = req.query.isVisible;
condition = isVisible ? isVisible : undefined;
query.isVisible = condition;
const isActive = req.query.isActive;
condition = isActive ? isActive : undefined;
query.isActive = condition;
const sort = req.query.sort;
if(sort !== 'undefined'){
switch (sort){
case 'asc':
condition = {title: 1};
break;
case 'desc':
condition = {title: -1};
break;
case 'createdAtAsc':
condition = {createdAt: 1};
break;
case 'createdAtDesc':
condition = {createdAt: -1};
break;
case 'updatedAtAsc':
condition = {updatedAt: 1};
break;
case 'updatedAtDesc':
condition = {updatedAt: -1};
break;
default:
condition = {title: 1};
}
}
const query_sort = {sort: condition};
// Remove undefined key
Object.keys(query).forEach(key => query[key] === undefined ? delete query[key] : {});
console.log(query);
Ad.find(query, {}, query_sort)
.then(data => {
if(data){
return sendMessage(res, 42, data, token);
}
})
.catch(err => {
return sendError(res,500,100,err.message || "Some error occurred while finding the Ads.", token);
});
}
};
// Find single Ad from id if admin or session id
exports.findOne = (req, res) => {
const token = checkLogin(req, res, roles.Advertiser);
if(token && typeof req.params.id !== 'undefined') {
const id = req.params.id;
if(id && ObjectId.isValid(id)){
Ad.findById(id, {})
.then(data => {
if(data){
return sendMessage(res, 43, data, token);
} else {
return sendError(res,404,105,`Ad not found with id=${id}`, token);
}
})
.catch(err => {
return sendError(res,500,100,err.message || `Some error occurred while finding the Ad with id=${id}`, token);
});
} else {
return sendError(res, 500, -1, `Error id is not valid`, token);
}
} else {
return sendError(res, 500, -1, `No id given`, token);
}
};
// Update a Ad with ad id
exports.update = (req, res) => {
const token = checkLogin(req, res, roles.Advertiser);
if(token && typeof req.params.id !== 'undefined') {
const id = req.params.id;
if(typeof req.body._id !== 'undefined' || typeof req.body.id !== 'undefined'){
return sendError(res, 500, -1, `User do not have the permission to modify id or _id`, token);
} else{
let update = {};
let condition;
const title = req.body.title;
condition = title ? title : undefined;
update.title = condition;
const images = req.body.images;
condition = images ? images : undefined;
update.images = condition;
const url = req.body.url;
condition = url ? url : undefined;
update.url = condition;
let interests = req.body.interests;
condition = interests ? {interests: [...new Map(interests.map(v => [v.id, v])).values()]} : undefined;
update.$addToSet = condition;
const comment = req.body.comment;
condition = comment ? comment : undefined;
update.comment = condition;
const isVisible = req.body.isVisible;
if(typeof isVisible !== 'undefined'){
condition = isVisible;
} else{
condition = undefined;
}
update.isVisible = condition;
const isActive = req.body.isActive;
if(typeof isActive !== 'undefined'){
condition = isActive;
} else{
condition = undefined;
}
update.isActive = condition;
// Remove undefined key
Object.keys(update).forEach(key => update[key] === undefined ? delete update[key] : {});
if(id && ObjectId.isValid(id)){
Ad.updateOne({_id: id, userId: token.id}, update)
.then(data => {
if(data) {
//Object.keys(update).forEach(key => data[key] = update[key]);
return sendMessage(res, 44, update, token);
} else {
return sendError(res, 404, -1, `Ad not found with id=${id}`, token);
}
})
.catch(err => {
return sendError(res, 500, -1, err.message || `Some error occurred while updating the Ad with id=${id}`, token);
});
} else {
return sendError(res, 500, -1, `Error id is not valid`, token);
}
}
} else {
return sendError(res, 500, -1, `No id given`, token);
}
};
// Delete an Ad with ad id
exports.delete = (req, res) => {
const token = checkLogin(req, res, roles.Advertiser);
if(token && typeof req.params.id !== 'undefined') {
let match = null;
const id = req.params.id;
if(id && ObjectId.isValid(id)){
if(typeof token.role !== 'undefined' &&
typeof token.role.permission !== 'undefined' &&
typeof token.role.isAccepted !== 'undefined' &&
token.role.isAccepted === true &&
token.role.permission >= roles.Admin.permission) {
match = {_id: id, isActive: true};
} else {
match = {_id: id, userId: token.id, isActive: true};
}
Ad.findOneAndUpdate(match, {isActive: false}, {useFindAndModify: false, new: true})
.then(data => {
if(data) {
if(data.isActive !== true){
return sendMessage(res, 45, {message: `Ad ${id} was successfully deleted.`}, token);
} else {
return sendError(res, 404, 105, `Ad ${id} was not deleted.`, token);
}
} else {
return sendError(res, 404, 105, `Ad not found with id=${id}`, token);
}
})
.catch(err => {
return sendError(res, 500, 100, err.message || `Some error occurred while deleting the Ad with id=${id}`, token);
});
} else {
return sendError(res, 500, -1, `Error id is not valid`, token);
}
} else {
return sendError(res, 500, -1, `No id given`, token);
}
};
// Delete all Ad from session id
exports.deleteAll = (req, res) => {
const token = checkLogin(req, res, roles.Advertiser);
if(token) {
Ad.updateMany({userId: {$eq: token.id}, isActive: true}, {isActive: false})
.then(data => {
return sendMessage(res, 46, {
message: `${data.modifiedCount} Ads were deleted successfully.`
});
})
.catch(err => {
return sendError(res, 500, -1, err.message || "Some error occurred while removing all Ads.");
});
}
};

View file

@ -1,7 +0,0 @@
const {sendMessage} = require ("../config/response.config");
const interests = require("../models/objects/video.categories.model");
// Get all interests available
exports.getInterests = (req, res) => {
return sendMessage(res, 51, interests, null)
};

View file

@ -1,407 +0,0 @@
const db = require("../models/mongodb.model");
const {sendError, sendMessage} = require ("../config/response.config");
const {checkLogin} = require("../config/sessionJWT.config");
const {youtube, dailymotion} = require("../config/host.config");
const {asyncRequest, asyncInterest} = require("../config/functions.config");
const ObjectId = require('mongoose').Types.ObjectId;
const Playlist = db.playlists;
const Video = db.videos;
// Create a new Playlist
exports.create = (req, res) => {
const token = checkLogin(req, res);
if(token && req.body.name){
const video = req.body.video;
if(typeof video !== 'undefined' &&
video !== null &&
typeof video.videoId !== 'undefined' &&
video.videoId !== null &&
typeof video.source !== 'undefined' &&
video.source !== null &&
typeof video.interest !== 'undefined' &&
video.interest !== null
){
Video.exists({userId: token.id, videoId: video.videoId, source: video.source, isActive: true}, function (err, docs){
if(err){
sendError(res, 500,100,err.message || "Some error occurred while checking if the Video already exists.", token);
} else{
if(docs === null) {
let video;
video = new Video({
userId: token.id,
videoId: id,
source: req.body.source,
interest: req.body.interest,
watchedDates: [new Date()]
});
// Save Video in the database
video
.save(video)
.then(data => {
if(data) {
Playlist.exists({name: req.body.name, isActive: true}, function (err, docs){
if(err){
sendError(res, 500,100,err.message || "Some error occurred while checking if the Playlist already exists.", token);
} else{
if(docs === null) {
let playlist;
playlist = new Playlist({
userId: token.id,
name: req.body.name,
videoIds: data._id ? [data._id] : undefined,
isActive: true
});
// Save User in the database
playlist
.save(playlist)
.then(data => {
return sendMessage(res, 21, data, token)
})
.catch(err => {
return sendError(res, 500,100,err.message || "Some error occurred while creating the Playlist.", token);
});
} else{
return sendError(res, 500, 104, err || `Playlist ${req.body.name} already exists.`, token);
}
}
});
}
})
.catch(err => {
return sendError(res, 500,100,err.message || "Some error occurred while creating the Video.", token);
});
} else{
const id = docs._id.toString();
Playlist.exists({name: req.body.name, isActive: true}, function (err, docs){
if(err){
sendError(res, 500,100,err.message || "Some error occurred while checking if the Playlist already exists.", token);
} else{
if(docs === null) {
let playlist;
playlist = new Playlist({
userId: token.id,
name: req.body.name,
videoIds: [id],
isActive: true
});
// Save User in the database
playlist
.save(playlist)
.then(data => {
return sendMessage(res, 21, data, token)
})
.catch(err => {
return sendError(res, 500,100,err.message || "Some error occurred while creating the Playlist.", token);
});
} else{
return sendError(res, 500, 104, err || `Playlist ${req.body.name} already exists.`, token);
}
}
});
}
}
});
} else {
Playlist.exists({name: req.body.name, isActive: true}, function (err, docs){
if(err){
sendError(res, 500,100,err.message || "Some error occurred while checking if the Playlist already exists.", token);
} else{
if(docs === null) {
let playlist;
playlist = new Playlist({
userId: token.id,
name: req.body.name,
videoIds: req.body.videoIds ? req.body.videoIds : undefined,
isActive: req.body.isActive ? req.body.isActive : undefined
});
// Save User in the database
playlist
.save(playlist)
.then(data => {
return sendMessage(res, 21, data, token)
})
.catch(err => {
return sendError(res, 500,100,err.message || "Some error occurred while creating the Playlist.", token);
});
} else{
return sendError(res, 500, 104, err || `Playlist ${req.body.name} already exists.`, token);
}
}
});
}
}
};
// Retrieve all Playlist from id if admin or session id
exports.findAll = (req, res) => {
const token = checkLogin(req, res);
if(token){
let query = {};
let condition;
const playlistId = req.query.playlistId;
condition = playlistId ? playlistId : undefined;
query._id = condition;
const userId = token.id;
condition = userId ? userId : undefined;
query.userId = condition;
const videoIds = req.query.videoIds;
condition = videoIds ? {$in: videoIds} : undefined;
query.videoIds = condition;
const name = req.query.name;
condition = name ? { $regex: new RegExp(name), $options: "i" } : undefined;
query.name = condition;
const isActive = req.query.isActive;
condition = isActive ? isActive : undefined;
query.isActive = condition;
const sort = req.query.sort;
if(sort !== 'undefined'){
switch (sort){
case 'asc':
condition = {name: 1};
break;
case 'desc':
condition = {name: -1};
break;
case 'createdAtAsc':
condition = {createdAt: 1};
break;
case 'createdAtDesc':
condition = {createdAt: -1};
break;
case 'updatedAtAsc':
condition = {updatedAt: 1};
break;
case 'updatedAtDesc':
condition = {updatedAt: -1};
break;
default:
condition = {name: 1};
}
}
const query_sort = {sort: condition};
// Remove undefined key
Object.keys(query).forEach(key => query[key] === undefined ? delete query[key] : {});
console.log(query);
Playlist.find(query, {}, query_sort)
.then(data => {
return sendMessage(res, 22, data, token);
})
.catch(err => {
return sendError(res,500,100,err.message || "Some error occurred while finding the Playlists.", token);
});
}
};
// Find single Playlist from session id
exports.findOne = (req, res) => {
const token = checkLogin(req, res);
if(token && typeof req.params.id !== 'undefined') {
const id = req.params.id;
if(id && ObjectId.isValid(id)){
Playlist.aggregate([
{$match: {_id: new ObjectId(id), userId: token.id, isActive: true}},
{$unwind: '$videoIds'},
{$project: {
userId: true,
name: true,
isActive: true,
createdAt: true,
updatedAt: true,
videoIds: {$toObjectId: '$videoIds'}
}},
{$lookup: {
from: 'videos',
localField: 'videoIds',
foreignField: '_id',
as: 'videos'
}},
{$unwind: '$videos'},
{$group: {
_id: '$_id',
userId: {$first: "$userId"},
name: {$first: "$name"},
isActive: {$first: "$isActive"},
createdAt: {$first: "$createdAt"},
updatedAt: {$first: "$updatedAt"},
videos: {$push: "$videos"}
}}
])
.then(async data => {
let yt_results = [];
let dm_results = [];
let yt_videoIds = "";
let dm_videoIds = "";
for (const i in data[0].videos) {
if (data[0].videos[i].source === youtube.name) {
yt_videoIds = yt_videoIds + data[0].videos[i].videoId + ",";
} else if (data[0].videos[i].source === dailymotion.name) {
dm_videoIds = dm_videoIds + data[0].videos[i].videoId + ",";
}
}
if (yt_videoIds !== "") {
const uri = youtube.baseAPIUrl + '/videos' + '?part=snippet&part=statistics&id=' + yt_videoIds.slice(0, -1) + '&key=' + youtube.YOUTUBE_API_KEY;
const dataVideos = await asyncRequest(uri, {});
if (dataVideos.response.statusCode === 200 && dataVideos.body.items.length > 0) {
yt_results = dataVideos.body.items;
}
}
if (dm_videoIds !== "") {
const uri = dailymotion.baseAPIUrl + '/videos?ids=' + dm_videoIds.slice(0, -1) + '&fields=thumbnail_480_url%2Ctitle%2Cid';
const data = await asyncRequest(uri, {});
const response = data.response;
const jsonBody = data.body;
if (response.statusCode === 200) {
dm_results = jsonBody.list;
}
}
for (const i in data[0].videos) {
if (data[0].videos[i].source === youtube.name) {
const obj = yt_results.filter(obj => obj.id === data[0].videos[i].videoId);
data[0].videos[i].imageUrl = obj[0].snippet.thumbnails.medium.url ? obj[0].snippet.thumbnails.medium.url : null;
data[0].videos[i].interest = obj[0].snippet.categoryId ? await asyncInterest(obj[0].snippet.categoryId, youtube.name): null;
data[0].videos[i].title = obj[0].snippet.title ? obj[0].snippet.title : null;
data[0].videos[i].views = obj[0].statistics.viewCount ? parseInt(obj[0].statistics.viewCount) : null;
data[0].videos[i].publishedAt = obj[0].snippet.publishedAt ? obj[0].snippet.publishedAt : null;
} else if (data[0].videos[i].source === dailymotion.name) {
const obj = dm_results.filter(obj => obj.id === data[0].videos[i].videoId);
data[0].videos[i].imageUrl = obj[0].thumbnail_480_url ? obj[0].thumbnail_480_url : null;
data[0].videos[i].interest = obj[0]['channel.name'] ? await asyncInterest( obj[0]['channel.name'], dailymotion.name) : null;
data[0].videos[i].title = obj[0].title ? obj[0].title : null;
data[0].videos[i].views = obj[0].views_total ? parseInt(obj[0].views_total) : null;
data[0].videos[i].publishedAt = obj[0].created_time ? new Date(obj[0].created_time * 1000) : null
}
}
return sendMessage(res, 12, data[0], token)
})
.catch(err => {
return sendError(res,500,100,err.message || `Some error occurred while finding the Playlist with id=${id}`, token);
});
} else {
return sendError(res, 500, -1, `Error id is not valid`, token);
}
} else {
return sendError(res, 500, -1, `No id given`, token);
}
};
// Update a Playlist with playlist id
exports.update = (req, res) => {
const token = checkLogin(req, res);
if(token && typeof req.params.id !== 'undefined') {
const id = req.params.id;
if(typeof req.body._id !== 'undefined' || typeof req.body.id !== 'undefined'){
return sendError(res, 500, -1, `User do not have the permission to modify id or _id`, token);
} else{
const ids = id.split(',');
let update = {};
let condition;
const name = req.body.name;
condition = name ? name : undefined;
update.name = condition;
const videoIds = req.body.videoIds;
condition = videoIds ? videoIds : undefined;
update.videoIds = condition;
const videoId = req.body.videoId;
if(typeof videoId !== 'undefined' && typeof videoId.id !== 'undefined' && typeof videoId.action !== 'undefined'){
if(videoId.action === 'add'){
condition = videoId.id ? {videoIds: videoId.id} : undefined;
update.$addToSet = condition;
} else if(videoId.action === 'delete'){
condition = videoId.id ? {videoIds: videoId.id} : undefined;
update.$pull = condition;
}
}
const isActive = req.body.isActive;
if(typeof isActive !== 'undefined'){
condition = isActive;
} else{
condition = undefined;
}
update.isActive = condition;
// Remove undefined key
Object.keys(update).forEach(key => update[key] === undefined ? delete update[key] : {});
Playlist.updateMany({_id: {$in: ids}, userId: token.id, isActive: true}, update, {new: false})
.then(data => {
if(data) {
if(data.modifiedCount > 0){
return sendMessage(res, 24, update, token);
} else {
return sendError(res, 500, -1, `Video in Playlist ${data} already exists.`, token);
}
} else {
return sendError(res, 404, -1, `Playlist not found with id=${id}`, token);
}
})
.catch(err => {
return sendError(res, 500, -1, err.message || `Some error occurred while updating the Playlist with id=${id}`, token);
});
}
} else {
return sendError(res, 500, -1, `No id given`, token);
}
};
// Delete a Playlist with playlist id
exports.delete = (req, res) => {
const token = checkLogin(req, res);
if(token && typeof req.params.id !== 'undefined') {
const id = req.params.id;
if(id && ObjectId.isValid(id)){
Playlist.findByIdAndUpdate(id, {isActive: false}, {useFindAndModify: false})
.then(data => {
if(data) {
return sendMessage(res, 25, {message: `Playlist ${id} was successfully deleted.`}, token);
} else {
return sendError(res, 404, 105, `Playlist not found with id=${id}`, token);
}
})
.catch(err => {
return sendError(res, 500, 100, err.message || `Some error occurred while deleting the Playlist with id=${id}`, token);
});
} else {
return sendError(res, 500, -1, `Error id is not valid`, token);
}
} else {
return sendError(res, 500, -1, `No id given`, token);
}
};
// Delete all Playlists from session id
exports.deleteAll = (req, res) => {
const token = checkLogin(req, res);
if(token) {
Playlist.updateMany({userId: {$eq: token.id}, isActive: true}, {isActive: false})
.then(data => {
return sendMessage(res, 26, {
message: `${data.modifiedCount} Playlists were deleted successfully.`
});
})
.catch(err => {
return sendError(res, 500, -1, err.message || "Some error occurred while removing all Playlists.");
});
}
};

View file

@ -1,546 +0,0 @@
const db = require("../models/mongodb.model");
const {sendError, sendMessage} = require ("../config/response.config");
const {checkLogin, setSessionCookie} = require("../config/sessionJWT.config");
const ObjectId = require('mongoose').Types.ObjectId;
const roles = require("../models/objects/role.model");
const {youtube, dailymotion} = require("../config/host.config");
const {asyncRequest} = require("../config/functions.config");
const User = db.users;
const Video = db.videos;
const Ad = db.ads;
// Authenticate a User
exports.auth = (req, res) => {
// Validate request
if (!req.body.email || !req.body.hashPass) {
sendError(res, 400,-1,"Content can not be empty . (email and hashPass needed)");
} else{
// Check User in the database
User
.findOne({email: req.body.email, hashPass: req.body.hashPass, isActive: true, "role.isAccepted": true}, {role: true, profileImageUrl: true})
.then(data => {
if (data !== null){
User.findByIdAndUpdate(data._id.toString(), {lastConnexion: new Date()}, {useFindAndModify: false},
function (err) {
if (err){
return sendError(res, 400, 100,err.message || "Some error occurred while updating the User.");
}
else{
const dataRes = {id: data._id.toString(), email: req.body.email, profileImageUrl: data.profileImageUrl, role: data.role};
setSessionCookie(req, res, dataRes);
return sendMessage(res, 1, dataRes);
}
});
} else {
setSessionCookie(req, res, {id: -1, email: -1, profileImageUrl: -1, role: -1});
return sendError(res, 500, 101, "Invalid login or password.");
}
})
.catch(err => {
return sendError(res, 400, 100,err.message || "Some error occurred while authenticating the User.");
});
}
};
// Logout a User
exports.logout = (req, res) => {
const token = checkLogin(req, res);
if(token){
setSessionCookie(req, res, {id: -1, email: -1, profileImageUrl: -1, role: -1});
return sendMessage(res, 2, {message: "User disconnected"});
}
};
// Request password reset with email
exports.resetPass = (req, res) => {
return sendError(res, 501, -1, "User.resetPass not Implemented", null);
};
// Create and Save a new User
exports.create = (req, res) => {
// Validate request
if (!req.body.email || !req.body.hashPass || !req.body.login) {
sendError(res, 400,-1,"Content can not be empty . (email, hashPass and login needed");
}
else{
User.exists({email: req.body.email}, function (err, docs){
if(err){
sendError(res, 500,100,err.message || "Some error occurred while checking if the User already exists.");
} else{
if(docs === null) {
let user;
let var_role;
if(typeof req.body.role !== 'undefined'){
switch(req.body.role){
case 'admin':
var_role = roles.Admin;
break;
case 'advertiser':
var_role = roles.Advertiser;
break;
default:
var_role = roles.User;
}
} else{
var_role = roles.User;
}
user = new User({
email: req.body.email,
hashPass: req.body.hashPass,
login: req.body.login,
role: var_role,
company: req.body.company ? req.body.company : null,
dateOfBirth: req.body.dateOfBirth ? req.body.dateOfBirth : null,
gender: req.body.gender ? req.body.gender : null,
interests: req.body.interests ? req.body.interests : null,
});
// Save User in the database
user
.save(user)
.then(data => {
data.hashPass = undefined; // Hiding hashPass on return
return sendMessage(res, 4, data)
})
.catch(err => {
return sendError(res, 500,100,err.message || "Some error occurred while creating the User.");
});
} else{
return sendError(res, 500, 104, err || `Email ${req.body.email} already exists.`);
}
}
});
}
};
// Retrieve all Users from the database if at least admin.
exports.findAll = (req, res) => {
const token = checkLogin(req, res, roles.Admin);
if(token){
let query = {};
let condition;
const ids = req.query.userId;
condition = ids ? {$in: ids} : undefined;
query._id = condition;
const email = req.query.email;
condition = email ? { $regex: new RegExp(email), $options: "i" } : undefined;
query.email = condition;
const login = req.query.login;
condition = login ? { $regex: new RegExp(login), $options: "i" } : undefined;
query.login = condition;
const role = req.query.role;
condition = role ? role : undefined;
query["role.name"] = condition;
const company = req.query.company;
condition = company ? { $regex: new RegExp(company), $options: "i" } : undefined;
query.company = condition;
const dateOfBirth = req.query.dateOfBirth;
condition = dateOfBirth ? new Date(dateOfBirth) : undefined;
query.dateOfBirth = condition;
const gender = req.query.gender;
condition = gender ? gender : undefined;
query.gender = condition;
const isActive = req.query.isActive;
condition = isActive ? isActive : undefined;
query.isActive = condition;
const isAccepted = req.query.isAccepted;
if(isAccepted !== 'undefined'){
switch (isAccepted){
case 'true':
condition = true;
break;
case 'false':
condition = false;
break;
}
}
query["role.isAccepted"] = condition;
const sort = req.query.sort;
if(sort !== 'undefined'){
switch (sort){
case 'asc':
condition = {email: 1};
break;
case 'desc':
condition = {email: -1};
break;
case 'lastConnexionAsc':
condition = {lastConnexion: 1};
break;
case 'lastConnexionDesc':
condition = {lastConnexion: -1};
break;
case 'createdAtAsc':
condition = {createdAt: 1};
break;
case 'createdAtDesc':
condition = {createdAt: -1};
break;
case 'updatedAtAsc':
condition = {updatedAt: 1};
break;
case 'updatedAtDesc':
condition = {updatedAt: -1};
break;
default:
condition = {email: 1};
}
}
const query_sort = {sort: condition};
// Remove undefined key
Object.keys(query).forEach(key => query[key] === undefined ? delete query[key] : {});
console.log(query);
User.find(query, {hashPass: false}, query_sort)
.then(data => {
return sendMessage(res, 5, data, token);
})
.catch(err => {
return sendError(res,500,100,err.message || "Some error occurred while retrieving users.", token);
});
}
};
// Find a single User by session id
exports.findOne = (req, res) => {
const token = checkLogin(req, res);
if(token && typeof req.params.id !== 'undefined') {
let id = null;
if(typeof token.id !== 'undefined' && token.id === req.params.id){
id = req.params.id;
} else {
if(typeof token.role !== 'undefined' &&
typeof token.role.permission !== 'undefined' &&
typeof token.role.isAccepted !== 'undefined' &&
token.role.isAccepted === true &&
token.role.permission >= roles.Admin.permission) {
id = req.params.id;
} else {
return sendError(res, 500, 106, `User do not have the permission.`, token);
}
}
if(id && ObjectId.isValid(id)){
User.findById(id, {hashPass: false})
.then(data => {
if(data){
return sendMessage(res, 6, data, token);
} else {
return sendError(res,404,105,`User not found with id=${id}`, token);
}
})
.catch(err => {
return sendError(res,500,100,err.message || `Some error occurred while finding the User with id=${id}`, token);
});
} else {
sendError(res, 500, -1, `Error id is not valid`, token);
}
} else {
return sendError(res, 500, -1, `No id given`, token);
}
};
// Update a User by the id in the request
exports.update = (req, res) => {
const token = checkLogin(req, res);
if(token && typeof req.params.id !== 'undefined') {
let id = null;
if(typeof token.id !== 'undefined' && token.id === req.params.id){
id = req.params.id;
} else {
if(typeof token.role !== 'undefined' &&
typeof token.role.permission !== 'undefined' &&
typeof token.role.isAccepted !== 'undefined' &&
token.role.isAccepted === true &&
token.role.permission >= roles.Admin.permission) {
id = req.params.id;
} else {
return sendError(res, 500, 106, `User do not have the permission.`, token);
}
}
if(id && ObjectId.isValid(id)){
let update = null;
if(typeof req.body._id !== 'undefined' || typeof req.body.id !== 'undefined'){
return sendError(res, 500, -1, `User do not have the permission to modify id or _id`, token);
} else{
if(typeof req.body.role !== 'undefined' ||
typeof req.body.isActive !== 'undefined' ||
typeof req.body.lastConnexion !== 'undefined' ||
typeof req.body.createdAt !== 'undefined'||
typeof req.body.updatedAt !== 'undefined'){
if(typeof token.role !== 'undefined' &&
typeof token.role.permission !== 'undefined' &&
typeof token.role.isAccepted !== 'undefined' &&
token.role.isAccepted === true &&
token.role.permission >= roles.Admin.permission) {
update = true;
} else{
return sendError(res, 500, 106, `User do not have the permission to modify these keys.`, token);
}
} else{
update = true;
}
}
if(update === true){
User.findByIdAndUpdate(id, req.body, {useFindAndModify: false})
.then(data => {
if(data) {
data.hashPass = undefined;
Object.keys(req.body).forEach(key => data[key] = req.body[key]);
sendMessage(res, 7, data, token);
} else {
sendError(res, 404, -1, `User not found with id=${id}`, token);
}
})
.catch(err => {
sendError(res, 500, -1, err.message || `Some error occurred while updating the User with id=${id}`, token);
});
}
} else {
sendError(res, 500, -1, `Error id is not valid`, token);
}
} else {
return sendError(res, 500, -1, `No id given`, token);
}
};
// Delete a User with the specified id in the request
exports.delete = (req, res) => {
const token = checkLogin(req, res);
if(token && typeof req.params.id !== 'undefined') {
let id = null;
if(typeof token.id !== 'undefined' && token.id === req.params.id){
id = req.params.id;
} else {
if(typeof token.role !== 'undefined' &&
typeof token.role.permission !== 'undefined' &&
typeof token.role.isAccepted !== 'undefined' &&
token.role.isAccepted === true &&
token.role.permission >= roles.Admin.permission) {
id = req.params.id;
} else {
return sendError(res, 500, 106, `User do not have the permission.`, token);
}
}
if(id && ObjectId.isValid(id)){
User.findByIdAndUpdate(id, {isActive: false}, {useFindAndModify: false})
.then(data => {
if(data) {
return sendMessage(res, 8, {message: `User ${id} was successfully deleted.`}, token);
} else {
return sendError(res, 404, 105, `User not found with id=${id}`, token);
}
})
.catch(err => {
return sendError(res, 500, 100, err.message || `Some error occurred while deleting the User with id=${id}`, token);
});
} else {
return sendError(res, 500, -1, `Error id is not valid`, token);
}
} else {
return sendError(res, 500, -1, `No id given`, token);
}
};
// Delete all Users from the database except superAdmin
exports.deleteAll = (req, res) => {
const token = checkLogin(req, res, roles.SuperAdmin);
if(token) {
User.deleteMany({login: {$ne: "superAdmin"}})
.then(data => {
return sendMessage(res, 9, {
message: `${data.deletedCount} Users were deleted successfully.`
});
})
.catch(err => {
return sendError(res, 500, 100, err.message || "Some error occurred while removing all Users.");
});
}
};
// Get all Roles depending on the role of the User
exports.roles = (req, res) => {
const token = checkLogin(req, res);
if(token){
return sendMessage(res, 10, roles, token);
}
};
// Get 1 or multiple ad adapted to the User session id
exports.ad = (req, res) => {
const token = checkLogin(req, res);
if(token && typeof req.query.quantity !== 'undefined'){
const id = token.id;
const quantity = req.query.quantity;
// Interests from the user and from last 20 videos viewed if no ad matches -> find x ad from these interests + add date view to the ad
let interests = [];
const maxInterests = 20;
let limit = maxInterests;
User.findById(id, {_id: false, interests: true})
.then(data => {
if(typeof data.interests !== 'undefined' && data.interests !== null){
interests = interests.concat(data.interests);
limit = maxInterests-data.interests.length;
}
Video.aggregate([
{$match: {userId: id}},
{$project: {_id: false, interest: true}},
{$sort: {watchedDates: -1}},
{$limit: limit},
{$unwind: '$interest'},
{$group: {_id: null, interests: {$push: '$interest'}}}
])
.then(data => {
if(typeof data[0] !== 'undefined' &&
typeof data[0].interests !== 'undefined' &&
data[0].interests !== [] &&
data[0].interests !== null){
interests = interests.concat(data[0].interests);
}
let match, pick;
if(interests.length > 0){
match = {$match: {isVisible: true, isActive: true, interests: {$elemMatch: {interest: {$in: interests}}}}};
pick = {$limit: parseInt(quantity, 10)}
} else {
match = {$match: {isVisible: true, isActive: true}};
pick = {$sample: {size: parseInt(quantity, 10)}};
}
Ad.aggregate([
match,
pick
])
.then(data => {
if(data.length > 0){
let ids = []
for(const i in data){ids.push(data[i]._id);}
Ad.updateMany({_id: {$in: ids}}, {$push: {views: [new Date()]}})
.then(dataUpdate => {
if(dataUpdate && dataUpdate.modifiedCount > 0){
return sendMessage(res, 11, data, token);
} else {
return sendError(res,500,101,`Some error occurred while updating ${quantity} ad(s) for the User.`, token);
}
})
.catch(err => {
return sendError(res,500,101,err.message || `Some error occurred while updating ${quantity} ad(s) for the User.`, token);
});
} else {
Ad.aggregate([{$match: {isVisible: true, isActive: true}}, {$sample: {size: parseInt(quantity, 10)}}])
.then(data => {
let ids = []
for(const i in data){ids.push(data[i]._id);}
Ad.updateMany({_id: {$in: ids}}, {$push: {views: [new Date()]}})
.then(dataUpdate => {
if(dataUpdate && dataUpdate.modifiedCount > 0){
return sendMessage(res, 11, data, token);
} else {
return sendError(res,500,101,`Some error occurred while updating ${quantity} ad(s) for the User.`, token);
}
})
.catch(err => {
return sendError(res,500,101,err.message || `Some error occurred while updating ${quantity} ad(s) for the User.`, token);
});
})
.catch(err => {
return sendError(res,500,101,err.message || `Some error occurred while getting ${quantity} ad(s) for the User.`, token);
});
}
})
.catch(err => {
return sendError(res,500,101,err.message || `Some error occurred while getting ${quantity} ad(s) for the User.`, token);
});
})
.catch(err => {
return sendError(res,500,102,err.message || `Some error occurred while getting ${quantity} ad(s) for the User.`, token);
});
})
.catch(err => {
return sendError(res,500,100,err.message || `Some error occurred while getting ${quantity} ad(s) for the User.`, token);
});
} else {
sendError(res, 500, -1, `No quantity given`, token);
}
};
// Get History
exports.history = (req, res) => {
const token = checkLogin(req, res);
if(token){
const id = token.id;
Video.aggregate([
{$match: {userId: id, $expr: {$gt: [{$size: "$watchedDates"}, 0]}}},
{$limit: 300},
{$project: {
videoId: true,
source: true,
tags: true,
interest: true,
views: {$size: '$watchedDates'},
watchedDate: {$arrayElemAt: ["$watchedDates", -1]},
createdAt: true,
updatedAt: true
}},
{$sort: {watchedDate: -1}}])
.then(async data => {
let yt_results = [];
let dm_results = [];
let yt_videoIds = "";
let dm_videoIds = "";
for(const i in data) {
if(data[i].source === youtube.name) {
yt_videoIds = yt_videoIds + data[i].videoId + ",";
} else if (data[i].source === dailymotion.name) {
dm_videoIds = dm_videoIds + data[i].videoId + ",";
}
}
if(yt_videoIds !== ""){
const uri = youtube.baseAPIUrl + '/videos' + '?part=snippet&part=statistics&id=' + yt_videoIds.slice(0, -1) + '&key=' + youtube.YOUTUBE_API_KEY;
const dataVideos = await asyncRequest(uri, {});
if (dataVideos.response.statusCode === 200 && dataVideos.body.items.length > 0) {
yt_results = dataVideos.body.items;
}
}
if(dm_videoIds !== ""){
const uri = dailymotion.baseAPIUrl + '/videos?ids='+dm_videoIds.slice(0, -1)+'&fields=thumbnail_480_url%2Ctitle%2Cid';
const data = await asyncRequest(uri, {});
const response = data.response;
const jsonBody = data.body;
if(response.statusCode === 200){
dm_results = jsonBody.list;
}
}
for(const i in data) {
if(data[i].source === youtube.name) {
const obj = yt_results.filter(obj => obj.id === data[i].videoId);
data[i].imageUrl = obj[0].snippet.thumbnails.medium.url ? obj[0].snippet.thumbnails.medium.url : null;
data[i].title = obj[0].snippet.title ? obj[0].snippet.title : null;
} else if (data[i].source === dailymotion.name) {
const obj = dm_results.filter(obj => obj.id === data[i].videoId);
data[i].imageUrl = obj[0].thumbnail_480_url ? obj[0].thumbnail_480_url : null;
data[i].title = obj[0].title ? obj[0].title : null;
}
}
return sendMessage(res, 12, data, token)
})
.catch(err => {
return sendError(res,500,100,err.message || "Some error occurred while getting the User history.", token);
});
}
};

View file

@ -1,473 +0,0 @@
const db = require("../models/mongodb.model");
const request = require('request');
const {sendError, sendMessage} = require ("../config/response.config");
const {checkLogin} = require("../config/sessionJWT.config");
const {youtube, dailymotion} = require("../config/host.config");
const {asyncRequest, asyncInterest} = require("../config/functions.config");
const ObjectId = require('mongoose').Types.ObjectId;
const Video = db.videos;
// Search Videos
exports.search = async (req, res) => {
const token = checkLogin(req, res);
if(token && typeof req.query.q !== 'undefined'){
const query = req.query.q;
const maxResults = req.query.maxResults ? req.query.maxResults : 45;
const pageToken = req.query.pageToken ? req.query.pageToken : undefined;
let sources;
if(typeof req.query.sources !== 'undefined' && req.query.sources !== ''){
sources = req.query.sources.split(',');
} else {
sources = ["yt", "dm"];
}
let yt_results = [];
let dm_results = [];
for(const i in sources){
if(sources[i] === youtube.shortname){
if(youtube.YOUTUBE_API_KEY !== 'undefined' && youtube.YOUTUBE_API_KEY !== ''){
let uri;
if(query !== ''){
if(typeof pageToken !== 'undefined'){
uri = youtube.baseAPIUrl+'/search'+'?part=snippet&maxResults='+maxResults+'&q='+query+'&pageToken='+pageToken+'&key='+youtube.YOUTUBE_API_KEY;
} else{
uri = youtube.baseAPIUrl+'/search'+'?part=snippet&maxResults='+maxResults+'&q='+query+'&key='+youtube.YOUTUBE_API_KEY;
}
const dataIds = await asyncRequest(uri, {});
if(dataIds.response.statusCode === 200 && dataIds.body.items.length > 0){
let yt_videoIds = "";
dataIds.body.items.forEach(item => yt_videoIds = yt_videoIds+item.id.videoId+",");
uri = youtube.baseAPIUrl+'/videos'+'?part=snippet&part=statistics&id='+yt_videoIds.slice(0, -1)+'&key='+youtube.YOUTUBE_API_KEY;
const dataVideos = await asyncRequest(uri, {});
if(dataVideos.response.statusCode === 200 && dataVideos.body.items.length > 0){
yt_results = dataVideos.body.items;
}
}
} else {
uri = youtube.baseAPIUrl+'/videos'+'?part=snippet&part=statistics&chart=mostPopular&maxResults='+maxResults+'&key='+youtube.YOUTUBE_API_KEY;
const dataVideos = await asyncRequest(uri, {});
if(dataVideos.response.statusCode === 200 && dataVideos.body.items.length > 0){
yt_results = dataVideos.body.items;
}
}
} else{
return sendError(res, 500, -1, `Error Env Variable DAILYMOTION_API_KEY missing, please contact the admin.`, token);
}
} else if(sources[i] === dailymotion.shortname){
if(dailymotion.DAILYMOTION_API_KEY !== 'undefined' && dailymotion.DAILYMOTION_API_KEY !== '') {
let uri;
if(query !== ''){
uri = dailymotion.baseAPIUrl + '/videos?limit='+maxResults+'&search='+query+'&fields=created_time%2Cdescription%2Cthumbnail_480_url%2Clikes_total%2Ctitle%2Cid%2Cembed_url%2Cviews_total%2Cowner.username%2Cowner.id%2Cchannel.name';
} else {
uri = dailymotion.baseAPIUrl + '/videos?limit='+maxResults+'&sort=trending&fields=created_time%2Cdescription%2Cthumbnail_480_url%2Clikes_total%2Ctitle%2Cid%2Cembed_url%2Cviews_total%2Cowner.username%2Cowner.id%2Cchannel.name';
}
const data = await asyncRequest(uri, {});
const response = data.response;
const jsonBody = data.body;
if(response.statusCode === 200){
dm_results = jsonBody.list;
}
} else{
return sendError(res, 500, -1, `Error Env Variable DAILYMOTION_API_KEY missing, please contact the admin.`, token);
}
}
}
let results = [];
for(let i = 0; i < Math.max(dm_results.length, yt_results.length); i++){
// Youtube
if(yt_results.length > i){
const yt_data = {
videoId: yt_results[i].id,
source: youtube.name,
imageUrl: yt_results[i].snippet.thumbnails.medium.url ? yt_results[i].snippet.thumbnails.medium.url : null,
title: yt_results[i].snippet.title ? yt_results[i].snippet.title : null,
channelTitle: yt_results[i].snippet.channelTitle ? yt_results[i].snippet.channelTitle : null,
channelUrl: youtube.baseChannelUrl+yt_results[i].snippet.channelId ? yt_results[i].snippet.channelId : null,
description: yt_results[i].snippet.description ? yt_results[i].snippet.description : null,
embedUrl: 'https://www.youtube.com/embed/'+yt_results[i].id,
interest: await asyncInterest(yt_results[i].snippet.categoryId, youtube.name),
views: yt_results[i].statistics.viewCount ? parseInt(yt_results[i].statistics.viewCount) : null,
likes: yt_results[i].statistics.likeCount ? parseInt(yt_results[i].statistics.likeCount) : null,
dislikes: yt_results[i].statistics.dislikeCount ? parseInt(yt_results[i].statistics.dislikeCount) : null,
publishedAt: yt_results[i].snippet.publishedAt ? yt_results[i].snippet.publishedAt : null
};
results.push(yt_data);
}
// Dailymotion
if(dm_results.length > i) {
const channelTitle = dm_results[i]['owner.username'] ? dm_results[i]['owner.username'] : null;
const dm_data = {
videoId: dm_results[i].id ? dm_results[i].id : null,
source: dailymotion.name,
imageUrl: dm_results[i].thumbnail_480_url ? dm_results[i].thumbnail_480_url : null,
title: dm_results[i].title ? dm_results[i].title : null,
channelTitle: channelTitle.charAt(0).toUpperCase() + channelTitle.slice(1),
channelUrl: dailymotion.baseChannelUrl + channelTitle,
description: dm_results[i].description ? dm_results[i].description : null,
embedUrl: dm_results[i].embed_url ? dm_results[i].embed_url : null,
interest: await asyncInterest(dm_results[i]['channel.name'], dailymotion.name),
views: dm_results[i].views_total ? parseInt(dm_results[i].views_total) : null,
likes: dm_results[i].likes_total ? parseInt(dm_results[i].likes_total) : null,
dislikes: null,
publishedAt: dm_results[i].created_time ? new Date(dm_results[i].created_time * 1000) : null
};
results.push(dm_data);
}
}
return sendMessage(res, 31, results, token);
} else{
return sendError(res, 500, -1, `No q given`, token);
}
};
// Get Video with id of source
exports.get = (req, res) => {
if(typeof req.query.source !== 'undefined' && typeof req.params.id !== 'undefined'){
const source = req.query.source;
const id = req.params.id;
if(source === youtube.shortname){
if(youtube.YOUTUBE_API_KEY !== 'undefined' && youtube.YOUTUBE_API_KEY !== ''){
const uri = youtube.baseAPIUrl+'/videos'+'?part=snippet&part=statistics&id='+id+'&key='+youtube.YOUTUBE_API_KEY;
request(uri,{},async function (error, response, body){
if(typeof body !== 'undefined'){
const jsonBody = JSON.parse(body);
if(jsonBody.items.length !== 0 &&
typeof jsonBody.items[0] !== 'undefined' &&
typeof jsonBody.items[0].id !== 'undefined' &&
jsonBody.items[0].id === id){
const imageUrl = jsonBody.items[0].snippet.thumbnails.standard.url ? jsonBody.items[0].snippet.thumbnails.standard.url : null;
const title = jsonBody.items[0].snippet.title ? jsonBody.items[0].snippet.title : null;
const channelId = jsonBody.items[0].snippet.channelId ? jsonBody.items[0].snippet.channelId : null;
const channelTitle = jsonBody.items[0].snippet.channelTitle ? jsonBody.items[0].snippet.channelTitle : null;
const description = jsonBody.items[0].snippet.description ? jsonBody.items[0].snippet.description : null;
//const embedUrl = jsonBody.embed_url ? jsonBody.embed_url : null;
const publishedAt = jsonBody.items[0].snippet.publishedAt ? jsonBody.items[0].snippet.publishedAt : null;
const interest = jsonBody.items[0].snippet.categoryId ? await asyncInterest(jsonBody.items[0].snippet.categoryId, youtube.name): null;
const views = jsonBody.items[0].statistics.viewCount ? parseInt(jsonBody.items[0].statistics.viewCount) : null;
const likes = jsonBody.items[0].statistics.likeCount ? parseInt(jsonBody.items[0].statistics.likeCount) : null;
const dislikes = jsonBody.items[0].statistics.dislikeCount ? parseInt(jsonBody.items[0].statistics.dislikeCount) : null;
const data = {
videoId: id,
source: youtube.name,
imageUrl: imageUrl,
title: title,
channelTitle: channelTitle,
channelUrl: youtube.baseChannelUrl+channelId,
description: description,
embedUrl: 'https://www.youtube.com/embed/'+id,
interest: interest,
views: views,
likes: likes,
dislikes: dislikes,
publishedAt: publishedAt
};
return sendMessage(res, 32, data);
} else{
return sendError(res, 404, -1, `No result`);
}
} else{
return sendError(res, 500, -1, error);
}
});
} else{
return sendError(res, 500, -1, `Error Env Variable YOUTUBE_API_KEY missing, please contact the admin.`);
}
} else if(source === dailymotion.shortname){
if(dailymotion.DAILYMOTION_API_KEY !== 'undefined' && dailymotion.DAILYMOTION_API_KEY !== ''){
const uri = dailymotion.baseAPIUrl+'/video/'+id+'?fields=created_time%2Cdescription%2Cthumbnail_480_url%2Clikes_total%2Ctitle%2Cid%2Cembed_url%2Cviews_total%2Cowner.username%2Cowner.id%2Cchannel.name';
request(uri,{},async function (error, response, body) {
if (typeof body !== 'undefined') {
const jsonBody = JSON.parse(body);
if(response.statusCode === 200 &&
typeof jsonBody.id !== 'undefined' &&
jsonBody.id === id){
const imageUrl = jsonBody.thumbnail_480_url ? jsonBody.thumbnail_480_url : null;
const title = jsonBody.title ? jsonBody.title : null;
//const channelId = jsonBody['owner.id'] ? jsonBody['owner.id'] : null;
const channelTitle = jsonBody['owner.username'] ? jsonBody['owner.username'] : null;
const description = jsonBody.description ? jsonBody.description : null;
const embedUrl = jsonBody.embed_url ? jsonBody.embed_url : null;
const publishedAt = jsonBody.created_time ? new Date(jsonBody.created_time * 1000) : null;
const interest = jsonBody['channel.name'] ? await asyncInterest(jsonBody['channel.name'], dailymotion.name) : null;
const views = jsonBody.views_total ? parseInt(jsonBody.views_total) : null;
const likes = jsonBody.likes_total ? parseInt(jsonBody.likes_total) : null;
const dislikes = null;
const data = {
videoId: id,
source: dailymotion.name,
imageUrl: imageUrl,
title: title,
channelTitle: channelTitle.charAt(0).toUpperCase() + channelTitle.slice(1),
channelUrl: dailymotion.baseChannelUrl+channelTitle,
description: description,
embedUrl: embedUrl,
interest: interest,
views: views,
likes: likes,
dislikes: dislikes,
publishedAt: publishedAt
};
return sendMessage(res, 32, data);
} else{
return sendError(res, 404, -1, jsonBody.error.message);
}
}
});
} else{
return sendError(res, 500, -1, `Error Env Variable DAILYMOTION_API_KEY missing, please contact the admin.`);
}
} else{
return sendError(res, 500, -1, `Wrong source name`);
}
} else{
return sendError(res, 500, -1, `No source or/and id given`);
}
};
// Create a new Video
exports.create = (req, res) => {
const token = checkLogin(req, res);
if(token &&
typeof req.body.source !== 'undefined' &&
typeof req.body.interest !== 'undefined' &&
typeof req.params.id !== 'undefined'){
const id = req.params.id;
Video.exists({userId: token.id, videoId: id, source: req.body.source, isActive: true}, function (err, docs){
if(err){
sendError(res, 500,100,err.message || "Some error occurred while checking if the Video already exists.", token);
} else{
if(docs === null) {
let video;
video = new Video({
userId: token.id,
videoId: id,
source: req.body.source,
interest: req.body.interest,
watchedDates: [new Date()]
});
// Save Video in the database
video
.save(video)
.then(data => {
return sendMessage(res, 33, data, token)
})
.catch(err => {
return sendError(res, 500,100,err.message || "Some error occurred while creating the Video.", token);
});
} else{
const id = docs._id.toString();
Video.findByIdAndUpdate(id, {$push: {watchedDates: [new Date()]}}, {useFindAndModify: false, new: true})
.then(data => {
if(data) {
return sendMessage(res, 33, data, token);
} else {
return sendError(res, 404, 105, `Video not found with id=${id}`, token);
}
})
.catch(err => {
return sendError(res, 500, 100, err.message || `Some error occurred while updating the Video with id=${id}`, token);
});
}
}
});
} else {
return sendError(res, 500, -1, `No source or interest or id given`, token);
}
};
// Retrieve all Videos
exports.findAll = (req, res) => {
const token = checkLogin(req, res);
if(token){
let query = {};
let condition;
const userId = req.query.userId;
condition = userId ? userId : undefined;
query.userId = condition;
const videoId = req.query.videoId;
condition = videoId ? videoId : undefined;
query.videoId = condition;
const source = req.query.source;
condition = source ? source : undefined;
query.source = condition;
const interests = req.query.interests;
condition = interests ? {$in: interests} : undefined;
query["interests.interest"] = condition
const isActive = req.query.isActive;
condition = isActive ? isActive : undefined;
query.isActive = condition;
const sort = req.query.sort;
if(sort !== 'undefined'){
switch (sort){
case 'asc':
condition = {videoId: 1};
break;
case 'desc':
condition = {videoId: -1};
break;
case 'createdAtAsc':
condition = {createdAt: 1};
break;
case 'createdAtDesc':
condition = {createdAt: -1};
break;
case 'updatedAtAsc':
condition = {updatedAt: 1};
break;
case 'updatedAtDesc':
condition = {updatedAt: -1};
break;
default:
condition = {createdAt: -1};
}
}
const query_sort = {sort: condition};
// Remove undefined key
Object.keys(query).forEach(key => query[key] === undefined ? delete query[key] : {});
console.log(query);
Video.find(query, {}, query_sort)
.then(data => {
return sendMessage(res, 34, data, token);
})
.catch(err => {
return sendError(res,500,100,err.message || "Some error occurred while finding the Videos.", token);
});
}
};
// Find single Video with id
exports.findOne = (req, res) => {
const token = checkLogin(req, res);
if(token && typeof req.params.id !== 'undefined') {
const id = req.params.id;
if(id && ObjectId.isValid(id)){
Video.findById(id, {})
.then(data => {
if(data){
return sendMessage(res, 35, data, token);
} else {
return sendError(res,404,105,`Video not found with id=${id}`, token);
}
})
.catch(err => {
return sendError(res,500,100,err.message || `Some error occurred while finding the Video with id=${id}`, token);
});
} else {
return sendError(res, 500, -1, `Error id is not valid`, token);
}
} else {
return sendError(res, 500, -1, `No id given`, token);
}
};
// Update Video with id
exports.update = (req, res) => {
const token = checkLogin(req, res);
if(token && typeof req.params.id !== 'undefined') {
const id = req.params.id;
if(typeof req.body._id !== 'undefined' || typeof req.body.id !== 'undefined'){
return sendError(res, 500, -1, `User do not have the permission to modify id or _id`, token);
} else{
let update = {};
let condition;
const watchedDate = req.body.watchedDate;
if(typeof watchedDate !== 'undefined'){
if(watchedDate){
condition = {watchedDates: [new Date()]}
} else {
condition = undefined;
}
} else{
condition = undefined;
}
update.$push = condition;
const watchedDates = req.body.watchedDates ? req.body.watchedDates : undefined;
update.watchedDates = watchedDates;
const isActive = req.body.isActive;
if(typeof isActive !== 'undefined'){
condition = isActive;
} else{
condition = undefined;
}
update.isActive = condition;
// Remove undefined key
Object.keys(update).forEach(key => update[key] === undefined ? delete update[key] : {});
if(id && ObjectId.isValid(id)){
Video.updateOne({_id: id, userId: token.id, isActive: true}, update)
.then(data => {
if(data) {
return sendMessage(res, 36, update, token);
} else {
return sendError(res, 404, -1, `Video not found with id=${id}`, token);
}
})
.catch(err => {
return sendError(res, 500, -1, err.message || `Some error occurred while updating the Video with id=${id}`, token);
});
} else {
return sendError(res, 500, -1, `Error id is not valid`, token);
}
}
} else {
return sendError(res, 500, -1, `No id given`, token);
}
};
// Delete Video with id
exports.delete = (req, res) => {
const token = checkLogin(req, res);
if(token && typeof req.params.id !== 'undefined') {
const id = req.params.id;
if(id && ObjectId.isValid(id)){
Video.updateOne({_id: id, userId: token.id, isActive: true}, {isActive: false}, {useFindAndModify: false})
.then(data => {
if(data.modifiedCount > 0) {
return sendMessage(res, 37, {message: `Video ${id} was successfully deleted.`}, token);
} else {
return sendError(res, 404, 105, `Video not found with id=${id}`, token);
}
})
.catch(err => {
return sendError(res, 500, 100, err.message || `Some error occurred while deleting the Video with id=${id}`, token);
});
} else {
return sendError(res, 500, -1, `Error id is not valid`, token);
}
} else {
return sendError(res, 500, -1, `No id given`, token);
}
};
// Delete all Videos
exports.deleteAll = (req, res) => {
const token = checkLogin(req, res);
if(token) {
Video.updateMany({userId: {$eq: token.id}, isActive: true}, {isActive: false})
.then(data => {
return sendMessage(res, 38, {
message: `${data.modifiedCount} Videos were deleted successfully.`,
});
})
.catch(err => {
return sendError(res, 500, 100, err.message || "Some error occurred while removing all Videos.");
});
}
};

View file

@ -1,14 +0,0 @@
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtW31Xj62sjbJVBxnn0G2
Habc22q7/pFIBdfn8+OsajdNVnmtfRNOsSXZP8sNhXt1QLPSgxZ/wogG0fLXIX2+
ewzPgqrwTnr+quJ1DZ6RqOY3G1PGOibgk25aHkIXJ/gTPk1yTT6pjUmKiaGKM8pt
M2wGwugCdEH5Wndgby8Jej30v/PPzyPxTSXrIWDeaSMX+jQyFZTGgEdgL7JvjkTj
qLtfWKIAcEeO4PzOlRXVvbzBoYphBiZqkbzEeuOjSLPxgy4cQdbqVMlJ/lZt0SBO
MLiIUBTufLcJS3ApesiZWWfUCq+pFFdhEABc9qrtVumzhmzWAv2rKVrHRXbguxc/
eHKlRjAE4qmnNnTP2fsAuQIPkXVHOPWdXM1IBwnhXVB+XhxEHSANx/2oeKS6fO/e
1oNJCiVkHin9gC8vkU9seEN73lNKZ5wPXMqTYUGA65dCY+8li+n/1pveJOJozFk7
amkmOAPTi44lBJmxRm88XBHC3TXz6tFqX3phMqFDcQs2D9s3/2UylK0dSH5MSLnb
9x24/ykO4RlPRVCC90vwlxzbnb0rfQVlT4dKcE6OIyXw3UsqIqFnXWmm+hnGu4QH
Ysr+i1VIhPOs9YdZwlqhzcPTuNcdxmxy9ZfZ8KlLIWbAMbSH+obwm4w+HYTZjspe
2MwrKGgzpl4YW7ct/ViqeQMCAwEAAQ==
-----END PUBLIC KEY-----

View file

@ -1,8 +0,0 @@
#!/usr/bin/env bash
ssh-keygen -t rsa -b 4096 -m PEM -f jwtRS256.key -q -N ""
openssl rsa -in jwtRS256.key -pubout -outform PEM -out jwtRS256.key.pub
#rm .env
echo "JWTRS256_PRIVATE_KEY='`cat ./jwtRS256.key | base64 -w 0`'" >> .env
echo "JWTRS256_PUBLIC_KEY='`cat ./jwtRS256.key.pub | base64 -w 0`'" >> .env
source .env
rm jwtRS256.key

View file

@ -1,44 +0,0 @@
module.exports = mongoose => {
let schema = mongoose.Schema({
userId: String,
title: String,
images: {
type: Array,
default: []
},
url: {
type: String,
default: null
},
interests: {
type: Array,
default: []
},
comment: {
type: String,
default: null
},
views: {
type: Array,
default: []
},
isVisible: {
type: Boolean,
default: true
},
isActive: {
type: Boolean,
default: true
}
},
{ timestamps: true }
);
schema.method("toJSON", function() {
const { __v, _id, ...object } = this.toObject();
object.id = _id;
return object;
});
return mongoose.model("ads", schema);
};

View file

@ -1,24 +0,0 @@
module.exports = mongoose => {
let schema = mongoose.Schema({
userId: String,
videoIds: {
type: Array,
default: []
},
name: String,
isActive: {
type: Boolean,
default: true
}
},
{ timestamps: true }
);
schema.method("toJSON", function() {
const { __v, _id, ...object } = this.toObject();
object.id = _id;
return object;
});
return mongoose.model("playlists", schema);
};

View file

@ -1,48 +0,0 @@
const roles = require("../objects/role.model");
module.exports = mongoose => {
let schema = mongoose.Schema({
email: String,
hashPass: String, // WARNING: We don't want to send back the hashPass
login: String,
role: {
type: Object,
default: roles.User
},
company: String,
profileImageUrl: {
type: String,
default: "https://www.handiclubnimois.fr/wp-content/uploads/2020/10/blank-profile-picture-973460_1280.png"
},
dateOfBirth: {
type: Date,
default: null
},
gender: {
type: String,
default: null
},
interests: {
type: Array,
default: null
},
isActive: {
type: Boolean,
default: true
},
lastConnexion: {
type: Date,
default: null
}
},
{ timestamps: true }
);
schema.method("toJSON", function() {
const { __v, _id, ...object } = this.toObject();
object.id = _id;
return object;
});
return mongoose.model("users", schema);
};

View file

@ -1,29 +0,0 @@
module.exports = mongoose => {
let schema = mongoose.Schema({
userId: String,
videoId: String,
source: String,
interest: {
type: String,
default: null
},
watchedDates: {
type: Array,
default: null
},
isActive: {
type: Boolean,
default: true
}
},
{ timestamps: true }
);
schema.method("toJSON", function() {
const { __v, _id, ...object } = this.toObject();
object.id = _id;
return object;
});
return mongoose.model("videos", schema);
};

View file

@ -1,19 +0,0 @@
const dbConfig = require("../config/mongodb.config");
const mongoose = require("mongoose");
mongoose.Promise = global.Promise;
const db = {};
db.mongoose = mongoose;
if(typeof process.env.NODE_ENV !== 'undefined' && process.env.NODE_ENV === 'production'){
db.url = dbConfig.prodUrl;
} else {
db.url = dbConfig.devUrl;
}
db.users = require("./database/users.model")(mongoose);
db.playlists = require("./database/playlists.model")(mongoose);
db.videos = require("./database/videos.model")(mongoose);
db.ads = require("./database/ads.model")(mongoose);
module.exports = db;

View file

@ -1,10 +0,0 @@
class Image {
constructor(base64, url, description, type){
this.base64 = base64;
this.url = url;
this.description = description;
this.type = type;
}
}
module.exports = Image;

View file

@ -1,22 +0,0 @@
module.exports = {
User: {
name: "user",
permission: 0,
isAccepted: true
},
Advertiser: {
name: "advertiser",
permission: 5,
isAccepted: false
},
Admin: {
name: "admin",
permission: 10,
isAccepted: false
},
SuperAdmin: {
name: "superAdmin",
permission: 1000,
isAccepted: true
}
};

View file

@ -1,157 +0,0 @@
const {youtube, dailymotion} = require('../../config/host.config');
module.exports = [
{
id: 0,
interest: "Actualités",
categories: [
{id: "news", name: "News", source: dailymotion.name},
{id: "25", name: "News & Politics", source: youtube.name},
]
},
{
id: 1,
interest: "Animaux",
categories: [
{id: "animals", name: "animaux", source: dailymotion.name},
{id: "15", name: "Pets & Animals", source: youtube.name},
]
},
{
id: 2,
interest: "Arts",
categories: [
{id: "creation", name: "Art", source: dailymotion.name},
{id: "", name: "", source: youtube.name},
]
},
{
id: 3,
interest: "Autos",
categories: [
{id: "auto", name: "Auto-Moto", source: dailymotion.name},
{id: "2", name: "Autos & Vehicles", source: youtube.name},
]
},
{
id: 4,
interest: "Divertissements",
categories: [
{id: "tv", name: "TV", source: dailymotion.name},
{id: "fun", name: "Humour & Divertissement", source: dailymotion.name},
{id: "webcam", name: "Webcam", source: dailymotion.name},
{id: "23", name: "Comedy", source: youtube.name},
{id: "24", name: "Entertainment", source: youtube.name},
{id: "43", name: "Shows", source: youtube.name}
]
},
{
id: 5,
interest: "Éducation",
categories: [
{id: "school", name: "Éducation", source: dailymotion.name},
{id: "27", name: "Education", source: youtube.name}
]
},
{
id: 6,
interest: "Événements",
categories: [
{id: "", name: "", source: dailymotion.name},
{id: "19", name: "Travel & Events", source: youtube.name},
]
},
{
id: 7,
interest: "Films",
categories: [
{id: "shortfilms", name: "Cinéma", source: dailymotion.name},
{id: "1", name: "Film & Animation", source: youtube.name},
{id: "18", name: "Short Movies", source: youtube.name},
{id: "30", name: "Movies", source: youtube.name},
{id: "31", name: "Anime/Animation", source: youtube.name},
{id: "32", name: "Action/Adventure", source: youtube.name},
{id: "33", name: "Comedy", source: youtube.name},
{id: "35", name: "Documentary", source: youtube.name},
{id: "36", name: "Drama", source: youtube.name},
{id: "39", name: "Horror", source: youtube.name},
{id: "40", name: "Sci-Fi/Fantasy", source: youtube.name},
{id: "41", name: "Thriller", source: youtube.name},
{id: "42", name: "Shorts", source: youtube.name},
{id: "44", name: "Trailers", source: youtube.name}
]
},
{
id: 8,
interest: "Jeux vidéo",
categories: [
{id: "videogames", name: "Jeux vidéo", source: dailymotion.name},
{id: "20", name: "Gaming", source: youtube.name},
]
},
{
id: 9,
interest: "Kids",
categories: [
{id: "kids", name: "Kids", source: dailymotion.name},
{id: "", name: "", source: youtube.name},
]
},
{
id: 10,
interest: "Modes de vie",
categories: [
{id: "lifestyle", name: "Lifestyle & Tutoriels", source: dailymotion.name},
{id: "26", name: "Howto & Style", source: youtube.name},
]
},
{
id: 11,
interest: "Musiques",
categories: [
{id: "music", name: "Musique", source: dailymotion.name},
{id: "10", name: "Music", source: youtube.name},
]
},
{
id: 12,
interest: "People",
categories: [
{id: "people", name: "Amis & Famille", source: dailymotion.name},
{id: "21", name: "Videoblogging", source: youtube.name},
{id: "22", name: "People & Blogs", source: youtube.name},
{id: "37", name: "Family", source: youtube.name},
]
},
{
id: 13,
interest: "Science et Technologie",
categories: [
{id: "tech", name: "Tech", source: dailymotion.name},
{id: "28", name: "Science & Technology", source: youtube.name},
]
},
{
id: 14,
interest: "Sports",
categories: [
{id: "sport", name: "Sport", source: dailymotion.name},
{id: "17", name: "Sports", source: youtube.name},
]
},
{
id: 15,
interest: "Voyages",
categories: [
{id: "travel", name: "Voyages", source: dailymotion.name},
{id: "38", name: "Foreign", source: youtube.name},
]
},
{
id: 16,
interest: "Autres",
categories: [
{id: "29", name: "Nonprofits & Activism", source: youtube.name},
{id: "33", name: "Classics", source: youtube.name}
]
}
];

View file

@ -1,24 +0,0 @@
const ads = require("../controllers/ad.controller");
module.exports = app => {
let router = require("express").Router();
// Create a new Ad
router.post("/ad/create", ads.create);
// Retrieve all Ad from id if admin or session id
router.get("/ad/findAll", ads.findAll);
// Find single Ad from id if admin or session id
router.get("/ad/findOne/:id", ads.findOne);
// Update a Ad with ad id
router.put("/ad/update/:id", ads.update);
// Delete a Ad with ad id
router.delete("/ad/delete/:id", ads.delete);
// Delete all Ad from id if admin or session id
router.delete("/ad/deleteAll", ads.deleteAll);
app.use('/api', router);
};

View file

@ -1,9 +0,0 @@
const misc = require("../controllers/misc.controller");
module.exports = app => {
let router = require("express").Router();
// Get all interests available
router.get("/misc/getInterests", misc.getInterests);
app.use('/api', router);
};

View file

@ -1,24 +0,0 @@
const playlists = require("../controllers/playlist.controller");
module.exports = app => {
let router = require("express").Router();
// Create a new Playlist
router.post("/playlist/create", playlists.create);
// Retrieve all Playlist from id if admin or session id
router.get("/playlist/findAll", playlists.findAll);
// Find single Playlist from id if admin or session id
router.get("/playlist/findOne/:id", playlists.findOne);
// Update a Playlist with playlist id
router.put("/playlist/update/:id", playlists.update);
// Delete a Playlist with playlist id
router.delete("/playlist/delete/:id", playlists.delete);
// Delete all Playlists from id if admin or session id
router.delete("/playlist/deleteAll", playlists.deleteAll);
app.use('/api', router);
};

View file

@ -1,42 +0,0 @@
const users = require("../controllers/user.controller");
module.exports = app => {
let router = require("express").Router();
// Authenticate a User
router.post("/user/auth", users.auth);
// Logout a User
router.delete("/user/logout", users.logout);
// Request password reset with email
router.post("/user/resetPass", users.resetPass);
// Create and Save a new User
router.post("/user/create", users.create);
// Retrieve all Users if admin
router.get("/user/findAll", users.findAll);
// Find single User from id if admin or session id
router.get("/user/findOne/:id", users.findOne);
// Update a User from id if admin or session id
router.put("/user/update/:id", users.update);
// Delete a User from id if admin or session id
router.delete("/user/delete/:id", users.delete);
// Delete all Users if superAdmin
router.delete("/user/deleteAll", users.deleteAll);
// Get all Roles depending on the User session id
router.get("/user/roles", users.roles);
// Get 1 or multiple ad adapted to the User session id
router.get("/user/ad", users.ad);
// Get History
router.get("/user/history", users.history);
app.use('/api', router);
};

View file

@ -1,30 +0,0 @@
const videos = require("../controllers/video.controller");
module.exports = app => {
let router = require("express").Router();
// Search Videos
router.get("/video/search", videos.search);
// Get Video with id of source
router.get("/video/get/:id", videos.get);
// Create a new Video
router.post("/video/create/:id", videos.create);
// Retrieve all Videos
router.get("/video/findAll", videos.findAll);
// Find single Video with id
router.get("/video/findOne/:id", videos.findOne);
// Update Video with id
router.put("/video/update/:id", videos.update);
// Delete Video with id
router.delete("/video/delete/:id", videos.delete);
// Delete all Videos
router.delete("/video/deleteAll", videos.deleteAll);
app.use('/api', router);
};

View file

@ -1,41 +0,0 @@
version: '3.8'
services:
frontend:
container_name: frontend
build: .
command: ng serve --host 0.0.0.0
volumes:
- ./src:/data/frontend/
- ./node_modules:/data/frontend/node_modules
ports:
- 4200:4200
depends_on:
- backend
links:
- backend
backend:
container_name: backend
build: ./backend
command: node server.js
volumes:
- ./backend:/data/backend
- ./backend/node_modules:/data/backend/node_modules
- ./backend/app:/data/backend/app
ports:
- 3000:3000
depends_on:
- mongodb
links:
- mongodb
environment:
NODE_ENV: development
mongodb:
image: mongo
container_name: mongodb
volumes:
- ./backend/database:/data/db
ports:
- 27017:27017

View file

@ -1,37 +0,0 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
browserName: 'chrome'
},
directConnect: true,
SELENIUM_PROMISE_MANAGER: false,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({
spec: {
displayStacktrace: StacktraceOption.PRETTY
}
}));
}
};

View file

@ -1,23 +0,0 @@
import { browser, logging } from 'protractor';
import { AppPage } from './app.po';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', async () => {
await page.navigateTo();
expect(await page.getTitleText()).toEqual('frontend app is running!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});

View file

@ -1,11 +0,0 @@
import { browser, by, element } from 'protractor';
export class AppPage {
async navigateTo(): Promise<unknown> {
return browser.get(browser.baseUrl);
}
async getTitleText(): Promise<string> {
return element(by.css('app-root .content span')).getText();
}
}

View file

@ -1,13 +0,0 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es2018",
"types": [
"jasmine",
"node"
]
}
}

View file

@ -1,44 +0,0 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
jasmine: {
// you can add configuration options for Jasmine here
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
// for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321`
},
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
jasmineHtmlReporter: {
suppressAll: true // removes the duplicated traces
},
coverageReporter: {
dir: require('path').join(__dirname, './coverage/frontend'),
subdir: '.',
reporters: [
{ type: 'html' },
{ type: 'text-summary' }
]
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

View file

@ -1,67 +0,0 @@
{
"name": "frontend",
"version": "1.0.0",
"scripts": {
"ng": "ng",
"start": "node server.js",
"dev": "ng serve",
"build": "ng build --configuration production",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "^12.2.11",
"@angular/cdk": "^12.2.11",
"@angular/cli": "~12.2.11",
"@angular/common": "^12.2.11",
"@angular/compiler": "^12.2.11",
"@angular/compiler-cli": "~12.2.11",
"@angular/core": "^12.2.11",
"@angular/forms": "^12.2.11",
"@angular/material": "^12.2.11",
"@angular/platform-browser": "^12.2.11",
"@angular/platform-browser-dynamic": "^12.2.11",
"@angular/router": "^12.2.11",
"@ng-bootstrap/ng-bootstrap": "^10.0.0",
"angular-responsive-carousel": "^2.1.2",
"body-parser": "^1.19.0",
"bootstrap": "^5.1.3",
"chart.js": "^2.9.3",
"cookie-parser": "^1.4.5",
"cors": "^2.8.5",
"dotenv": "^10.0.0",
"express": "^4.17.1",
"jquery": "^3.6.0",
"jsonwebtoken": "^8.5.1",
"mongoose": "^6.0.12",
"ng2-charts": "^2.2.3",
"popper": "^1.0.1",
"request": "^2.88.2",
"rxjs": "~6.6.0",
"tslib": "^2.0.0",
"typescript": "~4.3.5",
"zone.js": "~0.11.3"
},
"devDependencies": {
"@angular-devkit/build-angular": "~12.2.11",
"@angular/cli": "~12.2.11",
"@angular/compiler-cli": "~12.2.11",
"@angular/localize": "^12.2.11",
"@types/jasmine": "~3.6.0",
"@types/node": "^12.11.1",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~6.3.5",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.0.3",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~4.3.5"
}
}

View file

@ -1,84 +0,0 @@
const path = require('path');
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
const cookieParser = require('cookie-parser');
app.use(cookieParser());
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({extended:true}));
app.use(bodyParser.json());
const cors = require('cors');
app.use(cors({origin: 'http://127.0.0.1:4200', credentials: true}));
const db = require("./app-backend/models/mongodb.model");
console.log("Db Url: ",db.url);
db.mongoose
.connect(db.url, {
useNewUrlParser: true,
useUnifiedTopology: true
}, function (err){
const admin = new db.mongoose.mongo.Admin(db.mongoose.connection.db);
admin.buildInfo(function (err, info) {
console.log("MongoDB Version: "+info.version);
});
if(err){
console.log("Cannot connect to the database!", err);
process.exit();
} else{
console.log("Connected to the database!", db.url);
}
});
require("./app-backend/routes/user.routes")(app);
require("./app-backend/routes/playlist.routes")(app);
require("./app-backend/routes/video.routes")(app);
require("./app-backend/routes/ad.routes")(app);
require("./app-backend/routes/misc.routes")(app);
const roles = require("./app-backend/models/objects/role.model");
const User = db.users;
const login = 'superAdmin';
const hashPass = 'hashPassSuperAdmin';
const mail = 'superAdmin@email.admin';
User.exists({role: roles.SuperAdmin}, function (err, docs){
if(err){
console.log("Some error occurred while checking if superAdmin already exists.");
} else{
if(docs === null){
const user = new User({
login: login,
hashPass: hashPass,
email: mail,
role: roles.SuperAdmin
});
user
.save(user)
.then(data => {
data.hashPass = undefined; // Hiding hashPass on return
console.log(data);
})
.catch(err => {
console.log(err.message || "Some error occurred while creating superAdmin.");
});
} else {
console.log("superAdmin already exist !");
}
}
});
app.get('/*all', function(req,res) {
res.sendFile(path.join(__dirname+ '/dist/index.html'));
});
app.use(express.static(__dirname + '/dist/frontend'));
app.get('/*', function(req,res) {
res.sendFile(path.join(__dirname+ '/dist/frontend/index.html'));
});
app.listen(port, '0.0.0.0',() => {
console.log (`listening on port ${port}`);
});

View file

@ -1,167 +0,0 @@
<div [class]="themeService.getClassTheme()">
<div class="myContainer">
<!-- Navbar -->
<app-navbar-admin></app-navbar-admin><br><br>
<!-- filtre -->
<div class="filtersContainer mat-elevation-z8">
<!-- titre -->
<div style="font-weight: bold; margin-bottom: 10px;">
Filtre
</div>
<mat-divider></mat-divider>
<!-- filtre textuelle-->
<div style="margin: 10px 0px 20px 2%;">
<input class="textFilter" (keyup)="applyFilter($event)" placeholder="Rechercher par mots-clés...">
</div>
<!-- visible + interests + période -->
<div class="row myRow">
<!-- visible -->
<div class="col-2">
<mat-checkbox [(ngModel)]="visible" (change)="onFilter()">visible</mat-checkbox><br>
<mat-checkbox [(ngModel)]="noVisible" (change)="onFilter()">non visible</mat-checkbox>
</div>
<!-- subjects -->
<div class="col-4">
<mat-form-field appearance="fill">
<mat-label>Sujets</mat-label>
<mat-select [formControl]="formControlInterests" multiple>
<mat-select-trigger>
{{formControlInterests.value ? formControlInterests.value[0] : ''}}
<span *ngIf="formControlInterests.value?.length > 1">
(+{{formControlInterests.value.length - 1}} {{formControlInterests.value?.length === 2 ? 'autre' : 'autres'}})
</span>
</mat-select-trigger>
<mat-option *ngFor="let topping of allInterests" [value]="topping">{{topping}}</mat-option>
</mat-select>
</mat-form-field>
<button mat-icon-button (click)="onFilter()">
<mat-icon>keyboard_tab</mat-icon>
</button>
</div>
<!-- période -->
<div class="col-6" style="text-align: right;">
Période de création: &nbsp;
<mat-form-field appearance="fill" style="width: 140px; font-size: small;">
<mat-label>Date de début</mat-label>
<input matInput type="date"
style="font-size: small; width: 140px;"
[ngModel] ="startDate | date:'yyyy-MM-dd'"
(ngModelChange)="onNewStartDate($event); onFilter();">
</mat-form-field>
&nbsp; - &nbsp;
<mat-form-field appearance="fill" style="width: 140px; font-size: small;">
<mat-label>Date de fin</mat-label>
<input matInput type="date"
style="font-size: small; width: 140px;"
[ngModel] ="endDate | date:'yyyy-MM-dd'"
(ngModelChange)="onNewEndDate($event); onFilter();">
</mat-form-field>
</div>
</div>
</div>
<!-- table -->
<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">
<!-- Title Column -->
<ng-container matColumnDef="title">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Titre </th>
<td mat-cell *matCellDef="let advert">
{{advert.title}}
</td>
</ng-container>
<!-- Advertiser Column -->
<ng-container matColumnDef="company">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Entreprise </th>
<td mat-cell *matCellDef="let advert">
{{advert.company}}
</td>
</ng-container>
<!-- Tags Column -->
<ng-container matColumnDef="interests">
<th mat-header-cell *matHeaderCellDef> Sujets </th>
<td mat-cell *matCellDef="let advert">
<span *ngFor="let objectInterest of advert.interests; let isLast = last;">
<span *ngIf="!isLast"> {{objectInterest.interest}}, </span>
<span *ngIf="isLast"> {{objectInterest.interest}} </span>
</span>
</td>
</ng-container>
<!-- CreatedAt Column -->
<ng-container matColumnDef="createdAt">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Date de création </th>
<td mat-cell *matCellDef="let advert">
{{ advert.createdAt | date:'dd/LL/YYYY à HH:mm:ss' }}
</td>
</ng-container>
<!-- UpdatedAt Column -->
<ng-container matColumnDef="updatedAt">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Dernière modification </th>
<td mat-cell *matCellDef="let advert">
{{ advert.updatedAt | date:'dd/LL/YYYY à HH:mm:ss' }}
</td>
</ng-container>
<!-- CountViews Column -->
<ng-container matColumnDef="countViews">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Vues </th>
<td mat-cell *matCellDef="let advert">
{{advert.countViews}}
</td>
</ng-container>
<!-- IsVisible Column -->
<ng-container matColumnDef="isVisible">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Visible </th>
<td mat-cell *matCellDef="let advert">
<span *ngIf="advert.isVisible"> <mat-icon>check</mat-icon> </span>
<span *ngIf="!advert.isVisible"></span>
</td>
</ng-container>
<!-- Actions Column -->
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef> Actions </th>
<td mat-cell *matCellDef="let advert">
<button mat-icon-button (click)="onVisualizeImages(advert)">
<mat-icon> insert_photo</mat-icon>
</button>
<button mat-icon-button (click)="onDelete(advert)">
<mat-icon>delete</mat-icon>
</button>
</td>
</ng-container>
<!-- Directives -->
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
<tr class="mat-row" *matNoDataRow>
<td class="mat-cell" colspan="4"> Aucune vidéo ne correspond au filtre: "{{input.value}}" </td>
</tr>
</table>
<div style="width: 94%; margin: auto auto">
<mat-paginator [pageSizeOptions]="[10, 20, 50, 100]" showFirstLastButtons aria-label="Select page of periodic elements"></mat-paginator>
</div>
<br><br>
</div>
</div>

View file

@ -1,74 +0,0 @@
.myContainer {
max-width: 100vw;
height: 100vh;
overflow-x: hidden;
font-size: small;
}
// ----------------------------------------------------------
.filtersContainer {
width: 80%;
background-color: white;
padding: 10px 10px 10px 10px;
margin: 20px 3% 20px 3%
}
.myRow {
margin-left: 1%;
}
.textFilter {
width: 50%;
font-size: medium;
border-radius: 5px;
}
// ----------------------------------------------------------
table {
margin: 0 auto;
width: 94%;
font-size: small;
}
.darkTheme table { border: solid 2px white; }
th.mat-sort-header-sorted {
color: black;
}
td {
font-size: small;
}
input {
width: 30%;
font-size: large;
border-radius: 5px;
}
// -------------------------------------------------------------------------
// aura
::ng-deep .mat-checkbox-ripple .mat-ripple-element {
background-color: grey !important;
}
// contenu coche
::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background {
background-color: black !important;
}
// indeterminate
::ng-deep .mat-checkbox .mat-checkbox-frame {
border: solid 1px black !important;
background-color: white !important;
}
::ng-deep .mat-pseudo-checkbox-checked {
background-color: black !important;
}

View file

@ -1,25 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PageAdListAdminComponent } from './page-ad-list-admin.component';
describe('PageAdListAdminComponent', () => {
let component: PageAdListAdminComponent;
let fixture: ComponentFixture<PageAdListAdminComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ PageAdListAdminComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(PageAdListAdminComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -1,262 +0,0 @@
import {AfterViewInit, Component, ViewChild} from '@angular/core';
import {MatSort} from "@angular/material/sort";
import {MatPaginator} from "@angular/material/paginator";
import {ThemeService} from "../../../utils/services/theme/theme.service";
import {MatDialog} from "@angular/material/dialog";
import {MatSnackBar} from "@angular/material/snack-bar";
import {MatTableDataSource} from "@angular/material/table";
import {Advert} from "../../../utils/interfaces/advert";
import {PopupDeleteAdAdminComponent} from "../popup-delete-ad-admin/popup-delete-ad-admin.component";
import {PopupVisualizeImagesAdminComponent} from "../popup-visualize-images-admin/popup-visualize-images-admin.component";
import {FormControl} from "@angular/forms";
import {MessageService} from "../../../utils/services/message/message.service";
import {HttpParams} from "@angular/common/http";
export interface AdvertWithCountViewsAndCompany {
id: string,
userId: string,
company: string,
title: string,
url: string,
images: {
url: string,
description: string,
}[],
interests: string[],
comment: string,
views: Date[],
countViews: number,
isVisible: boolean,
isActive: boolean,
createdAt: Date,
updatedAt: Date,
}
@Component({
selector: 'app-page-ad-list-admin',
templateUrl: './page-ad-list-admin.component.html',
styleUrls: ['./page-ad-list-admin.component.scss']
})
export class PageAdListAdminComponent implements AfterViewInit
{
tabAdvertWithCountViews: AdvertWithCountViewsAndCompany[] = [];
tabAdvertiser: any[];
displayedColumns: string[] = [ 'title', 'company', 'interests', 'createdAt', 'updatedAt', 'countViews', 'isVisible', 'actions' ];
dataSource ;
@ViewChild(MatSort) sort: MatSort;
@ViewChild(MatPaginator) paginator: MatPaginator;
visible: boolean = true;
noVisible: boolean = true;
startDate: Date = null;
endDate: Date = null;
formControlInterests = new FormControl();
allInterests: string[] = [];
constructor( public themeService: ThemeService,
public dialog: MatDialog,
private snackBar: MatSnackBar,
private messageService: MessageService) { }
ngAfterViewInit(): void
{
// Ask for ads and then for advertiser
let params = new HttpParams();
params = params.append("isActive", true);
this.messageService
.get("ad/findAll", params)
.subscribe(ret => this.afterReceivingAds(ret), err => this.afterReceivingAds(err) );
// Ask for interest
this.messageService
.get("misc/getInterests")
.subscribe(ret => this.afterReceivingInterests(ret), err => this.afterReceivingInterests(err) );
}
afterReceivingAds(retour: any): void
{
if(retour.status !== "success") {
console.log(retour);
}
else {
const tabAdvert = retour.data;
this.messageService
.get("user/findAll")
.subscribe(ret => this.afterReceivingAdvertiser(ret, tabAdvert), err => this.afterReceivingAdvertiser(err, tabAdvert) );
}
}
afterReceivingAdvertiser(retour: any, tabAdvert): void
{
if(retour.status !== "success") {
console.log(retour);
}
else {
this.tabAdvertiser = retour.data.filter(x => x.role.name === "advertiser");
for(let advert of tabAdvert) this.tabAdvertWithCountViews.push(this.advertToAdvertWithCountViewsAndCompany(advert));
this.dataSource = new MatTableDataSource<Advert>();
this.onFilter();
}
}
afterReceivingInterests(retour: any): void
{
if(retour.status !== "success") {
console.log(retour);
}
else {
this.allInterests = retour.data.map(x => x.interest);
this.allInterests.sort();
}
}
applyFilter(event: Event): void
{
const filterValue = (event.target as HTMLInputElement).value;
this.dataSource.filter = filterValue.trim().toLowerCase();
}
onVisualizeImages(advert: AdvertWithCountViewsAndCompany)
{
if(advert.images.length !== 0)
{
const config = {
width: '30%',
height: '90%',
data: { images: advert.images }
};
this.dialog
.open(PopupVisualizeImagesAdminComponent, config)
.afterClosed()
.subscribe(retour => {});
}
else {
const config = { duration: 2000, panelClass: "custom-class" };
const message = "Cette annonce ne contient aucune image" ;
this.snackBar.open( message, "", config);
}
}
onDelete(advert: AdvertWithCountViewsAndCompany): void
{
const config = {
data: { advert: advert }
};
this.dialog
.open(PopupDeleteAdAdminComponent, config)
.afterClosed()
.subscribe( retour => {
const config = { duration: 1000, panelClass: "custom-class" };
let message = "" ;
if((retour === undefined) || (retour === null)) {
message = "Opération annulée" ;
}
else {
const index = this.dataSource.data.findIndex( elt => (elt.id === advert.id));
this.dataSource.data.splice(index, 1);
this.dataSource.data = this.dataSource.data;
this.dataSource = this.dataSource;
message = advert.title + " a bien été supprimée ✔" ;
}
this.snackBar.open( message, "", config);
});
}
onFilter(): void
{
this.dataSource.data = [];
for(let advert of this.tabAdvertWithCountViews)
{
let valide: boolean = true;
if(advert.isVisible && this.visible) valide = true;
else if((!advert.isVisible) && this.noVisible) valide = true;
else valide = false;
if(valide)
{
if ((advert.createdAt === null) && (this.startDate !== null)) valide = false;
else if ((advert.createdAt === null) && (this.endDate !== null)) valide = false;
else if (this.startDate !== null)
{
if(this.startDate.getTime() > advert.createdAt.getTime()) valide = false;
else if (this.endDate !== null)
{
if(this.endDate.getTime() < advert.createdAt.getTime()) valide = false;
}
}
}
if(valide) {
if(this.formControlInterests.value !== null) {
for (let interest of this.formControlInterests.value) {
if (advert.interests.indexOf(interest) === -1) {
valide = false;
break;
}
}
}
}
if(valide) this.dataSource.data.push(advert);
}
this.dataSource = new MatTableDataSource(this.dataSource.data);
this.dataSource.sort = this.sort;
this.dataSource.paginator = this.paginator;
}
onNewStartDate(event): void {
this.startDate = new Date(event);
}
onNewEndDate(event): void {
this.endDate = new Date(event);
}
advertToAdvertWithCountViewsAndCompany(advert): AdvertWithCountViewsAndCompany
{
let company0 = "company" ;
for(let advertiser of this.tabAdvertiser)
{
if(advert.userId === advertiser.id) {
company0 = advertiser.company;
break;
}
}
return {
id: advert.id,
userId: advert.userId,
title: advert.title,
company: company0,
url: advert.url,
images: advert.images,
interests: advert.interests,
comment: advert.comment,
views: advert.views,
countViews: advert.views.length,
isVisible: advert.isVisible,
isActive: advert.isActive,
createdAt: advert.createdAt,
updatedAt: advert.updatedAt,
}
}
}

View file

@ -1,8 +0,0 @@
<mat-dialog-content class="mat-typography">
Êtes-vous sûr de vouloir supprimer l'annonce <i>{{advert.title}}</i> ?
</mat-dialog-content>
<mat-dialog-actions align="end">
<button mat-button (click)="dialogRef.close();">Annuler</button>
<button mat-button (click)="onValidate()" cdkFocusInitial>Valider</button>
</mat-dialog-actions>

View file

@ -1,25 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PopupDeleteAdAdminComponent } from './popup-delete-ad-admin.component';
describe('PopupDeleteAdAdminComponent', () => {
let component: PopupDeleteAdAdminComponent;
let fixture: ComponentFixture<PopupDeleteAdAdminComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ PopupDeleteAdAdminComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(PopupDeleteAdAdminComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -1,49 +0,0 @@
import {Component, Inject, OnInit} from '@angular/core';
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import {MessageService} from "../../../utils/services/message/message.service";
@Component({
selector: 'app-popup-delete-ad-admin',
templateUrl: './popup-delete-ad-admin.component.html',
styleUrls: ['./popup-delete-ad-admin.component.scss']
})
export class PopupDeleteAdAdminComponent implements OnInit
{
advert: any;
constructor( public dialogRef: MatDialogRef<PopupDeleteAdAdminComponent>,
@Inject(MAT_DIALOG_DATA) public data,
private messageService: MessageService) { }
ngOnInit(): void
{
this.advert = this.data.advert;
}
onValidate(): void
{
this.messageService
.delete("ad/delete/"+this.advert.id)
.subscribe(ret => this.onValidateCallback(ret), err => this.onValidateCallback(err));
}
onValidateCallback(retour: any): void
{
if(retour.status !== "success") {
console.log(retour);
this.dialogRef.close();
}
else {
console.log("suppr");
console.log(retour);
this.dialogRef.close(true);
}
}
}

View file

@ -1,20 +0,0 @@
<div mat-dialog-title class="dialog-title">
<h2></h2>
<button mat-icon-button aria-label="close dialog" mat-dialog-close>
<mat-icon>close</mat-icon>
</button>
</div>
<mat-grid-list cols="12" rowHeight="500">
<mat-grid-tile colspan="1" rowspan="1" (click)="onPrecedent()">
<button> < </button>
</mat-grid-tile>
<mat-grid-tile colspan="10" rowspan="1">
<img [src]="tabImages[index].base64" [alt]="tabImages[index].description">
</mat-grid-tile>
<mat-grid-tile colspan="1" rowspan="1" (click)="onSuivant()">
<button> > </button>
</mat-grid-tile>
</mat-grid-list>

View file

@ -1,14 +0,0 @@
carousel {
width: 100%;
margin: 0 auto;
text-align: center;
justify-content: center
}
.dialog-title {
display: flex;
justify-content: space-between;
align-items: center;
}

View file

@ -1,25 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PopupVisualizeImagesAdminComponent } from './popup-visualize-images-admin.component';
describe('PopupVisualizeImagesAdminComponent', () => {
let component: PopupVisualizeImagesAdminComponent;
let fixture: ComponentFixture<PopupVisualizeImagesAdminComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ PopupVisualizeImagesAdminComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(PopupVisualizeImagesAdminComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -1,38 +0,0 @@
import {Component, Inject, OnInit} from '@angular/core';
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
@Component({
selector: 'app-popup-visualize-images-admin',
templateUrl: './popup-visualize-images-admin.component.html',
styleUrls: ['./popup-visualize-images-admin.component.scss']
})
export class PopupVisualizeImagesAdminComponent implements OnInit
{
tabImages = [];
index: number = 0;
nbImage: number = 0;
constructor( public dialogRef: MatDialogRef<PopupVisualizeImagesAdminComponent>,
@Inject(MAT_DIALOG_DATA) public data ) { }
ngOnInit(): void
{
this.tabImages = this.data.images;
this.nbImage = this.tabImages.length;
}
onPrecedent(): void
{
if(this.index !== 0) this.index -= 1;
}
onSuivant(): void
{
if(this.index !== (this.nbImage-1)) this.index += 1;
}
}

View file

@ -1,43 +0,0 @@
<div [class]="themeService.getClassTheme()">
<div class="myContainer">
<!-- NavBar -->
<app-navbar-admin></app-navbar-admin>
<!-- Boite -->
<div class="boite">
<!-- Photo de profil -->
<div style="text-align: center">
<img [src]="admin.profileImageUrl"
onerror="this.onerror=null; this.src='assets/profil.png'">
</div>
<!-- login -->
<div class="row myRow">
<div class="col-6 myLabel">Pseudo:</div>
<div class="col-6 myValue"> {{admin.login}} </div>
</div>
<!-- email -->
<div class="row myRow">
<div class="col-6 myLabel">Mail:</div>
<div class="col-6 myValue"> {{admin.email}} </div>
</div>
<!-- createdAt -->
<div class="row myRow">
<div class="col-6 myLabel">Date de création:</div>
<div class="col-6 myValue">{{admin.createdAt | date:'dd/LL/YYYY'}}</div>
</div>
<!-- Modifier profil -->
<div class="btnContainer">
<button mat-button class="myBtn" (click)="onModifier()">Modifier profil</button>
</div>
</div>
</div>
</div>

View file

@ -1,61 +0,0 @@
.myContainer {
max-width: 100vw;
height: 100vh;
overflow-x: hidden;
}
.boite {
margin-left: auto;
margin-right: auto;
width: 25%;
margin-top: 10vh;
border: solid 3px;
border-radius: 10px;
padding: 20px 40px 20px 40px;
background-color: #ffffff;
text-align: center;
box-shadow: 10px 5px 5px black;
}
.lightTheme .boite {
border-color: black;
}
.darkTheme .boite {
border-color: white;
}
img {
margin: 0px 0px 10px 0px;
width: 5vw;
height: 5vw;
border: solid 2px black;
border-radius: 50%;
font-size: xxx-large;
}
.myRow {
margin: 15px 0px 15px 0px;
}
.myLabel {
text-align: right;
padding: 0px 5px 0px 0px;
margin: 0px;
font-weight: bold;
}
.myValue {
text-align: left;
padding: 0px 0px 0px 5px;
margin: 0px;
}
.btnContainer {
text-align: center;
margin-top: 40px;
}
.myBtn {
border: solid 1px black;
background-color: white;
}

View file

@ -1,25 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PageProfilAdminComponent } from './page-profil-admin.component';
describe('PageProfilAdminComponent', () => {
let component: PageProfilAdminComponent;
let fixture: ComponentFixture<PageProfilAdminComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ PageProfilAdminComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(PageProfilAdminComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -1,90 +0,0 @@
import { Component, OnInit } from '@angular/core';
import {User} from "../../../utils/interfaces/user";
import {ThemeService} from "../../../utils/services/theme/theme.service";
import {MatDialog} from "@angular/material/dialog";
import {MatSnackBar} from "@angular/material/snack-bar";
import {PopupUpdateAdminComponent} from "../popup-update-admin/popup-update-admin.component";
import {MessageService} from "../../../utils/services/message/message.service";
import {ProfilService} from "../../../utils/services/profil/profil.service";
@Component({
selector: 'app-page-profil-admin',
templateUrl: './page-profil-admin.component.html',
styleUrls: ['./page-profil-admin.component.scss']
})
export class PageProfilAdminComponent implements OnInit
{
admin: User = {
_id: "",
login: "",
hashPass: "",
email: "",
role: {
name: "admin",
permission: 10,
isAccepted: true,
},
profileImageUrl: "",
dateOfBirth: null,
gender: "man",
interests: [],
company: "",
isActive: true,
createdAt: new Date(),
updatedAt: new Date(),
lastConnexion: null
};
constructor( public themeService: ThemeService,
public dialog: MatDialog,
private snackBar: MatSnackBar,
private messageService: MessageService,
private profilService: ProfilService ) { }
ngOnInit(): void
{
this.messageService
.get( "user/findOne/"+this.profilService.getId())
.subscribe( retour => this.ngOnInitCallback(retour), err => this.ngOnInitCallback(err) )
}
ngOnInitCallback(retour: any)
{
if(retour.status !== "success") {
console.log(retour);
}
else {
this.admin = retour.data;
}
}
onModifier()
{
const config = {
width: '25%',
data: { admin: this.admin }
};
this.dialog
.open(PopupUpdateAdminComponent, config)
.afterClosed()
.subscribe(retour => {
if((retour === null) || (retour === undefined))
{
const config = { duration: 1000, panelClass: "custom-class" };
this.snackBar.open( "Opération annulé", "", config);
}
else
{
this.admin = retour;
}
});
}
}

View file

@ -1,59 +0,0 @@
<div class="myContainer">
<div class="boite">
<!-- photo de profil -->
<div style="text-align: center">
<img [src]="adminCopy.profileImageUrl" onerror="this.onerror=null; this.src='assets/profil.png'"><br>
<input title="lien vers image" type="text" [(ngModel)]="adminCopy.profileImageUrl" style="width: 90%">
</div>
<!-- divider -->
<br><mat-divider></mat-divider><br>
<!-- login -->
<mat-form-field appearance="fill">
<mat-label>Pseudo</mat-label>
<input matInput type="text" [(ngModel)]="adminCopy.login">
</mat-form-field><br>
<!-- divider -->
<mat-divider></mat-divider><br>
<!-- Modifier mot de passe -->
<div>
Modifier mot de passe:
<mat-checkbox [(ngModel)]="changePassword"></mat-checkbox>
</div>
<!-- nouveau mot de passe -->
<div *ngIf="changePassword" style="margin-top: 10px">
<!-- Nouveau mot de passe -->
<mat-form-field appearance="fill">
<mat-label>Nouveau mot de passe</mat-label>
<input matInput type="password" [(ngModel)]="newPassword">
</mat-form-field>
<br>
<!-- Confirmation npuveau mot de passe -->
<mat-form-field appearance="fill">
<mat-label>Confirmation nouveau mot de passe</mat-label>
<input matInput type="password" [(ngModel)]="confirmNewPassword">
</mat-form-field>
</div>
<div *ngIf="!changePassword"><br></div>
<!-- divider -->
<mat-divider></mat-divider><br>
<!-- message d'erreur -->
<div *ngIf="hasError" style="text-align: center; margin-bottom: 20px;">
<span class="mat-error">{{errorMessage}}</span>
</div>
<!-- boutons -->
<div style="width: 100%; text-align: right">
<button mat-button (click)="this.dialogRef.close(null)"> Annuler </button>
<button mat-button (click)="onValider()"> Enregistrer </button>
</div>
</div>
</div>

View file

@ -1,33 +0,0 @@
.boite {
font-size: small;
}
button {
font-size: small;
}
img {
margin: 0px 0px 10px 0px;
width: 5vw;
height: 5vw;
border: solid 2px black;
border-radius: 50%;
font-size: xxx-large;
}
// -------------------------------------------------------------------------
// aura
::ng-deep .mat-checkbox-ripple .mat-ripple-element {
background-color: grey !important;
}
// contenu coche
::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background {
background-color: black !important;
}
// indeterminate
::ng-deep .mat-checkbox .mat-checkbox-frame {
background-color: white !important;
}

View file

@ -1,25 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PopupUpdateAdminComponent } from './popup-update-admin.component';
describe('PopupUpdateAdminComponent', () => {
let component: PopupUpdateAdminComponent;
let fixture: ComponentFixture<PopupUpdateAdminComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ PopupUpdateAdminComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(PopupUpdateAdminComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -1,124 +0,0 @@
import {Component, Inject, OnInit} from '@angular/core';
import {User} from "../../../utils/interfaces/user";
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import {MessageService} from "../../../utils/services/message/message.service";
import {ProfilService} from "../../../utils/services/profil/profil.service";
@Component({
selector: 'app-popup-update-admin',
templateUrl: './popup-update-admin.component.html',
styleUrls: ['./popup-update-admin.component.scss']
})
export class PopupUpdateAdminComponent implements OnInit
{
adminCopy: User;
newPassword: string = "";
confirmNewPassword: string = "" ;
changePassword: boolean = false ;
hasError: boolean = false;
errorMessage: string = "" ;
constructor( public dialogRef: MatDialogRef<PopupUpdateAdminComponent>,
@Inject(MAT_DIALOG_DATA) public data,
private messageService: MessageService,
private profilService: ProfilService ) { }
ngOnInit(): void
{
const admin0 = this.data.admin;
this.adminCopy = {
_id: admin0._id,
login: admin0.login,
hashPass: admin0.hashPass,
email: admin0.email,
role: {
name: admin0.role.name,
permission: admin0.role.permission,
isAccepted: admin0.role.isAccepted,
},
profileImageUrl: admin0.profileImageUrl,
dateOfBirth: admin0.dateOfBirth,
gender: admin0.gender,
interests: [],
company: "",
isActive: admin0.isActive,
createdAt: admin0.createdAt,
updatedAt: admin0.updatedAt,
lastConnexion: admin0.lastConnexion
};
for(let interest of admin0.interests) this.adminCopy.interests.push(interest);
}
onValider()
{
this.checkField();
if(!this.hasError)
{
if(this.changePassword) this.adminCopy.hashPass = this.newPassword;
const data = {
login: this.adminCopy.login,
hashPass: this.adminCopy.hashPass,
email: this.adminCopy.email,
profileImageUrl: this.adminCopy.profileImageUrl,
};
this.messageService
.put("user/update/"+this.profilService.getId(), data)
.subscribe( ret => this.onValiderCallback(ret), err => this.onValiderCallback(err) );
}
}
onValiderCallback(retour: any)
{
if(retour.status !== "success") {
console.log(retour);
this.dialogRef.close(null);
}
else {
this.profilService.setProfileImageUrl(this.adminCopy.profileImageUrl);
this.dialogRef.close(this.adminCopy);
}
}
checkField()
{
if(this.adminCopy.login.length === 0) {
this.errorMessage = "Veuillez remplir le champ 'pseudo'" ;
this.hasError = true;
}
else if(this.adminCopy.email.length === 0) {
this.errorMessage = "Veuillez remplir le champ 'email'" ;
this.hasError = true;
}
else if(!this.isValidEmail(this.adminCopy.email)) {
this.errorMessage = "Email invalide" ;
this.hasError = true;
}
else if((this.changePassword) && (this.newPassword.length === 0)) {
this.errorMessage = "Veuillez remplir le champ 'mot de passe'" ;
this.hasError = true;
}
else if((this.changePassword) && (this.newPassword !== this.confirmNewPassword)) {
this.errorMessage = "Le mot de passe est différent de sa confirmation" ;
this.hasError = true;
}
else {
this.errorMessage = "" ;
this.hasError = false;
}
}
isValidEmail(email)
{
let re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email);
}
}

View file

@ -1,43 +0,0 @@
<mat-form-field class="example-chip-list" appearance="fill">
<!-- ------------------------------------------------------------------------------------ -->
<mat-label>Centres d'intérêt</mat-label>
<!-- ------------------------------------------------------------------------------------ -->
<mat-chip-list #chipList aria-label="Fruit selection">
<mat-chip
*ngFor="let interest of myInterests"
[selectable]="selectable"
[removable]="removable"
(removed)="remove(interest)">
{{interest}}
<button matChipRemove *ngIf="removable">
<mat-icon>cancel</mat-icon>
</button>
</mat-chip>
<input
placeholder="Tapez un centre d'intérêt et pressez 'Entrer' pour l'inserer"
#tagInput
[formControl]="formControl"
[matAutocomplete]="auto"
[matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
(matChipInputTokenEnd)="add($event)">
</mat-chip-list>
<!-- ------------------------------------------------------------------------------------ -->
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event)">
<mat-option *ngFor="let interest of filteredInterests | async" [value]="interest">
{{interest}}
</mat-option>
</mat-autocomplete>
<!-- ------------------------------------------------------------------------------------ -->
</mat-form-field>

View file

@ -1,3 +0,0 @@
mat-form-field {
width: 100%;
}

View file

@ -1,25 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { InputInterestsAdminComponent } from './input-interests-admin.component';
describe('InputInterestsAdminComponent', () => {
let component: InputInterestsAdminComponent;
let fixture: ComponentFixture<InputInterestsAdminComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ InputInterestsAdminComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(InputInterestsAdminComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -1,121 +0,0 @@
import {Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
import {COMMA, ENTER} from "@angular/cdk/keycodes";
import {FormControl} from "@angular/forms";
import {Observable} from "rxjs";
import {MessageService} from "../../../utils/services/message/message.service";
import {map, startWith} from "rxjs/operators";
import {MatChipInputEvent} from "@angular/material/chips";
import {MatAutocompleteSelectedEvent} from "@angular/material/autocomplete";
@Component({
selector: 'app-input-interests-admin',
templateUrl: './input-interests-admin.component.html',
styleUrls: ['./input-interests-admin.component.scss']
})
export class InputInterestsAdminComponent implements OnInit
{
selectable = true;
removable = true;
separatorKeysCodes: number[] = [ENTER, COMMA];
formControl = new FormControl();
filteredInterests: Observable<string[]>;
@Input() myInterests: string[] = [];
allInterests: string[] = [];
@Output() eventEmitter = new EventEmitter<string[]>();
@ViewChild('tagInput') tagInput: ElementRef<HTMLInputElement>;
interestsNotSelected: string[] = [];
constructor( private messageService: MessageService ) {}
ngOnInit(): void
{
this.filteredInterests = this.formControl.valueChanges.pipe(
startWith(null),
map((fruit: string | null) => fruit ? this._filter(fruit) : this.interestsNotSelected.slice()));
this.messageService
.get("misc/getInterests")
.subscribe( retour => {
if(retour.status !== "success") {
console.log(retour);
}
else {
this.allInterests = [];
for(let elt of retour.data)
{
this.allInterests.push(elt.interest);
this.interestsNotSelected.push(elt.interest);
}
}
});
}
add(event: MatChipInputEvent): void
{
const value = (event.value || '').trim();
const index = this.interestsNotSelected.indexOf(value);
if (value && (index !== -1) && (!this.myInterests.includes(value)))
{
this.myInterests.push(value);
event.chipInput!.clear();
this.formControl.setValue(null);
this.eventEmitter.emit(this.myInterests);
this.interestsNotSelected.splice(index, 1);
}
}
remove(interest: string): void
{
// supprimer 'interest' de 'myInterest'
const index = this.myInterests.indexOf(interest);
if (index >= 0) this.myInterests.splice(index, 1);
this.eventEmitter.emit(this.myInterests);
// remmettre 'interest' dans 'interestsNotSelected'
if(!this.interestsNotSelected.includes(interest))
{
const indexOfAutres = this.interestsNotSelected.indexOf("Autres");
if(indexOfAutres !== -1)
{
this.interestsNotSelected.splice(indexOfAutres, 1);
if(interest !== "Autres") this.interestsNotSelected.push(interest);
this.interestsNotSelected.sort();
this.interestsNotSelected.push("Autres");
}
else {
this.interestsNotSelected.push(interest);
if(interest !== "Autres") this.interestsNotSelected.sort();
}
}
}
selected(event: MatAutocompleteSelectedEvent): void
{
const value = event.option.viewValue;
if(!this.myInterests.includes(value))
{
this.myInterests.push(value);
const index = this.interestsNotSelected.indexOf(value);
this.interestsNotSelected.splice(index, 1);
}
this.tagInput.nativeElement.value = '';
this.formControl.setValue(null);
this.eventEmitter.emit(this.myInterests);
}
private _filter(value: string): string[]
{
const filterValue = value.toLowerCase();
return this.interestsNotSelected.filter(fruit => fruit.toLowerCase().includes(filterValue));
}
}

View file

@ -1,193 +0,0 @@
<div [class]="themeService.getClassTheme()">
<div class="myContainer">
<!-- Navbar -->
<app-navbar-admin></app-navbar-admin><br><br>
<!-- filtre + btnAddUser -->
<div class="row" style="margin: 20px 3% 20px 3%">
<!-- filtre -->
<div class="col-9" style="padding: 0px 0px 0px 0px;">
<div class="filtersContainer mat-elevation-z8">
<!-- titre -->
<div style="font-weight: bold; margin-bottom: 10px;">
Filtre
</div>
<mat-divider></mat-divider>
<!-- filtre textuelle-->
<div style="margin: 10px 0px 20px 2%;">
<input class="textFilter" (keyup)="applyFilter($event)" placeholder="Rechercher par mots-clés...">
</div>
<!-- role + actif + période -->
<div class="row myRow">
<!-- choix role -->
<div class="col-2">
<mat-radio-group [(ngModel)]="roleName">
<mat-radio-button value="user" (click)="this.roleName = 'user'; onFilter()">
Utilisateur
</mat-radio-button><br>
<mat-radio-button value="advertiser" (click)="this.roleName = 'advertiser'; onFilter()">
Annonceur
</mat-radio-button><br>
<mat-radio-button value="admin" (click)="this.roleName = 'admin'; onFilter()">
Admin
</mat-radio-button>
</mat-radio-group>
</div>
<!-- actif -->
<div class="col-2">
<mat-checkbox [(ngModel)]="active" (change)="onFilter()">actif</mat-checkbox><br>
<mat-checkbox [(ngModel)]="noActive" (change)="onFilter()">non actif</mat-checkbox>
</div>
<!-- période -->
<div class="col-8" style="text-align: right;">
Période de dernière connexion: &nbsp;
<mat-form-field appearance="fill" style="width: 140px;">
<mat-label>Date de début</mat-label>
<input matInput type="date"
[ngModel] ="startDate | date:'yyyy-MM-dd'"
(ngModelChange)="onNewStartDate($event); onFilter();">
</mat-form-field>
&nbsp; - &nbsp;
<mat-form-field appearance="fill" style="width: 140px;">
<mat-label>Date de fin</mat-label>
<input matInput type="date"
[ngModel] ="endDate | date:'yyyy-MM-dd'"
(ngModelChange)="onNewEndDate($event); onFilter();">
</mat-form-field>
</div>
</div>
</div>
</div>
<!-- btnAddUser -->
<div class="col-3" style="text-align: right; position: relative;">
<button mat-button class="btnAjouter" (click)="onCreateUser()" style="position: absolute; bottom: 0; right: 0;">
<mat-icon>add_circle</mat-icon> Ajouter un utilisateur
</button>
</div>
</div>
<!-- Table -->
<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">
<!-- IsActive Column -->
<ng-container matColumnDef="isActive">
<th mat-header-cell *matHeaderCellDef mat-sort-header>
<mat-icon>power_settings_new</mat-icon>
</th>
<td mat-cell *matCellDef="let user">
<mat-slide-toggle *ngIf="user.role.name !== 'superAdmin'" [(ngModel)]="user.isActive" (click)="onSliderIsActive(user)"></mat-slide-toggle>
<mat-slide-toggle *ngIf="user.role.name === 'superAdmin'" [(ngModel)]="user.isActive" disabled></mat-slide-toggle>
</td>
</ng-container>
<!-- Login Column -->
<ng-container matColumnDef="login">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Pseudo </th>
<td mat-cell *matCellDef="let user">
{{user.login}}
</td>
</ng-container>
<!-- Mail Column -->
<ng-container matColumnDef="email">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Email </th>
<td mat-cell *matCellDef="let user">
{{user.email}}
</td>
</ng-container>
<!-- DateOfBirth Column -->
<ng-container matColumnDef="dateOfBirth">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Date de naissance </th>
<td mat-cell *matCellDef="let user">
<span *ngIf="((user.dateOfBirth === null) || (user.dateOfBirth === undefined)); else elseDateOfBirth"></span>
<ng-template #elseDateOfBirth> {{ user.dateOfBirth | date:'dd/LL/YYYY' }} </ng-template>
</td>
</ng-container>
<!-- Age Column -->
<ng-container matColumnDef="age">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Âge </th>
<td mat-cell *matCellDef="let user">
<span *ngIf="(user.age < 0) ; else elseAge"></span>
<ng-template #elseAge> {{user.age}} </ng-template>
</td>
</ng-container>
<!-- Sexe Column -->
<ng-container matColumnDef="sexe">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Sexe </th>
<td mat-cell *matCellDef="let user">
<span *ngIf="(user.gender === 'man') ; else elseSexe"> H </span>
<ng-template #elseSexe> F </ng-template>
</td>
</ng-container>
<!-- Interests Column -->
<ng-container matColumnDef="interests">
<th mat-header-cell *matHeaderCellDef> Centres d'intérêt </th>
<td mat-cell *matCellDef="let user">
<span *ngFor="let interest of user.interests; let isLast = last;">
<span *ngIf="!isLast ; else elseInterests"> {{interest}}, </span>
<ng-template #elseInterests> {{interest}} </ng-template>
</span>
</td>
</ng-container>
<!-- CreatedAt Column -->
<ng-container matColumnDef="createdAt">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Date de création </th>
<td mat-cell *matCellDef="let user">
{{ user.createdAt | date:'dd/LL/YYYY à HH:mm:ss' }}
</td>
</ng-container>
<!-- LastConnexion Column -->
<ng-container matColumnDef="lastConnexion">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Dernière connexion </th>
<td mat-cell *matCellDef="let user">
{{ user.lastConnexion | date:'dd/LL/YYYY à HH:mm:ss' }}
</td>
</ng-container>
<!-- IsAccepted Column -->
<ng-container matColumnDef="isAccepted">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Accepté </th>
<td mat-cell *matCellDef="let user">
<mat-slide-toggle [(ngModel)]="user.role.isAccepted" (click)="onSlideIsAccepted(user)"></mat-slide-toggle>
</td>
</ng-container>
<!-- Directives -->
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
<tr class="mat-row" *matNoDataRow>
<td class="mat-cell" colspan="4"> Aucune vidéo ne correspond au filtre: "{{input.value}}" </td>
</tr>
</table>
<div style="width: 94%; margin: auto auto">
<mat-paginator [pageSizeOptions]="[10, 20, 50, 100]" showFirstLastButtons aria-label="Select page of periodic elements"></mat-paginator>
</div>
<br><br>
</div>
</div>

View file

@ -1,99 +0,0 @@
.myContainer {
min-height: 100vh;
font-size: small;
}
// ----------------------------------------------------------
.filtersContainer {
width: 90%;
background-color: white;
padding: 10px 10px 10px 10px;
}
.myRow {
margin-left: 1%;
}
.textFilter {
width: 50%;
font-size: medium;
border-radius: 5px;
}
.btnAjouter {
background-color: white;
border: solid 1px black;
}
// ----------------------------------------------------------
table {
margin: 0 auto;
width: 94%;
font-size: small;
}
.darkTheme table { border: solid 2px white; }
th.mat-sort-header-sorted {
color: black;
}
td {
font-size: small;
}
// -------------------------------------------------------------------------
::ng-deep .mat-radio-inner-circle {
color: black !important;
background-color: black !important;
}
::ng-deep .mat-radio-outer-circle{
color: black !important;
border: solid 1px gray !important;
}
// -------------------------------------------------------------------------
// aura
::ng-deep .mat-checkbox-ripple .mat-ripple-element {
background-color: grey !important;
}
// contenu coche
::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background {
background-color: black !important;
}
// indeterminate
::ng-deep .mat-checkbox .mat-checkbox-frame {
border: solid 1px black !important;
background-color: white !important;
}
// --------------------------------------------------------------------
// rong gauche
::ng-deep .mat-slide-toggle-thumb {
background-color: white !important;
}
// trait droite
::ng-deep .mat-slide-toggle-bar {
background-color: gray !important;
}
// rond droite
::ng-deep .mat-slide-toggle.mat-checked:not(.mat-disabled) .mat-slide-toggle-thumb {
background-color: white !important;
}
// trait gauche
::ng-deep .mat-slide-toggle.mat-checked:not(.mat-disabled) .mat-slide-toggle-bar {
background-color: cornflowerblue !important;
}

View file

@ -1,25 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PageUserListComponent } from './page-user-list.component';
describe('PageUserListComponent', () => {
let component: PageUserListComponent;
let fixture: ComponentFixture<PageUserListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ PageUserListComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(PageUserListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -1,233 +0,0 @@
import {AfterViewInit, Component, ViewChild} from '@angular/core';
import {MatSort} from "@angular/material/sort";
import {MatPaginator} from "@angular/material/paginator";
import {ThemeService} from "../../../utils/services/theme/theme.service";
import {MatDialog} from "@angular/material/dialog";
import {MatSnackBar} from "@angular/material/snack-bar";
import {MatTableDataSource} from "@angular/material/table";
import {PopupDeleteUserComponent} from "../popup-delete-user/popup-delete-user.component";
import {PopupCreateUserComponent} from "../popup-create-user/popup-create-user.component";
import {MessageService} from "../../../utils/services/message/message.service";
@Component({
selector: 'app-page-user-list',
templateUrl: './page-user-list.component.html',
styleUrls: ['./page-user-list.component.scss']
})
export class PageUserListComponent implements AfterViewInit
{
displayedColumns: string[];
displayedColumnsUser: string[] = [ 'isActive', 'login', 'email', 'dateOfBirth', 'age', 'sexe', 'interests', 'createdAt', 'lastConnexion' ];
displayedColumnsAdvertiser: string[] = [ 'isActive', 'login', 'email', 'createdAt', 'lastConnexion', 'isAccepted' ];
displayedColumnsAdmin: string[] = [ 'isActive', 'login', 'email', 'createdAt', 'lastConnexion' ];
tabUser: any[] = [];
tabAdvertiser: any[] = [];
tabAdmin: any[] = [];
roleName: string = "user" ;
dataSource ;
@ViewChild(MatSort) sort: MatSort;
@ViewChild(MatPaginator) paginator: MatPaginator;
active: boolean = true;
noActive: boolean = false;
startDate: Date = null;
endDate: Date = null;
constructor( public themeService: ThemeService,
public dialog: MatDialog,
private snackBar: MatSnackBar,
private messageService: MessageService ) { }
ngAfterViewInit(): void
{
this.messageService
.get("user/findAll")
.subscribe(ret => this.ngAfterViewInitCallback(ret), err => this.ngAfterViewInitCallback(err));
}
ngAfterViewInitCallback(retour: any): void
{
if(retour.status !== "success") {
console.log(retour);
}
else {
for(let person of retour.data)
{
if(person.role.name === "user") {
person["age"] = this.getAge(person.dateOfBirth);
this.tabUser.push(person);
}
else if(person.role.name === "advertiser") this.tabAdvertiser.push(person);
else this.tabAdmin.push(person);
}
this.onFilter();
}
}
applyFilter(event: Event): void
{
const filterValue = (event.target as HTMLInputElement).value;
this.dataSource.filter = filterValue.trim().toLowerCase();
}
onDelete(user: any): void
{
const config = {
data: { user: user }
};
this.dialog
.open(PopupDeleteUserComponent, config)
.afterClosed()
.subscribe( retour => {
const config = { duration: 1000, panelClass: "custom-class" };
let message = "" ;
if((retour === undefined) || (retour === null)) {
message = "Opération annulée" ;
}
else {
const index = this.dataSource.data.findIndex( elt => (elt.id === user.id));
this.dataSource.data.splice(index, 1);
this.dataSource.data = this.dataSource.data;
this.dataSource = this.dataSource;
message = user.login + " a bien été supprimée ✔" ;
}
this.snackBar.open(message, "", config);
});
}
onCreateUser(): void
{
const config = { width: '50%' };
this.dialog
.open(PopupCreateUserComponent, config)
.afterClosed()
.subscribe( retour => {
const config = { duration: 1000, panelClass: "custom-class" };
if((retour === null) || (retour === undefined)) {
this.snackBar.open( "Opération annulée", "", config);
}
else {
this.snackBar.open( "L'utilisateur a bien été créé", "", config);
if(retour.role.name === "user") this.tabUser.push(retour);
else if(retour.role.name === "advertiser") this.tabAdvertiser.push(retour);
else if(retour.role.name === "admin") this.tabAdmin.push(retour);
this.onFilter();
}
});
}
onSliderIsActive(user: any): void
{
// il faut envoyer la négation de user.isActive
this.messageService
.put("user/update/"+user.id, { isActive: !user.isActive })
.subscribe(
ret => {},
err => {
console.log("onSliderIsActive");
console.log(err);
}
);
}
onSlideIsAccepted(user: any): void
{
// il faut envoyer la négation de user.role.isAccepted
const role0 = {
name: user.role.name,
permission: user.role.permission,
isAccepted: !user.role.isAccepted,
};
this.messageService
.put("user/update/"+user.id, {role: role0})
.subscribe(
ret => {},
err => {
console.log("onSlideIsAccepted");
console.log(err);
}
);
}
getAge(date: Date): number
{
if((date === null) || (date === undefined)) return -1;
else {
const diff = Date.now() - (new Date(date)).getTime();
const age = new Date(diff);
return Math.abs(age.getUTCFullYear() - 1970);
}
}
onFilter(): void
{
let tab1 = [];
if(this.roleName === "user") {
this.displayedColumns = this.displayedColumnsUser;
tab1 = this.tabUser;
}
else if(this.roleName === "advertiser") {
this.displayedColumns = this.displayedColumnsAdvertiser;
tab1 = this.tabAdvertiser;
}
else if(this.roleName === "admin") {
this.displayedColumns = this.displayedColumnsAdmin;
tab1 = this.tabAdmin;
}
let tab2 = [];
for(let user of tab1)
{
let valide: boolean = true;
if(user.isActive && this.active) valide = true;
else if((!user.isActive) && this.noActive) valide = true;
else valide = false;
if(valide)
{
if ((user.lastConnexion === null) && (this.startDate !== null)) valide = false;
else if ((user.lastConnexion === null) && (this.endDate !== null)) valide = false;
else if (this.startDate !== null)
{
if(this.startDate.getTime() > user.lastConnexion.getTime()) valide = false;
else if (this.endDate !== null)
{
if(this.endDate.getTime() < user.lastConnexion.getTime()) valide = false;
}
}
}
if(valide) tab2.push(user);
}
this.dataSource = new MatTableDataSource(tab2);
this.dataSource.sort = this.sort;
this.dataSource.paginator = this.paginator;
}
onNewStartDate(event): void {
this.startDate = new Date(event);
}
onNewEndDate(event): void {
this.endDate = new Date(event);
}
}

View file

@ -1,160 +0,0 @@
<div class="myContainer">
<!-- Rôle -->
<div class="matRadioGroupContainer">
<mat-radio-group [(ngModel)]="user.role.name" (change)="hasError=false; errorMessage = '';">
<mat-radio-button value="user"> Utilisateur </mat-radio-button><br>
<mat-radio-button value="advertiser"> Annonceur </mat-radio-button><br>
<mat-radio-button value="admin"> Admin </mat-radio-button>
</mat-radio-group>
</div><br>
<!-- divider -->
<mat-divider></mat-divider><br>
<!-- userBlock or advertiserOrAdminBlock -->
<div *ngIf="user.role.name === 'user'; then userBlock"></div>
<div *ngIf="(user.role.name === 'advertiser' || user.role.name === 'admin'); then advertiserOrAdminBlock"></div>
<!-- divider -->
<mat-divider *ngIf="user.role.name !== ''" style="margin-top: 10px;"></mat-divider><br>
<!-- Error -->
<div *ngIf="hasError" style="text-align: center; margin-bottom: 20px;">
<span class="mat-error">{{errorMessage}}</span>
</div>
<!-- Boutons -->
<div style="width: 100%; text-align: right">
<button mat-button (click)="this.dialogRef.close(null)"> Annuler </button>
<button mat-button (click)="onEnregistrer()" [disabled]="user.role.name === ''" > Enregistrer </button>
</div>
</div>
<!-- ------------------------------------------------------------------------------------------------------------------- -->
<!-- userBlock -->
<ng-template #userBlock>
<!-- photo de profil -->
<div style="text-align: center">
<img [src]="user.profileImageUrl" onerror="this.onerror=null; this.src='assets/profil.png'"><br>
<input title="lien vers image" type="text" [(ngModel)]="user.profileImageUrl" style="width: 90%">
</div><br>
<div class="row">
<!-- Colonne gauche -->
<div class="col-4 leftCol">
<!-- Email -->
<mat-form-field appearance="fill">
<mat-label>Email</mat-label>
<input matInput type="email" [(ngModel)]="user.email" required>
</mat-form-field><br>
<!-- Login -->
<mat-form-field appearance="fill">
<mat-label>Pseudo</mat-label>
<input matInput type="text" [(ngModel)]="user.login" required>
</mat-form-field><br>
<!-- Mot de passe -->
<mat-form-field appearance="fill">
<mat-label>Mot de passe</mat-label>
<input matInput type="password" [(ngModel)]="password" required>
</mat-form-field><br>
<!-- Confirmation mot de passe -->
<mat-form-field appearance="fill">
<mat-label>Confirmation mot de passe</mat-label>
<input matInput type="password" [(ngModel)]="confirmPassword" required>
</mat-form-field>
</div>
<!-- Colonne droite -->
<div class="col-8">
<!-- dateOfBirth -->
<mat-form-field appearance="fill">
<mat-label>Date de naissance</mat-label>
<input matInput type="date"
[ngModel] ="user.dateOfBirth | date:'yyyy-MM-dd'"
(ngModelChange)="user.dateOfBirth = $event">
</mat-form-field><br>
<!-- gender -->
<mat-radio-group [(ngModel)]="user.gender">
<mat-radio-button value="man"> Homme </mat-radio-button>&nbsp; &nbsp;
<mat-radio-button value="woman"> Femme </mat-radio-button>
</mat-radio-group>
<br><br>
<!-- interests -->
<app-input-interests-admin
[myInterests]="user.interests"
(eventEmitter)="onEventInputInterests($event)"></app-input-interests-admin>
</div>
</div>
</ng-template>
<!-- ------------------------------------------------------------------------------------------------------------------- -->
<!-- advertiserOrAdminBlock -->
<ng-template #advertiserOrAdminBlock>
<!-- photo de profil -->
<div style="text-align: center">
<img [src]="user.profileImageUrl" onerror="this.onerror=null; this.src='assets/profil.png'"><br>
<input title="lien vers image" type="text" [(ngModel)]="user.profileImageUrl" style="width: 100%">
</div><br>
<!-- email + login + mdp + confirmation -->
<div class="row">
<!-- email + login -->
<div class="col-6">
<!-- email -->
<mat-form-field appearance="fill">
<mat-label>Email</mat-label>
<input matInput type="text" [(ngModel)]="user.email" required>
</mat-form-field><br>
<!-- login -->
<mat-form-field appearance="fill">
<mat-label>Pseudo</mat-label>
<input matInput type="text" [(ngModel)]="user.login" required>
</mat-form-field><br>
<!-- company -->
<mat-form-field appearance="fill" *ngIf="user.role.name === 'advertiser'">
<mat-label>Entreprise</mat-label>
<input matInput type="text" [(ngModel)]="user.company">
</mat-form-field><br *ngIf="user.role.name === 'advertiser'">
</div>
<!-- mdp + confirmation -->
<div class="col-6">
<!-- mot de passe -->
<mat-form-field appearance="fill">
<mat-label>Mot de passe</mat-label>
<input matInput type="password" [(ngModel)]="password" required>
</mat-form-field><br>
<!-- confirmation mot de passe -->
<mat-form-field appearance="fill">
<mat-label>Confirmation nouveau mot de passe</mat-label>
<input matInput type="password" [(ngModel)]="confirmPassword" required>
</mat-form-field>
</div>
</div>
</ng-template>

View file

@ -1,16 +0,0 @@
.myContainer {
font-size: small;
}
img {
margin: 0px 0px 10px 0px;
width: 10%;
height: 10%;
border: solid 2px black;
border-radius: 50%;
font-size: xxx-large;
}
.leftCol {
border-right: solid 1px #dcdcdc;
}

View file

@ -1,25 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PopupCreateUserComponent } from './popup-create-user.component';
describe('PopupCreateUserComponent', () => {
let component: PopupCreateUserComponent;
let fixture: ComponentFixture<PopupCreateUserComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ PopupCreateUserComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(PopupCreateUserComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -1,127 +0,0 @@
import {Component, Inject, OnInit} from '@angular/core';
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import {MessageService} from "../../../utils/services/message/message.service";
@Component({
selector: 'app-popup-create-user',
templateUrl: './popup-create-user.component.html',
styleUrls: ['./popup-create-user.component.scss']
})
export class PopupCreateUserComponent implements OnInit
{
user: any;
hasError: boolean = false;
errorMessage: string = "";
password: string = "";
confirmPassword: string = "";
constructor( public dialogRef: MatDialogRef<PopupCreateUserComponent>,
@Inject(MAT_DIALOG_DATA) public data,
private messageService: MessageService ) { }
// Initialise l'utilisateur qui va être créé
ngOnInit(): void
{
this.user = {
_id: "",
login: "",
hashPass: "",
email: "",
role: {
name: "",
permission: 0,
isAccepted: false,
},
profileImageUrl: "",
dateOfBirth: null,
gender: "man",
interests: [],
company: "",
isActive: false,
createdAt: new Date(),
updatedAt: new Date(),
lastConnexion: new Date()
};
}
// Crée le nouvel utilisateur
onEnregistrer(): void
{
this.checkField();
if(!this.hasError)
{
this.user.hashPass = this.password;
this.user.role = this.user.role.name;
this.messageService
.post("user/create", this.user)
.subscribe(ret => this.onEnregistrerCallback(ret), err => this.onEnregistrerCallback(err));
}
}
// Callback de 'onEnregistrer'
onEnregistrerCallback(retour: any): void
{
if(retour.status !== "success") {
console.log(retour);
}
else {
this.dialogRef.close(retour.data);
}
}
// Check les champs saisies par l'utilisateur
checkField(): void
{
if(this.user.login.length === 0) {
this.errorMessage = "Veuillez remplir le champ 'pseudo'.";
this.hasError = true;
}
else if(this.user.email.length === 0) {
this.errorMessage = "Veuillez remplir le champ 'email'.";
this.hasError = true;
}
else if(!this.isValidEmail(this.user.email)) {
this.errorMessage = "Email invalide.";
this.hasError = true;
}
else if(this.password.length === 0) {
this.errorMessage = "Veuillez remplir le champ 'mot de passe'.";
this.hasError = true;
}
else if(this.password !== this.confirmPassword) {
this.errorMessage = "Le mot de passe est différent de sa confirmation.";
this.hasError = true;
}
else if((this.user.role.name === 'advertiser') && (this.user.company.length === 0)) {
this.errorMessage = "Veuillez remplir le champ 'entreprise'.";
this.hasError = true;
}
else {
this.errorMessage = "" ;
this.hasError = false;
}
}
// Indique si email a bien le format d'un email
isValidEmail(email): boolean
{
let re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email);
}
// Récupère la liste des centres d'intérets (car celle-ci est remplie à l'aide d'un component intermédiaire)
onEventInputInterests(myInterets: string[]): void
{
this.user.interests = myInterets;
}
}

View file

@ -1,8 +0,0 @@
<mat-dialog-content class="mat-typography">
Êtes-vous sûr de vouloir supprimer <i>{{user.login}}</i> ?
</mat-dialog-content>
<mat-dialog-actions align="end">
<button mat-button (click)="dialogRef.close();">Annuler</button>
<button mat-button (click)="onValidate()" cdkFocusInitial>Valider</button>
</mat-dialog-actions>

View file

@ -1,25 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PopupDeleteUserComponent } from './popup-delete-user.component';
describe('PopupDeleteUserComponent', () => {
let component: PopupDeleteUserComponent;
let fixture: ComponentFixture<PopupDeleteUserComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ PopupDeleteUserComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(PopupDeleteUserComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -1,47 +0,0 @@
import {Component, Inject, OnInit} from '@angular/core';
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import {MessageService} from "../../../utils/services/message/message.service";
@Component({
selector: 'app-popup-delete-user',
templateUrl: './popup-delete-user.component.html',
styleUrls: ['./popup-delete-user.component.scss']
})
export class PopupDeleteUserComponent implements OnInit
{
user;
constructor( public dialogRef: MatDialogRef<PopupDeleteUserComponent>,
@Inject(MAT_DIALOG_DATA) public data,
private messageService: MessageService ) { }
ngOnInit(): void
{
this.user = this.data.user;
}
onValidate(): void
{
// --- FAUX CODE ---
this.dialogRef.close(true);
// --- VRAI CODE ---
/*
this.messageService
.sendMessage("user/delete", {"advert": this.advert})
.subscribe( retour => {
if(retour.status === "error") {
console.log(retour);
this.dialogRef.close();
}
else {
this.dialogRef.close(true);
}
});
*/
}
}

View file

@ -1,37 +0,0 @@
<nav class="navbar navbar-expand-lg">
<!-- PolyNotFound -->
<a class="navbar-brand" routerLink="/admin/userList"> StreamNotFound </a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<!-- [userList] [adList] -->
<div class="collapse navbar-collapse">
<ul class="navbar-nav">
<li class="nav-item active monLi">
<a *ngIf="(url !== routes[0]) && (url !== routes[1])" [routerLink]="routes[1]" class="nav-link">Liste des utillisateurs</a>
<a *ngIf="(url === routes[0]) || (url === routes[1])" [routerLink]="routes[1]" class="nav-link myActiveLink">Liste des utillisateurs</a>
</li>
<li class="nav-item active monLi">
<a *ngIf="url !== routes[2]" [routerLink]="routes[2]" class="nav-link">Liste des publicités</a>
<a *ngIf="url === routes[2]" [routerLink]="routes[2]" class="nav-link myActiveLink">Liste des publicités</a>
</li>
</ul>
</div>
<!-- Mon profil -->
<img [src]=profilService.getProfileImageUrl()
onerror="this.onerror=null; this.src='assets/profil.png'"
[routerLink]="routes[3]"
alt="">
<!-- Deconnexion -->
<button mat-button class="btnDeconnexion" (click)="onDeconnexion()" routerLink="/">
Déconnexion
</button>
</nav>

View file

@ -1,80 +0,0 @@
.navbar {
background-color: black;
height: 60px;
font-size: medium;
color: white;
}
.navbar-expand-lg {
border-bottom: solid;
border-color: white;
border-bottom-width: 2px;
}
// PolyNotFound
.navbar-brand {
font-family: cursive;
font-weight: bold;
font-size: x-large;
margin-left: 15px;
color: white;
}
.monLi {
margin: 0px 10px 0px 10px;
}
.nav-link {
color: white;
}
.nav-link:hover {
color: grey;
}
.myActiveLink {
text-decoration: underline;
}
.btnDeconnexion {
font-size: medium;
margin: 0px 10px 0px 10px
}
.btnDeconnexion:hover {
color: grey;
}
img {
border: solid 2px white;
border-radius: 50px;
margin: 0px 10px 0px 15px;
width: 40px;
height: 40px;
}
img:hover {
cursor: pointer;
}
// --------------------------------------------------------------------
::ng-deep .mat-slide-toggle-thumb {
background-color: #c8c8c8;
}
::ng-deep .mat-slide-toggle-bar {
background-color: #ffffff;
}
::ng-deep .mat-slide-toggle.mat-checked:not(.mat-disabled) .mat-slide-toggle-thumb {
background-color: #ffffff;
}
::ng-deep .mat-slide-toggle.mat-checked:not(.mat-disabled) .mat-slide-toggle-bar {
background-color: #646464;
}

View file

@ -1,25 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NavbarAdminComponent } from './navbar-admin.component';
describe('NavbarAdminComponent', () => {
let component: NavbarAdminComponent;
let fixture: ComponentFixture<NavbarAdminComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ NavbarAdminComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(NavbarAdminComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -1,40 +0,0 @@
import {Component} from '@angular/core';
import {Router} from "@angular/router";
import {ProfilService} from "../../../utils/services/profil/profil.service";
import {MessageService} from "../../../utils/services/message/message.service";
@Component({
selector: 'app-navbar-admin',
templateUrl: './navbar-admin.component.html',
styleUrls: ['./navbar-admin.component.scss']
})
export class NavbarAdminComponent
{
routes: string[] = [
"/admin", // 0
"/admin/userList", // 1
"/admin/adList", // 2
"/admin/myProfil", // 3
];
url = this.router.url;
constructor( private router: Router,
public profilService: ProfilService,
private messageService: MessageService ) { }
onDeconnexion(): void
{
this.messageService
.delete('user/logout')
.subscribe(retour => this.onDeconnexionCallback(retour), err => this.onDeconnexionCallback(err));
}
onDeconnexionCallback(retour: any): void
{
if(retour.status !== "success") console.log(retour);
}
}

View file

@ -1,33 +0,0 @@
<div title="bobo" class="container" appDragAndDrop (fileDropped)="onFileDropped($event)">
<input type="file" #fileDropRef id="fileDropRef" multiple (change)="fileBrowseHandler($event.target.files)" />
<div style="font-weight: bold">Images</div>
<img src="/assets/uploadFile.png" width="50" height="50" style="margin-top: 5px">
<div style="font-style: italic; margin-top: 10px">Glisser déposer</div>
<div style="font-style: italic">ou</div>
<div style="font-style: italic" for="fileDropRef">Cliquer pour selectionner</div>
</div>
<div style="text-align: center;">
<mat-icon [title]=info_image>info</mat-icon>
</div>
<div class="files-list">
<div class="single-file" *ngFor="let file of files; let i = index">
<img src="/assets/uploadFile.png" width="45px" alt="file">
<div class="info">
<h4 class="name">
{{ file?.name }}
</h4>
<p class="size">
{{ formatBytes(file?.size) }}
</p>
<div class="progress-cont">
<div class="progress" [style.width]="file?.progress + '%'">
</div>
</div>
</div>
<button mat-icon-button class="delete" width="20px" alt="file" (click)="deleteFile(i)">
<mat-icon>delete</mat-icon>
</button>
</div>
</div>

View file

@ -1,135 +0,0 @@
.container {
width: 450px;
height: 180px;
padding: 20px 0px 20px 0px;
text-align: center;
border: dashed 1px #979797;
position: relative;
margin: 0 auto;
}
input {
opacity: 0;
position: absolute;
z-index: 2;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
.fileover {
animation: shake 1s;
animation-iteration-count: infinite;
}
.files-list {
margin-top: 1.5rem;
.single-file {
display: flex;
padding: 0.5rem;
justify-content: space-between;
align-items: center;
border: dashed 1px #979797;
margin-bottom: 1rem;
img.delete {
margin-left: 0.5rem;
cursor: pointer;
align-self: flex-end;
}
display: flex;
flex-grow: 1;
.name {
font-size: 14px;
font-weight: 500;
color: #353f4a;
margin: 0;
}
.size {
font-size: 12px;
font-weight: 500;
color: #a4a4a4;
margin: 0;
margin-bottom: 0.25rem;
}
.info {
width: 100%
}
}
}
/* Shake animation */
@keyframes shake {
0% {
transform: translate(1px, 1px) rotate(0deg);
}
10% {
transform: translate(-1px, -2px) rotate(-1deg);
}
20% {
transform: translate(-3px, 0px) rotate(1deg);
}
30% {
transform: translate(3px, 2px) rotate(0deg);
}
40% {
transform: translate(1px, -1px) rotate(1deg);
}
50% {
transform: translate(-1px, 2px) rotate(-1deg);
}
60% {
transform: translate(-3px, 1px) rotate(0deg);
}
70% {
transform: translate(3px, 1px) rotate(-1deg);
}
80% {
transform: translate(-1px, -1px) rotate(1deg);
}
90% {
transform: translate(1px, 2px) rotate(0deg);
}
100% {
transform: translate(1px, -2px) rotate(-1deg);
}
}
.progress-cont {
height: 7px;
width: 100%;
border-radius: 4px;
background-color: #d0d0d0;
position: relative;
.progress {
width: 0;
height: 100%;
position: absolute;
z-index: 1;
top: 0;
left: 0;
border-radius: 4px;
background-color: #4c97cb;
transition: 0.5s all;
}
}

View file

@ -1,25 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DragAndDropComponent } from './drag-and-drop.component';
describe('DragAndDropComponent', () => {
let component: DragAndDropComponent;
let fixture: ComponentFixture<DragAndDropComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DragAndDropComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(DragAndDropComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -1,93 +0,0 @@
import {Component, ElementRef, EventEmitter, OnInit, Output, ViewChild} from '@angular/core';
@Component({
selector: 'app-drag-and-drop',
templateUrl: './drag-and-drop.component.html',
styleUrls: ['./drag-and-drop.component.scss']
})
export class DragAndDropComponent
{
@ViewChild("fileDropRef", { static: false }) fileDropEl: ElementRef;
info_image = "Vos annonces seront affichées dans un rectangle de rapport 1/5 avec: \n • 1 la largeur du rectangle \n • 5 la hauteur du rectangle" ;
files: any[] = [];
@Output() eventEmitter = new EventEmitter<any>();
/**
* on file drop handler
*/
onFileDropped($event) {
this.prepareFilesList($event);
this.eventEmitter.emit(this.files);
}
/**
* handle file from browsing
*/
fileBrowseHandler(files) {
this.prepareFilesList(files);
this.eventEmitter.emit(this.files);
}
/**
* Delete file from files list
* @param index (File index)
*/
deleteFile(index: number) {
if (this.files[index].progress < 100) {
console.log("Upload in progress.");
return;
}
this.files.splice(index, 1);
this.eventEmitter.emit(this.files);
}
/**
* Simulate the upload process
*/
uploadFilesSimulator(index: number) {
setTimeout(() => {
if (index === this.files.length) {
return;
} else {
const progressInterval = setInterval(() => {
if (this.files[index].progress === 100) {
clearInterval(progressInterval);
this.uploadFilesSimulator(index + 1);
} else {
this.files[index].progress += 5;
}
}, 200);
}
}, 1000);
}
/**
* Convert Files list to normal array list
* @param files (Files List)
*/
prepareFilesList(files: Array<any>) {
for (const item of files) {
item.progress = 0;
this.files.push(item);
}
this.fileDropEl.nativeElement.value = "";
this.uploadFilesSimulator(0);
}
/**
* format bytes
* @param bytes (File size in bytes)
* @param decimals (Decimals point)
*/
formatBytes(bytes, decimals = 2) {
if (bytes === 0) {
return "0 Bytes";
}
const k = 1024;
const dm = decimals <= 0 ? 0 : decimals;
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
}
}

View file

@ -1,43 +0,0 @@
<mat-form-field class="example-chip-list" appearance="fill">
<!-- ------------------------------------------------------------------------------------ -->
<mat-label>Sujets</mat-label>
<!-- ------------------------------------------------------------------------------------ -->
<mat-chip-list #chipList aria-label="Fruit selection">
<mat-chip
*ngFor="let interest of myInterests"
[selectable]="selectable"
[removable]="removable"
(removed)="remove(interest)">
{{interest}}
<button matChipRemove *ngIf="removable">
<mat-icon>cancel</mat-icon>
</button>
</mat-chip>
<input
placeholder="Tapez un centre d'intérêt et pressez 'Entrer' pour l'inserer"
#tagInput
[formControl]="formControl"
[matAutocomplete]="auto"
[matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
(matChipInputTokenEnd)="add($event)">
</mat-chip-list>
<!-- ------------------------------------------------------------------------------------ -->
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event)">
<mat-option *ngFor="let interest of filteredInterests | async" [value]="interest">
{{interest}}
</mat-option>
</mat-autocomplete>
<!-- ------------------------------------------------------------------------------------ -->
</mat-form-field>

View file

@ -1,16 +0,0 @@
mat-form-field {
width: 100%;
font-size: small;
}
mat-chip-list {
font-size: small;
}
mat-chip {
font-size: small;
}
input {
font-size: small;
}

View file

@ -1,25 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { InputInterestsAdComponent } from './input-interests-ad.component';
describe('BarTagsComponent', () => {
let component: InputInterestsAdComponent;
let fixture: ComponentFixture<InputInterestsAdComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ InputInterestsAdComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(InputInterestsAdComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -1,121 +0,0 @@
import {Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
import {COMMA, ENTER} from "@angular/cdk/keycodes";
import {FormControl} from "@angular/forms";
import {Observable} from "rxjs";
import {map, startWith} from "rxjs/operators";
import {MatChipInputEvent} from "@angular/material/chips";
import {MatAutocompleteSelectedEvent} from "@angular/material/autocomplete";
import {MessageService} from "../../../utils/services/message/message.service";
@Component({
selector: 'app-input-interests-ad',
templateUrl: './input-interests-ad.component.html',
styleUrls: ['./input-interests-ad.component.scss']
})
export class InputInterestsAdComponent implements OnInit
{
selectable = true;
removable = true;
separatorKeysCodes: number[] = [ENTER, COMMA];
formControl = new FormControl();
filteredInterests: Observable<string[]>;
@Input() myInterests: string[] = [];
allInterests: string[] = [];
@Output() eventEmitter = new EventEmitter<string[]>();
@ViewChild('tagInput') tagInput: ElementRef<HTMLInputElement>;
interestsNotSelected: string[] = [];
constructor( private messageService: MessageService ) {}
ngOnInit(): void
{
this.filteredInterests = this.formControl.valueChanges.pipe(
startWith(null),
map((fruit: string | null) => fruit ? this._filter(fruit) : this.interestsNotSelected.slice()));
this.messageService
.get("misc/getInterests")
.subscribe( retour => {
if(retour.status !== "success") {
console.log(retour);
}
else {
this.allInterests = [];
for(let elt of retour.data)
{
this.allInterests.push(elt.interest);
this.interestsNotSelected.push(elt.interest);
}
}
});
}
add(event: MatChipInputEvent): void
{
const value = (event.value || '').trim();
const index = this.interestsNotSelected.indexOf(value);
if (value && (index !== -1) && (!this.myInterests.includes(value)))
{
this.myInterests.push(value);
event.chipInput!.clear();
this.formControl.setValue(null);
this.eventEmitter.emit(this.myInterests);
this.interestsNotSelected.splice(index, 1);
}
}
remove(interest: string): void
{
// supprimer 'interest' de 'myInterest'
const index = this.myInterests.indexOf(interest);
if (index >= 0) this.myInterests.splice(index, 1);
this.eventEmitter.emit(this.myInterests);
// remmettre 'interest' dans 'interestsNotSelected'
if(!this.interestsNotSelected.includes(interest))
{
const indexOfAutres = this.interestsNotSelected.indexOf("Autres");
if(indexOfAutres !== -1)
{
this.interestsNotSelected.splice(indexOfAutres, 1);
if(interest !== "Autres") this.interestsNotSelected.push(interest);
this.interestsNotSelected.sort();
this.interestsNotSelected.push("Autres");
}
else {
this.interestsNotSelected.push(interest);
if(interest !== "Autres") this.interestsNotSelected.sort();
}
}
}
selected(event: MatAutocompleteSelectedEvent): void
{
const value = event.option.viewValue;
if(!this.myInterests.includes(value))
{
this.myInterests.push(value);
const index = this.interestsNotSelected.indexOf(value);
this.interestsNotSelected.splice(index, 1);
}
this.tagInput.nativeElement.value = '';
this.formControl.setValue(null);
this.eventEmitter.emit(this.myInterests);
}
private _filter(value: string): string[]
{
const filterValue = value.toLowerCase();
return this.interestsNotSelected.filter(fruit => fruit.toLowerCase().includes(filterValue));
}
}

View file

@ -1,185 +0,0 @@
<div [class]="themeService.getClassTheme()">
<div class="myContainer">
<!-- Navbar -->
<app-navbar-advertiser></app-navbar-advertiser><br><br>
<!-- filtre + btnAddUser -->
<div class="row" style="margin: 20px 3% 20px 3%">
<!-- filtre -->
<div class="col-10" style="padding: 0px 0px 0px 0px;">
<div class="filtersContainer mat-elevation-z8">
<!-- titre -->
<div style="font-weight: bold; margin-bottom: 10px;">
Filtre
</div>
<mat-divider></mat-divider>
<!-- filtre textuelle-->
<div style="margin: 10px 0px 20px 2%;">
<input class="textFilter" (keyup)="applyFilter($event)" placeholder="filtre...">
</div>
<!-- role + actif + période -->
<div class="row myRow">
<!-- visible -->
<div class="col-2">
<mat-checkbox [(ngModel)]="visible" (change)="onFilter()">visible</mat-checkbox><br>
<mat-checkbox [(ngModel)]="noVisible" (change)="onFilter()">non visible</mat-checkbox>
</div>
<!-- sujets -->
<div class="col-4">
<mat-form-field appearance="fill">
<mat-label>Sujets</mat-label>
<mat-select [formControl]="formControlInterests" multiple>
<mat-select-trigger>
{{formControlInterests.value ? formControlInterests.value[0] : ''}}
<span *ngIf="formControlInterests.value?.length > 1">
(+{{formControlInterests.value.length - 1}} {{formControlInterests.value?.length === 2 ? 'autre' : 'autres'}})
</span>
</mat-select-trigger>
<mat-option *ngFor="let topping of allInterests" [value]="topping">{{topping}}</mat-option>
</mat-select>
</mat-form-field>
<button mat-icon-button (click)="onFilter()">
<mat-icon>keyboard_tab</mat-icon>
</button>
</div>
<!-- période -->
<div class="col-6" style="text-align: right;">
Période de création: &nbsp;
<mat-form-field appearance="fill" style="width: 140px;">
<mat-label>Date de début</mat-label>
<input matInput type="date"
style="font-size: small; width: 140px;"
[ngModel] ="startDate | date:'yyyy-MM-dd'"
(ngModelChange)="onNewStartDate($event); onFilter();">
</mat-form-field>
&nbsp; - &nbsp;
<mat-form-field appearance="fill" style="width: 140px;">
<mat-label>Date de fin</mat-label>
<input matInput type="date"
style="font-size: small; width: 140px;"
[ngModel] ="endDate | date:'yyyy-MM-dd'"
(ngModelChange)="onNewEndDate($event); onFilter();">
</mat-form-field>
</div>
</div>
</div>
</div>
<!-- btnAdd -->
<div class="col-2" style="text-align: right; position: relative;">
<button mat-button class="btnAjouter" (click)="onAdd()" style="position: absolute; bottom: 0; right: 0;">
<mat-icon>add_circle</mat-icon> Ajouter une annonce
</button>
</div>
</div>
<!-- Table -->
<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">
<!-- IsActive Column -->
<ng-container matColumnDef="isVisible">
<th mat-header-cell *matHeaderCellDef mat-sort-header>
<mat-icon>power_settings_new</mat-icon>
</th>
<td mat-cell *matCellDef="let advert">
<mat-slide-toggle [(ngModel)]="advert.isVisible" (click)="onSliderIsVisible(advert)"></mat-slide-toggle>
</td>
</ng-container>
<!-- Title Column -->
<ng-container matColumnDef="title">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Titre </th>
<td mat-cell *matCellDef="let advert">
{{advert.title}}
</td>
</ng-container>
<!-- Subject Column -->
<ng-container matColumnDef="interests">
<th mat-header-cell *matHeaderCellDef> Sujets </th>
<td mat-cell *matCellDef="let advert">
<span *ngFor="let interest of advert.interests; let isLast = last;">
<span *ngIf="!isLast"> {{interest}}, </span>
<span *ngIf="isLast"> {{interest}} </span>
</span>
</td>
</ng-container>
<!-- CreatedAt Column -->
<ng-container matColumnDef="createdAt">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Date de création </th>
<td mat-cell *matCellDef="let advert">
{{ advert.createdAt | date:'dd/LL/YYYY à HH:mm:ss' }}
</td>
</ng-container>
<!-- LastUpdate Column -->
<ng-container matColumnDef="updatedAt">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Dernière modification </th>
<td mat-cell *matCellDef="let advert">
{{ advert.updatedAt | date:'dd/LL/YYYY à HH:mm:ss' }}
</td>
</ng-container>
<!-- Views Column -->
<ng-container matColumnDef="countViews">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Vues </th>
<td mat-cell *matCellDef="let advert">
{{advert.countViews}}
</td>
</ng-container>
<!-- Actions Column -->
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef> Actions </th>
<td mat-cell *matCellDef="let advert">
<button mat-icon-button (click)="onVisualizeImages(advert)">
<mat-icon> insert_photo</mat-icon>
</button>
<button mat-icon-button (click)="onUpdate(advert)">
<mat-icon>edit</mat-icon>
</button>
<button mat-icon-button (click)="onDelete(advert)">
<mat-icon>delete</mat-icon>
</button>
</td>
</ng-container>
<!-- Directives -->
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
<tr class="mat-row" *matNoDataRow>
<td class="mat-cell" colspan="4"> Aucune vidéo ne correspond au filtre: "{{input.value}}" </td>
</tr>
</table>
<div style="width: 94%; margin: auto auto">
<mat-paginator [pageSizeOptions]="[10, 20, 50, 100]" showFirstLastButtons aria-label="Select page of periodic elements"></mat-paginator>
</div>
<br><br>
</div>
</div>

View file

@ -1,87 +0,0 @@
.myContainer {
max-width: 100vw;
height: 100vh;
overflow-x: hidden;
font-size: small;
}
// ----------------------------------------------------------
.filtersContainer {
width: 95%;
background-color: white;
padding: 10px 10px 10px 10px;
}
.myRow {
margin-left: 1%;
}
.textFilter {
width: 50%;
font-size: medium;
border-radius: 5px;
}
.btnAjouter {
background-color: white;
border: solid 1px black;
}
// ----------------------------------------------------------
table {
margin: 0 auto;
width: 94%;
font-size: small;
}
.darkTheme table { border: solid 2px white; }
th.mat-sort-header-sorted {
color: black;
}
td {
font-size: small;
}
input {
width: 30%;
font-size: large;
border-radius: 5px;
}
// --------------------------------------------------------------------
// rong gauche
::ng-deep .mat-slide-toggle-thumb {
background-color: white !important;
}
// trait droite
::ng-deep .mat-slide-toggle-bar {
background-color: gray !important;
}
// rond droite
::ng-deep .mat-slide-toggle.mat-checked:not(.mat-disabled) .mat-slide-toggle-thumb {
background-color: white !important;
}
// trait gauche
::ng-deep .mat-slide-toggle.mat-checked:not(.mat-disabled) .mat-slide-toggle-bar {
background-color: cornflowerblue !important;
}
// -------------------------------------------------------------------------
::ng-deep .mat-pseudo-checkbox-checked {
background-color: black !important;
}

View file

@ -1,25 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PageAdListAdvertiserComponent } from './page-ad-list-advertiser.component';
describe('PageAdvertiserComponent', () => {
let component: PageAdListAdvertiserComponent;
let fixture: ComponentFixture<PageAdListAdvertiserComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ PageAdListAdvertiserComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(PageAdListAdvertiserComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -1,309 +0,0 @@
import {AfterViewInit, Component, OnInit, ViewChild} from '@angular/core';
import {MatSort} from "@angular/material/sort";
import {ThemeService} from "../../../utils/services/theme/theme.service";
import {MatTableDataSource} from "@angular/material/table";
import {AdvertWithCountViews} from "../../../utils/interfaces/advert";
import {MatDialog} from "@angular/material/dialog";
import {PopupAddOrUpdateAdComponent} from "../popup-add-or-update-ad/popup-add-or-update-ad.component";
import {MatSnackBar} from "@angular/material/snack-bar";
import {PopupDeleteAdAdvertiserComponent} from "../popup-delete-ad-advertiser/popup-delete-ad-advertiser.component";
import {MatPaginator} from "@angular/material/paginator";
import {PopupVisualizeImagesAdvertiserComponent} from "../popup-visualize-images-advertiser/popup-visualize-images-advertiser.component";
import {FictitiousAdvertsService} from "../../../utils/services/fictitiousDatas/fictitiousAdverts/fictitious-adverts.service";
import {FormControl} from "@angular/forms";
import {FictitiousUtilsService} from "../../../utils/services/fictitiousDatas/fictitiousUtils/fictitious-utils.service";
import {MessageService} from "../../../utils/services/message/message.service";
import {HttpParams} from "@angular/common/http";
@Component({
selector: 'app-page-ad-list-advertiser',
templateUrl: './page-ad-list-advertiser.component.html',
styleUrls: ['./page-ad-list-advertiser.component.scss']
})
export class PageAdListAdvertiserComponent implements AfterViewInit
{
displayedColumns: string[] = [ 'isVisible', 'title', 'interests', 'createdAt', 'updatedAt', 'countViews', 'actions' ];
tabAdvertWithCountViews: AdvertWithCountViews[] = [];
dataSource;
@ViewChild(MatSort) sort: MatSort;
@ViewChild(MatPaginator) paginator: MatPaginator;
visible: boolean = true;
noVisible: boolean = true;
startDate: Date = null;
endDate: Date = null;
formControlInterests = new FormControl();
allVideoCategorie = [];
allInterests: string[] = [];
constructor( public themeService: ThemeService,
private fictitiousAdvertsService: FictitiousAdvertsService,
private fictitiousUtilsService: FictitiousUtilsService,
public dialog: MatDialog,
private snackBar: MatSnackBar,
private messageService: MessageService ) { }
ngAfterViewInit(): void
{
// Ask interests
this.messageService
.get("misc/getInterests")
.subscribe(ret => this.afterReceivingInterests(ret), err => this.afterReceivingInterests(err) );
// Ask ads
let params = new HttpParams();
params = params.append("isActive", true);
this.messageService
.get("ad/findAll", params)
.subscribe(ret => this.afterReceivingAds(ret), err => this.afterReceivingAds(err));
}
afterReceivingInterests(retour: any): void
{
if(retour.status !== "success") {
console.log("afterReceivingInterests");
console.log(retour);
}
else {
this.allVideoCategorie = retour.data;
this.allInterests = retour.data.map(x => x.interest);
this.allInterests.sort();
}
}
afterReceivingAds(retour: any): void
{
if(retour.status !== "success") {
console.log(retour);
}
else {
if(retour.data.length !== 0)
{
for(let advert of retour.data) this.tabAdvertWithCountViews.push(this.advertToAdvertWithCountViews(advert));
this.dataSource = new MatTableDataSource<AdvertWithCountViews>();
this.onFilter();
}
}
}
applyFilter(event: Event): void
{
const filterValue = (event.target as HTMLInputElement).value;
this.dataSource.filter = filterValue.trim().toLowerCase();
}
onVisualizeImages(advert: AdvertWithCountViews)
{
if(advert.images.length !== 0)
{
const config = {
width: '30%',
height: '90%',
data: { images: advert.images }
};
this.dialog
.open(PopupVisualizeImagesAdvertiserComponent, config)
.afterClosed()
.subscribe(retour => {});
}
else {
const config = { duration: 2000, panelClass: "custom-class" };
const message = "Cette annonce ne contient aucune image" ;
this.snackBar.open( message, "", config);
}
}
onAdd(): void
{
const config = {
width: '75%',
//height: '80%',
panelClass: 'custom-dialog-container',
data: {
action: "add",
advert: null,
allVideoCategorie: this.allVideoCategorie,
allTitle: this.tabAdvertWithCountViews.map(x => x.title)
}
};
this.dialog
.open(PopupAddOrUpdateAdComponent, config)
.afterClosed()
.subscribe( advertAdded => {
const config = { duration: 1000, panelClass: "custom-class" };
let message = "" ;
if((advertAdded === undefined) || (advertAdded === null)) {
message = "Opération annulée" ;
}
else {
this.tabAdvertWithCountViews.push(this.advertToAdvertWithCountViews(advertAdded));
this.onFilter();
message = "L'annonce a bien été ajoutée ✔" ;
}
this.snackBar.open( message, "", config);
});
}
onUpdate(advertToUpdate: AdvertWithCountViews): void
{
const config = {
width: '75%',
//height: '80%',
panelClass: 'custom-dialog-container',
data: {
action: "update",
advert: advertToUpdate,
allVideoCategorie: this.allVideoCategorie,
allTitle: this.tabAdvertWithCountViews.map(x => x.title)
}
};
this.dialog
.open(PopupAddOrUpdateAdComponent, config)
.afterClosed()
.subscribe( advertUpdated => {
const config = { duration: 1000, panelClass: "custom-class" };
let message = "" ;
if((advertUpdated === undefined) || (advertUpdated === null)) {
message = "Opération annulée" ;
}
else {
const index = this.tabAdvertWithCountViews.findIndex(elt => (elt.id === advertToUpdate.id));
this.tabAdvertWithCountViews.splice(index, 1, this.advertToAdvertWithCountViews(advertUpdated));
this.onFilter();
message = "L'annonce a bien été modifiée ✔" ;
}
this.snackBar.open( message, "", config);
});
}
onDelete(advert: AdvertWithCountViews): void
{
const config = {
data: { advert: advert }
};
this.dialog
.open(PopupDeleteAdAdvertiserComponent, config)
.afterClosed()
.subscribe( retour => {
const config = { duration: 1000, panelClass: "custom-class" };
let message = "" ;
if((retour === undefined) || (retour === null)) {
message = "Opération annulée" ;
}
else {
const index = this.dataSource.data.findIndex( elt => (elt.id === advert.id));
this.dataSource.data.splice(index, 1);
this.dataSource.data = this.dataSource.data;
this.dataSource = this.dataSource;
message = advert.title + " a bien été supprimée ✔" ;
}
this.snackBar.open( message, "", config);
});
}
onFilter(): void
{
if(this.dataSource === null || this.dataSource === undefined) this.dataSource = new MatTableDataSource();
this.dataSource.data = [];
for(let advert of this.tabAdvertWithCountViews)
{
let valide: boolean = true;
if(advert.isVisible && this.visible) valide = true;
else if((!advert.isVisible) && this.noVisible) valide = true;
else valide = false;
if(valide)
{
if ((advert.createdAt === null) && (this.startDate !== null)) valide = false;
else if ((advert.createdAt === null) && (this.endDate !== null)) valide = false;
else if (this.startDate !== null)
{
if(this.startDate.getTime() > advert.createdAt.getTime()) valide = false;
else if (this.endDate !== null)
{
if(this.endDate.getTime() < advert.createdAt.getTime()) valide = false;
}
}
}
if(valide) {
if(this.formControlInterests.value !== null) {
for (let interest of this.formControlInterests.value) {
if (advert.interests.indexOf(interest) === -1) {
valide = false;
break;
}
}
}
}
if(valide) this.dataSource.data.push(advert);
}
this.dataSource = new MatTableDataSource(this.dataSource.data);
this.dataSource.sort = this.sort;
this.dataSource.paginator = this.paginator;
}
onNewStartDate(event): void {
this.startDate = new Date(event);
}
onNewEndDate(event): void {
this.endDate = new Date(event);
}
onSliderIsVisible(advert: any): void
{
// il faut envoyer la négation de user.isActive
this.messageService
.put("ad/update/"+advert.id, { isVisible: !advert.isVisible })
.subscribe(
ret => {},
err => {
console.log("onSliderIsVisible");
console.log(err);
}
);
}
advertToAdvertWithCountViews(advert): AdvertWithCountViews
{
return {
id: advert.id,
userId: advert.userId,
title: advert.title,
url: advert.url,
images: advert.images,
interests: advert.interests.map(x => x.interest),
comment: advert.comment,
views: advert.views,
countViews: advert.views.length,
isVisible: advert.isVisible,
isActive: advert.isActive,
createdAt: advert.createdAt,
updatedAt: advert.updatedAt,
}
}
}

View file

@ -1,83 +0,0 @@
<div class="myContainer1">
<div class="myContainer2">
<!-- titre popup -->
<h1 mat-dialog-title>{{title}}</h1>
<mat-divider></mat-divider><br>
<!-- tous les champs -->
<div class="row">
<!-- title + interests + comments + isVisible -->
<div class="col-6">
<!-- Title -->
<mat-form-field class="titleContainer" appearance="fill">
<mat-label> Titre annonce </mat-label>
<input matInput type="text" [(ngModel)]="advert.title" required>
</mat-form-field>
<!-- Interests -->
<app-input-interests-ad [myInterests]="advert.interests" (eventEmitter)="onEventInputTags($event)"></app-input-interests-ad>
<!-- Comments -->
<mat-form-field class="commentContainer" appearance="fill">
<mat-label> Commentaire </mat-label>
<textarea matInput [(ngModel)]="advert.comment" rows="5" style="resize: none;"></textarea>
</mat-form-field><br>
<!-- url -->
<mat-form-field class="commentContainer" appearance="fill">
<mat-label> URL </mat-label>
<input matInput [(ngModel)]="advert.url">
</mat-form-field><br>
<!-- IsVisible -->
<mat-checkbox [(ngModel)]="advert.isVisible"> Visible </mat-checkbox><br><br>
<!-- Images déjà présentes -->
<div *ngIf="advert.images.length !== 0">
<div style="font-weight: bold; margin-bottom: 5px;">
Images déjà associées:
</div>
<div style="margin-left: 20px; padding-left: 2px; border-left: solid 1px #a4a4a4">
<div *ngFor="let image of advert.images" style="padding: 2px 0px 2px 0px;">
<mat-chip [selectable]="true" [removable]="true" style="font-size: small;">
{{image.description}}
<button matChipRemove (click)="onRemoveImgAlreadyPresent(image)">
<mat-icon>cancel</mat-icon>
</button>
</mat-chip>
</div>
</div>
</div>
</div>
<!-- nouvelles images -->
<div class="col-6" style="overflow-x: hidden; overflow-y: scroll; max-height: 70vh;">
<app-drag-and-drop (eventEmitter)="onReceiveNewImages($event)"></app-drag-and-drop>
</div>
</div>
<br><mat-divider></mat-divider>
<!-- message d'erreur -->
<div *ngIf="hasError" style="text-align: center; margin-bottom: 20px;">
<span class="mat-error">{{errorMessage}}</span>
</div>
<!-- Actions -->
<mat-dialog-actions align="end">
<button mat-button (click)="dialogRef.close()">Annuler</button>
<button mat-button (click)="onValidate()">Valider</button>
</mat-dialog-actions>
</div>
</div>

View file

@ -1,90 +0,0 @@
.myContainer1 {
padding: 10px 10px 0px 25px;
margin: 0px 0px 0px 0px;
font-size: small;
}
.myContainer2 {
padding: 0px 0px 0px 0px;
margin: 0px 0px 0px 0px;
overflow-y: hidden;
overflow-x: hidden;
-ms-overflow-style: none;
scrollbar-width: none;
}
.myContainer2::-webkit-scrollbar {
display: none;
}
h1 {
text-align: center;
font-size: large;
}
.col-6, .col-8 {
border-left: solid 1px #a4a4a4;
}
// -------------------------------------------------------------------------
.titleContainer {
width: 100%;
}
.commentContainer {
width: 100%;
}
.imageContainer {
border: solid 1px grey;
}
mat-dialog-actions {
margin-bottom: 0px;
}
button {
font-size: small;
}
// -------------------------------------------------------------------------
// --- LightTheme ---
// aura
.lightTheme ::ng-deep .mat-checkbox-ripple .mat-ripple-element {
background-color: grey !important;
}
// contenu coche
.lightTheme ::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background {
background-color: black !important;
}
// indeterminate
.lightTheme ::ng-deep .mat-checkbox .mat-checkbox-frame {
border-color: black !important;
background-color: white !important;
}
// --- DarkTheme ---
// aura
.darTheme ::ng-deep .mat-checkbox-ripple .mat-ripple-element {
background-color: grey !important;
}
// contenu coche
.darkTheme ::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background {
background-color: black !important;
}
// indeterminate
.darkTheme ::ng-deep .mat-checkbox .mat-checkbox-frame {
border-color: white !important;
//background-color: white !important;
}

View file

@ -1,25 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PopupAddOrUpdateAdComponent } from './popup-add-or-update-ad.component';
describe('PopupAddOrUpdateAdComponent', () => {
let component: PopupAddOrUpdateAdComponent;
let fixture: ComponentFixture<PopupAddOrUpdateAdComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ PopupAddOrUpdateAdComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(PopupAddOrUpdateAdComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -1,221 +0,0 @@
import {Component, Inject, OnInit} from '@angular/core';
import {Advert} from "../../../utils/interfaces/advert";
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import {MessageService} from "../../../utils/services/message/message.service";
import {ThemeService} from "../../../utils/services/theme/theme.service";
const ADVERT_VIDE: Advert = {
_id: "",
userId: "",
title: "",
url: "",
images: [],
interests: [],
comment: "",
views: [],
isVisible: true,
isActive: true,
createdAt: new Date(),
updatedAt: new Date(),
}
@Component({
selector: 'app-popup-add-or-update-ad',
templateUrl: './popup-add-or-update-ad.component.html',
styleUrls: ['./popup-add-or-update-ad.component.scss']
})
export class PopupAddOrUpdateAdComponent implements OnInit
{
advert: any;
title: string = "" ;
allVideoCategorie = [];
allTitle = [];
tabOfNewImagesBase64 = [];
tabOfNewImagesName = [];
hasError: boolean = false;
errorMessage: string = "" ;
constructor( public dialogRef: MatDialogRef<PopupAddOrUpdateAdComponent>,
@Inject(MAT_DIALOG_DATA) public data,
private messageService: MessageService,
public themeService: ThemeService ) { }
ngOnInit(): void
{
this.allVideoCategorie = this.data.allVideoCategorie;
this.allTitle = this.data.allTitle.slice();
if(this.data.action === "add")
{
this.advert = Object.assign({}, ADVERT_VIDE);
this.advert.images = [];
this.advert.interests = [];
this.title = "Ajouter annonce" ;
}
else
{
this.advert = Object.assign({}, this.data.advert);
this.advert.interests = this.data.advert.interests.slice();
this.title = "Modifier annonce" ;
const indexOldTitle = this.allTitle.findIndex(title => title == this.advert.title);
this.allTitle.splice(indexOldTitle, 1);
}
}
onValidate(): void
{
this.checkField();
if(!this.hasError)
{
// preparation des donnees
this.prepareAdvertInterests();
this.prepareAdvertImages();
// si creation
if (this.data.action === "add")
{
this.messageService
.post("ad/create", this.advert)
.subscribe(ret => this.onCreateCallback(ret), err => this.onCreateCallback(err));
}
// si update
else
{
const id = this.advert.id;
Reflect.deleteProperty(this.advert, "id");
Reflect.deleteProperty(this.advert, "_id");
this.messageService
.put("ad/update/" + id, this.advert)
.subscribe(ret => this.onUpdateCallback(ret, id), err => this.onUpdateCallback(err, id));
}
}
}
checkField()
{
if(this.advert.title.length === 0) {
this.errorMessage = "Veuillez remplir le champ 'titre'." ;
this.hasError = true;
}
else if(this.allTitle.includes(this.advert.title)) {
this.errorMessage = "Ce titre est déjà pris." ;
this.hasError = true;
}
else if((this.advert.images.length === 0) && (this.tabOfNewImagesName.length === 0)) {
this.errorMessage = "Veuillez uploader au moins une image." ;
this.hasError = true;
}
else {
this.errorMessage = "";
this.hasError = false;
}
}
onCreateCallback(retour: any): void
{
if(retour.status !== "success") {
console.log(retour);
this.dialogRef.close();
}
else {
this.dialogRef.close(retour.data);
}
}
onUpdateCallback(retour: any, id: string): void
{
if(retour.status !== "success") {
console.log(retour);
this.dialogRef.close();
}
else {
this.advert.id = id;
this.dialogRef.close(this.advert);
}
}
onEventInputTags(myTags: string[]): void
{
this.advert.interests = myTags;
}
onRemoveImgAlreadyPresent(image)
{
const index = this.advert.images.indexOf(image);
this.advert.images.splice(index, 1);
}
onReceiveNewImages(files: any): void
{
this.tabOfNewImagesBase64 = [];
this.tabOfNewImagesName = [];
if(files)
{
for(let file of files)
{
if(file)
{
const reader = new FileReader();
reader.onload = this.handleReaderLoaded.bind(this);
this.tabOfNewImagesName.push(file.name)
reader.readAsBinaryString(file);
}
}
}
}
handleReaderLoaded(e)
{
this.tabOfNewImagesBase64.push('data:image/png;base64,' + btoa(e.target.result))
}
// Met bien en forme les "images" avant d'être envoyer
prepareAdvertImages(): void
{
for(let i=0; i<this.tabOfNewImagesName.length ; i++)
{
let newImagePrepared = {
base64: this.tabOfNewImagesBase64[i],
url: "",
description: this.tabOfNewImagesName[i],
type: 0,
};
this.advert.images.push(newImagePrepared);
}
}
// Met bien en forme les "interests" avant d'être envoyer
prepareAdvertInterests(): void
{
let interests = [];
for (let interest of this.advert.interests) {
for (let videoCategorie of this.allVideoCategorie) {
if (videoCategorie.interest === interest) {
interests.push(videoCategorie);
break;
}
}
}
this.advert.interests = interests;
}
}

View file

@ -1,8 +0,0 @@
<mat-dialog-content class="mat-typography">
Êtes-vous sûr de vouloir supprimer l'annonce <i>{{advert.title}}</i> ?
</mat-dialog-content>
<mat-dialog-actions align="end">
<button mat-button (click)="dialogRef.close();">Annuler</button>
<button mat-button (click)="onValidate()" cdkFocusInitial>Valider</button>
</mat-dialog-actions>

View file

@ -1,25 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PopupDeleteAdAdvertiserComponent } from './popup-delete-ad-advertiser.component';
describe('PopupDeleteAdComponent', () => {
let component: PopupDeleteAdAdvertiserComponent;
let fixture: ComponentFixture<PopupDeleteAdAdvertiserComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ PopupDeleteAdAdvertiserComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(PopupDeleteAdAdvertiserComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -1,47 +0,0 @@
import {Component, Inject, OnInit} from '@angular/core';
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import {MessageService} from "../../../utils/services/message/message.service";
@Component({
selector: 'app-popup-delete-ad-advertiser',
templateUrl: './popup-delete-ad-advertiser.component.html',
styleUrls: ['./popup-delete-ad-advertiser.component.scss']
})
export class PopupDeleteAdAdvertiserComponent implements OnInit
{
advert: any;
constructor( public dialogRef: MatDialogRef<PopupDeleteAdAdvertiserComponent>,
@Inject(MAT_DIALOG_DATA) public data,
private messageService: MessageService) { }
ngOnInit(): void
{
this.advert = this.data.advert;
}
onValidate(): void
{
this.messageService
.delete("ad/delete/"+this.advert.id)
.subscribe(ret => this.onValidateCallback(ret), err => this.onValidateCallback(err));
}
onValidateCallback(retour: any): void
{
if(retour.status !== "success") {
console.log(retour);
this.dialogRef.close();
}
else {
this.dialogRef.close(true);
}
}
}

Some files were not shown because too many files have changed in this diff Show more