Skip to content
Snippets Groups Projects
Commit 541d6290 authored by Marlène SIMONDANT's avatar Marlène SIMONDANT
Browse files

Merge branch 'feat/US30-dynamic-about-page' into 'dev'

feat/US30-dynamic-about-page

See merge request web-et-numerique/pamn_plateforme-des-acteurs-de-la-mediation-numerique/pamn_server!113
parents 88baa264 e4096e89
No related branches found
No related tags found
3 merge requests!1251.14,!1171.13,!113feat/US30-dynamic-about-page
[
{
"slug": "qui-sommes-nous",
"id": "61efc44b26db9e00015c4f32",
"title": "Qui sommes-nous ?",
"html": "<p>La numérisation accélérée des différents services privés et publics ainsi que la crise sanitaire que nous traversons a renforcé une fracture numérique déjà forte pour un nombre important de citoyens.</p><p>Au printemps 2019, la Métropole de Lyon s'est saisie des enjeux autour de l'inclusion numérique en initiant la structuration d'un réseau des acteurs de la médiation numérique sur son territoire. Son objectif est de mettre en relation les acteurs qui œuvrent au quotidien pour limiter cette fracture numérique, nombreux sur le territoire de la Métropole : associations, centres sociaux, structures informations jeunesses, grands opérateurs de services publics, collectivités...</p><p>Des ateliers de travail ont été organisés en 2019 pour identifier les besoins de ces acteurs et 9 offres de services ont été identifiées :</p><h3 id=\"recenser-et-partager-des-ressources-existantes-optimisation\">Recenser et partager des ressources existantes (optimisation)</h3><figure class=\"kg-card kg-image-card\"><img src=\"http://localhost:4200/assets/img/about_illustration_1.jpg\" class=\"kg-image\" alt=\"illustration des besoins\" loading=\"lazy\"></figure><h3 id=\"co-construire-de-nouvelles-ressources-d%C3%A9veloppement\">Co-construire de nouvelles ressources (développement)</h3><figure class=\"kg-card kg-image-card\"><img src=\"http://localhost:4200/assets/img/about_illustration_2.jpg\" class=\"kg-image\" alt=\"illustration des besoins\" loading=\"lazy\"></figure><p>Cet espace vise à centraliser et mettre en commun les ressources développées dans le cadre du réseau par ses acteurs.</p><p>N'hésitez pas à contribuer à cet espace en partageant vos ressources</p><!--kg-card-begin: markdown--><div style=\"display:flex; margin:0;padding:5px;align-items:center;\"\n</div><!--kg-card-end: markdown--><figure class=\"kg-card kg-image-card\"><img src=\"http://localhost:2368/content/images/2022/01/logo_europe.png\" class=\"kg-image\" alt loading=\"lazy\"></figure><figure class=\"kg-card kg-image-card\"><img src=\"http://localhost:2368/content/images/2022/01/logo_region.png\" class=\"kg-image\" alt=\"logo de l'union européenne\" loading=\"lazy\" width=\"220\" height=\"98\"></figure>",
"feature_image": null,
"featured": false,
"visibility": "public",
"created_at": "2022-01-25T09:35:07.000+00:00",
"updated_at": "2022-01-25T09:35:07.000+00:00",
"published_at": "2022-01-25T09:35:07.000+00:00",
"status": "published",
"custom_excerpt": null,
"codeinjection_head": null,
"codeinjection_foot": null,
"custom_template": null,
"canonical_url": null,
"url": "http://localhost:2368/qui-sommes-nous/",
"excerpt": "La numérisation accélérée des différents services privés et publics ainsi que la\ncrise sanitaire que nous traversons a renforcé une fracture numérique déjà forte\npour un nombre important de citoyens.\n\nAu printemps 2019, la Métropole de Lyon s'est saisie des enjeux autour de\nl'inclusion numérique en initiant la structuration d'un réseau des acteurs de la\nmédiation numérique sur son territoire. Son objectif est de mettre en relation\nles acteurs qui œuvrent au quotidien pour limiter cette fracture ",
"page": true,
"og_image": null,
"og_title": null,
"og_description": null,
"twitter_image": null,
"twitter_title": null,
"twitter_description": null,
"meta_title": null,
"meta_description": null,
"frontmatter": null
},
{
"slug": "accessibilite",
"id": "61effea226db9e00015c4f41",
"title": "Accessibilité",
"html": "<p>Page accessibilité. Contenu en cours de rédaction. </p>",
"feature_image": null,
"featured": false,
"visibility": "public",
"created_at": "2022-01-25T13:44:02.000+00:00",
"updated_at": "2022-01-25T13:44:02.000+00:00",
"published_at": "2022-01-25T13:44:02.000+00:00",
"status": "published",
"custom_excerpt": null,
"codeinjection_head": null,
"codeinjection_foot": null,
"custom_template": null,
"canonical_url": null,
"url": "http://localhost:2368/accessibilite/",
"excerpt": "Page accessibilité. Contenu a venir.",
"og_image": null,
"og_title": null,
"og_description": null,
"twitter_image": null,
"twitter_title": null,
"twitter_description": null,
"meta_title": null,
"meta_description": null,
"frontmatter": null
}
]
......@@ -5,6 +5,8 @@ const tagsData = require('./ghost/migrations/init/tags.json');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const postsData = require('./ghost/migrations/init/posts.json');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const pagesData = require('./ghost/migrations/init/pages.json');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const path = require('path');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const GhostAdminAPI = require('@tryghost/admin-api');
......@@ -21,7 +23,7 @@ var api = new GhostAdminAPI({
async function deleteTags(existingTags) {
return await Promise.all(
_.forEach(existingTags, async (tag) => {
api.tags
await api.tags
.delete(_.pick(tag, ['id']))
.then((res) => {
return null;
......@@ -33,7 +35,7 @@ async function deleteTags(existingTags) {
async function deletePosts(existingPosts) {
return await Promise.all(
_.forEach(existingPosts, async (tag) => {
api.posts
await api.posts
.delete(_.pick(tag, ['id']))
.then((res) => {
return null;
......@@ -42,6 +44,18 @@ async function deletePosts(existingPosts) {
})
);
}
async function deletePages(existingPages) {
return await Promise.all(
_.forEach(existingPages, async (id) => {
await api.pages
.delete(_.pick(id, ['id']))
.then((res) => {
return null;
})
.catch((error) => console.error(error));
})
);
}
async function createTags(deleteOnly) {
// Get existing tags
......@@ -100,22 +114,22 @@ function processImagesInHTML(html) {
});
}
async function uploadPostImage(imagePath)
{
async function uploadPostImage(imagePath) {
let imagePromise = api.images.upload({
ref: imagePath,
file: path.resolve(imagePath)
});
ref: imagePath,
file: path.resolve(imagePath),
});
return Promise.resolve(imagePromise).then((url) => {
return Promise.resolve(imagePromise)
.then((url) => {
return url.url;
}).catch((error) => {
console.error(error);
return null
})
.catch((error) => {
console.error(error);
return null;
});
}
async function createPosts(deleteOnly) {
api.posts
.browse({ limit: 'all' })
......@@ -152,10 +166,52 @@ async function createPosts(deleteOnly) {
.catch((error) => console.error(error));
}
async function createPages(deleteOnly) {
api.pages
.browse({ limit: 'all' })
.then(async (existingPages) => {
// remove 'meta' key
delete existingPages['meta'];
if (existingPages.length > 0) {
console.log(`-- Dropping ${existingPages.length} pages... --`);
// Delete existing pages
await deletePages(existingPages).then(() => {
console.log('-- Pages dropped --');
});
} else {
console.log('-- No pages to drop --');
}
// wait complete delete of pages, if not page slugs are appended by _2
await new Promise((r) => setTimeout(r, 1000));
// Creating new pages
if (!deleteOnly) {
console.log(`-- Creating ${pagesData.length} pages --`);
_.forEach(pagesData, async (page) => {
//upload de l'image en featured_image
if (page.feature_image) {
page.feature_image = await uploadPostImage(page.feature_image);
}
api.pages
.add(page, { source: 'html' })
.then((res) => {
console.log(`-- Page \`${res.title}\` created --`);
})
.catch((error) => console.error(error));
});
}
})
.catch((error) => console.error(error));
}
async function main(deleteOnly) {
createTags(deleteOnly).then(() => {
createPosts(deleteOnly);
});
createTags(deleteOnly)
.then(() => {
createPosts(deleteOnly);
})
.then(() => {
createPages(deleteOnly);
});
}
var myArgs = process.argv.slice(2);
......
......@@ -11,6 +11,7 @@ import { MailerModule } from './mailer/mailer.module';
import { TclModule } from './tcl/tcl.module';
import { AdminModule } from './admin/admin.module';
import { PostsModule } from './posts/posts.module';
import { PagesModule } from './pages/pages.module';
import { TempUserModule } from './temp-user/temp-user.module';
import { NewsletterModule } from './newsletter/newsletter.module';
import { ContactModule } from './contact/contact.module';
......@@ -29,6 +30,7 @@ import { ContactModule } from './contact/contact.module';
TclModule,
AdminModule,
PostsModule,
PagesModule,
TempUserModule,
NewsletterModule,
ContactModule,
......
import { HttpModule, HttpService } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { of } from 'rxjs';
import { ConfigurationModule } from '../configuration/configuration.module';
import { PagesController } from './pages.controller';
import { AxiosResponse } from 'axios';
describe('PagesController', () => {
let controller: PagesController;
const httpServiceMock = {
get: jest.fn(),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [ConfigurationModule, HttpModule],
providers: [
{
provide: HttpService,
useValue: httpServiceMock,
},
],
controllers: [PagesController],
}).compile();
controller = module.get<PagesController>(PagesController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
describe('getPagebySlug', () => {
it('should get page Hello by Slug hello', async () => {
const data = [
{
id: '61c4847b0ff4550001505090',
uuid: 'f4ee5a37-a343-4cad-8a32-3f6cf87f9569',
title: 'Hello',
slug: 'hello',
html: '<p>Test</p>',
comment_id: '61c4847b0ff4550001505090',
feature_image: 'http://localhost:2368/content/images/2021/12/dacc-4.png',
featured: false,
visibility: 'public',
created_at: '2022-01-25T09:35:07.000+00:00',
updated_at: '2022-01-26T15:24:24.000+00:00',
published_at: '2022-01-25T09:35:16.000+00:00',
custom_excerpt: null,
codeinjection_head: null,
codeinjection_foot: null,
custom_template: null,
canonical_url: null,
url: 'http://localhost:2368/hello/',
excerpt:
'« Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus.\n' +
'Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed,\n' +
'dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper\n' +
'congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est\n' +
'eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu\n' +
'massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut\n' +
'in risus volutpat libero pharetra tem',
reading_time: 1,
page: true,
access: true,
og_image: null,
og_title: null,
og_description: null,
twitter_image: null,
twitter_title: null,
twitter_description: null,
meta_title: null,
meta_description: null,
frontmatter: null,
},
];
const axiosResult: AxiosResponse = {
data: {
pages: data,
},
status: 200,
statusText: 'OK',
headers: {},
config: {},
};
httpServiceMock.get.mockImplementationOnce(() => of(axiosResult));
const result = await (await controller.getPagebySlug('hello')).toPromise();
expect(result.pages).toStrictEqual(data);
});
});
});
import { Controller, Get, HttpService, HttpException, HttpStatus, Logger, Param } from '@nestjs/common';
import { ConfigurationService } from '../configuration/configuration.service';
import { Observable } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { Page } from './schemas/page.schema';
import { rewriteGhostImgUrl } from '../shared/utils';
@Controller('pages')
export class PagesController {
private readonly logger = new Logger(PagesController.name);
constructor(private readonly httpService: HttpService, private readonly configService: ConfigurationService) {}
@Get(':slug')
public async getPagebySlug(@Param('slug') slug: string): Promise<Observable<{ pages: Page[] }>> {
return this.httpService
.get(`${process.env.GHOST_HOST_AND_PORT}/ghost/api/v3/content/pages/slug/` + slug, {
params: {
key: process.env.GHOST_CONTENT_API_KEY,
},
})
.pipe(
map((response) => {
return { pages: [rewriteGhostImgUrl(this.configService, response.data.pages[0])] };
}),
catchError((err) => {
this.logger.error(err);
throw new HttpException('Page not found:' + err, HttpStatus.NOT_FOUND);
})
);
}
}
import { HttpModule, Module } from '@nestjs/common';
import { PagesController } from './pages.controller';
@Module({
imports: [HttpModule],
controllers: [PagesController],
})
export class PagesModule {}
export class Page {
id: string;
uuid: string;
title: string;
slug: string;
html: string;
comment_id: string;
feature_image: string;
featured: false;
visibility: string;
email_recipient_filter: string;
created_at: string;
updated_at: string;
published_at: string;
url: string;
excerpt: string;
custom_excerpt: string;
reading_time: string;
access: boolean;
send_email_when_published: boolean;
og_image: string;
og_title: string;
og_description: string;
twitter_image: string;
twitter_title: string;
twitter_description: string;
meta_title: string;
meta_description: string;
email_subject: string;
codeinjection_head: string;
codeinjection_foot: string;
custom_template: string;
canonical_url: string;
tags: [];
authors: [];
primary_author: [];
primary_tag: [];
frontmatter: [];
}
......@@ -5,6 +5,7 @@ import { ConfigurationService } from '../configuration/configuration.service';
import { TagEnum } from './enums/tag.enum';
import { Post } from './schemas/post.schema';
import { Tag } from './schemas/tag.schema';
import { rewriteGhostImgUrl } from '../shared/utils';
@Injectable()
export class PostsService {
......@@ -74,15 +75,7 @@ export class PostsService {
}
// Handle image display. Rewrite image URL to fit ghost infra issue.
if (!this.configService.isLocalConf()) {
if (postData.feature_image) {
postData.feature_image = `https://${this.configService.config.host}/blog/content${
postData.feature_image.split('/content')[1]
}`;
}
const regex = /(https?:\/\/ghost):(\d*)?/g;
postData.html = postData.html.replace(regex, `https://${this.configService.config.host}/blog`);
}
postData = rewriteGhostImgUrl(this.configService, postData);
return postData;
}
}
import { ConfigurationService } from '../configuration/configuration.service';
import { Page } from '../pages/schemas/page.schema';
import { Post } from '../posts/schemas/post.schema';
export function rewriteGhostImgUrl(configService: ConfigurationService, itemData: Page | Post): Page | Post {
// Handle image display. Rewrite image URL to fit ghost infra issue.
if (!configService.isLocalConf()) {
if (itemData.feature_image) {
itemData.feature_image = `https://${configService.config.host}/blog/content${
itemData.feature_image.split('/content')[1]
}`;
}
const regex = /(https?:\/\/ghost):(\d*)?/g;
itemData.html = itemData.html.replace(regex, `https://${configService.config.host}/blog`);
}
return itemData;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment