Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added page navigation #209

Merged
merged 18 commits into from
Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"morgan": "^1.10.0",
"multer": "^1.4.2",
"nedb": "^1.8.0",
"node-cache": "^5.1.2",
"node-fetch": "^2.6.1",
"open-graph-scraper": "^4.9.0",
"twig": "^1.15.4",
Expand Down
10 changes: 8 additions & 2 deletions src/backend/controllers/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import Page, { PageData } from '../models/page';
import Alias from '../models/alias';
import PagesOrder from './pagesOrder';
import PageOrder from '../models/pageOrder';
import HttpException from "../exceptions/httpException";
import HttpException from '../exceptions/httpException';
import PagesFlatArray from '../models/pagesFlatArray';

type PageDataFields = keyof PageData;

Expand Down Expand Up @@ -195,6 +196,7 @@ class Pages {
pagesAvailable[index] = null;
pagesAvailable = Pages.removeChildren(pagesAvailable, item._id);
});
PagesFlatArray.regenerate();

return pagesAvailable;
}
Expand All @@ -221,6 +223,7 @@ class Pages {

alias.save();
}
await PagesFlatArray.regenerate();

return insertedPage;
} catch (e) {
Expand Down Expand Up @@ -264,6 +267,7 @@ class Pages {
Alias.markAsDeprecated(previousUri);
}
}
await PagesFlatArray.regenerate();

return updatedPage;
}
Expand All @@ -286,8 +290,10 @@ class Pages {

await alias.destroy();
}
const removedPage = page.destroy();
await PagesFlatArray.regenerate();

return page.destroy();
return removedPage;
}

/**
Expand Down
8 changes: 7 additions & 1 deletion src/backend/controllers/pagesOrder.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import PageOrder from '../models/pageOrder';
import Page from '../models/page';
import PagesFlatArray from '../models/pagesFlatArray';

/**
* @class PagesOrder
Expand Down Expand Up @@ -62,6 +63,7 @@ class PagesOrder {

order.push(childId);
await order.save();
await PagesFlatArray.regenerate();
}

/**
Expand All @@ -76,11 +78,13 @@ class PagesOrder {

oldParentOrder.remove(targetPageId);
await oldParentOrder.save();
await PagesFlatArray.regenerate();

const newParentOrder = await PageOrder.get(newParentId);

newParentOrder.push(targetPageId);
await newParentOrder.save();
await PagesFlatArray.regenerate();
}

/**
Expand Down Expand Up @@ -125,6 +129,7 @@ class PagesOrder {
pageOrder.order = Array.from(new Set([...pageOrder.order, ...unordered]));
pageOrder.putAbove(currentPageId, putAbovePageId);
await pageOrder.save();
await PagesFlatArray.regenerate();
}

/**
Expand All @@ -138,7 +143,8 @@ class PagesOrder {
throw new Error('Page with given id does not contain order');
}

return order.destroy();
await order.destroy();
await PagesFlatArray.regenerate();
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/backend/models/pageOrder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ class PageOrder {
*
* @param {string} pageId - identity of page
*/
public getPageBefore(pageId: string): string | null {
public getSubPageBefore(pageId: string): string | null {
if (this.order === undefined) {
return null;
}
Expand All @@ -204,7 +204,7 @@ class PageOrder {
*
* @param pageId - identity of page
*/
public getPageAfter(pageId: string): string | null {
public getSubPageAfter(pageId: string): string | null {
if (this.order === undefined) {
return null;
}
Expand Down
183 changes: 183 additions & 0 deletions src/backend/models/pagesFlatArray.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import Page from './page';
import PageOrder from './pageOrder';
import NodeCache from 'node-cache';

// Create cache for flat array
const cache = new NodeCache({ stdTTL: 120 });

const cacheKey = 'pagesFlatArray';

/**
* Element for pagesFlatArray
*/
export interface PagesFlatArrayData {
/**
* Page id
*/
id: string;

/**
* Page parent id
*/
parentId?: string;

/**
* id of parent with parent id '0'
*/
rootId: string;

/**
* Page level in sidebar view
*/
level: number;

/**
* Page title
*/
title: string;

/**
* Page uri
*/
uri?: string;
}

/**
* @class PagesFlatArray
slaveeks marked this conversation as resolved.
Show resolved Hide resolved
* @class PagesFlatArray model - flat array of pages, which are ordered like in sidebar
*/
class PagesFlatArray {
/**
* Returns pages flat array
*
* @param nestingLimit - number of flat array nesting, set null to dismiss the restriction, default nesting 2
* @returns {Promise<Array<PagesFlatArrayData>>}
*/
public static async get(nestingLimit: number | null = 2): Promise<Array<PagesFlatArrayData>> {
// Get flat array from cache
let arr = cache.get(cacheKey) as Array<PagesFlatArrayData>;

// Check is flat array consists in cache
if (!arr) {
arr = await this.regenerate();
}

if (!nestingLimit) {
return arr;
}

return arr.filter( (item) => item.level < nestingLimit );
}

/**
* Generates new flat array, saves it to cache, returns it
* Calls, when there is no pages flat array data in cache or when page or pageOrder data updates
*
* @returns {Promise<Array<PagesFlatArrayData>>}
*/
public static async regenerate(): Promise<Array<PagesFlatArrayData>> {
const pages = await Page.getAll();
const pagesOrders = await PageOrder.getAll();

let arr = new Array<PagesFlatArrayData>();

// Get root order
const rootOrder = pagesOrders.find( order => order.page == '0' );

// Check is root order is not empty
if (!rootOrder) {
return [];
}

for (const pageId of rootOrder.order) {
arr = arr.concat(this.getChildrenFlatArray(pageId, 0, pages,
pagesOrders));
}

// Save generated flat array to cache
cache.set(cacheKey, arr);

return arr;
}

/**
* Returns previous page
*
* @param pageId - page id
* @returns {Promise<PagesFlatArrayData | undefined>}
*/
public static async getPageBefore(pageId: string): Promise<PagesFlatArrayData | undefined> {
const arr = await this.get();

const pageIndex = arr.findIndex( (item) => item.id == pageId);

// Check if index is not the first
if (pageIndex && pageIndex > 0) {
// Return previous element from array
return arr[pageIndex - 1];
} else {
return;
}
}

/**
* Returns next page
*
* @param pageId - page id
* @returns {Promise<PagesFlatArrayData | undefined>}
*/
public static async getPageAfter(pageId: string): Promise<PagesFlatArrayData | undefined> {
const arr = await this.get();

const pageIndex = arr.findIndex( (item) => item.id == pageId );

// Check if index is not the last
if (pageIndex < arr.length -1) {
// Return next element from array
return arr[pageIndex + 1];
} else {
return;
}
}

/**
* Returns child pages array
*
* @param pageId - parent page id
* @param level - page level in sidebar
* @param pages - all pages
* @param orders - all page orders
* @returns {Promise<Array<PagesFlatArrayData>>}
*/
private static getChildrenFlatArray(pageId: string, level: number,
pages: Array<Page>, orders: Array<PageOrder>): Array<PagesFlatArrayData> {
let arr: Array<PagesFlatArrayData> = new Array<PagesFlatArrayData>();

const page = pages.find( item => item._id == pageId );

// Add element to child array
if (page) {
arr.push( {
id: page._id!,
level: level,
parentId: page._parent,
rootId: '0',
title: page.title!,
uri: page.uri,
} );
}

const order = orders.find(item => item.page == pageId);

if (order) {
for (const childPageId of order.order) {
arr = arr.concat(this.getChildrenFlatArray(childPageId, level + 1,
pages, orders));
}
}

return arr;
}
}

export default PagesFlatArray;
6 changes: 6 additions & 0 deletions src/backend/routes/aliases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Aliases from '../controllers/aliases';
import Pages from '../controllers/pages';
import Alias from '../models/alias';
import verifyToken from './middlewares/token';
import PagesFlatArray from '../models/pagesFlatArray';

const router = express.Router();

Expand Down Expand Up @@ -32,9 +33,14 @@ router.get('*', verifyToken, async (req: Request, res: Response) => {

const pageParent = await page.getParent();

const previousPage = await PagesFlatArray.getPageBefore(alias.id);
const nextPage = await PagesFlatArray.getPageAfter(alias.id);

res.render('pages/page', {
page,
pageParent,
previousPage,
nextPage,
config: req.app.locals.config,
});
}
Expand Down
4 changes: 2 additions & 2 deletions src/backend/routes/api/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,8 @@ router.delete('/page/:id', async (req: Request, res: Response) => {
}

const parentPageOrder = await PagesOrder.get(page._parent);
const pageBeforeId = parentPageOrder.getPageBefore(page._id);
const pageAfterId = parentPageOrder.getPageAfter(page._id);
const pageBeforeId = parentPageOrder.getSubPageBefore(page._id);
const pageAfterId = parentPageOrder.getSubPageAfter(page._id);

let pageToRedirect;

Expand Down
6 changes: 6 additions & 0 deletions src/backend/routes/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Pages from '../controllers/pages';
import PagesOrder from '../controllers/pagesOrder';
import verifyToken from './middlewares/token';
import allowEdit from './middlewares/locals';
import PagesFlatArray from '../models/pagesFlatArray';

const router = express.Router();

Expand Down Expand Up @@ -62,10 +63,15 @@ router.get('/page/:id', verifyToken, async (req: Request, res: Response, next: N

const pageParent = await page.parent;

const previousPage = await PagesFlatArray.getPageBefore(pageId);
const nextPage = await PagesFlatArray.getPageAfter(pageId);

res.render('pages/page', {
page,
pageParent,
config: req.app.locals.config,
previousPage,
nextPage,
});
} catch (error) {
res.status(404);
Expand Down
Loading