Skip to content

Commit

Permalink
Recycle Bin implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
xtremespb committed Aug 10, 2021
1 parent ba286c7 commit fdf2462
Show file tree
Hide file tree
Showing 36 changed files with 859 additions and 60 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "zoia3",
"version": "3.1.94",
"version": "3.1.95",
"description": "ZOIA Content Management System",
"scripts": {
"config": "node ./dev/bin/config.js",
Expand Down
15 changes: 13 additions & 2 deletions src/modules/boilerplate/api/apiDelete.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,19 @@ export default () => ({
await utils.removeFiles(filesList, req.zoiaConfig);
// Remove images (from disk)
await utils.removeImages(imagesList, req.zoiaConfig);
// Delete requested IDs
const result = await this.mongo.db.collection(collectionName).deleteMany(queryDb);
let result;
if (req.body.recycle) {
result = await this.mongo.db.collection(collectionName).updateMany(queryDb, {
$set: {
deletedAt: new Date(),
}
}, {
upsert: false
});
} else {
// Delete requested IDs
result = await this.mongo.db.collection(collectionName).deleteMany(queryDb);
}
// Check result
if (!result || !result.acknowledged) {
response.deleteError();
Expand Down
6 changes: 5 additions & 1 deletion src/modules/boilerplate/api/apiList.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ export default () => ({
req.body.sortId = `${req.body.language}.${req.body.sortId}`;
break;
}
const query = {};
const query = {
deletedAt: {
$eq: null
}
};
if (req.body.searchText && req.body.searchText.length > 1) {
query.$or = [];
listData.search.map(c => {
Expand Down
43 changes: 43 additions & 0 deletions src/modules/boilerplate/api/apiRecycledDelete.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import moduleConfig from "../module.json";

export default () => ({
attachValidation: false,
async handler(req) {
const {
log,
response,
auth,
acl,
} = req.zoia;
// Check permissions
if (!auth.statusAdmin()) {
response.unauthorizedError();
return;
}
if (!acl.checkPermission(moduleConfig.id, "delete")) {
response.requestAccessDeniedError();
return;
}
const {
collectionName
} = req.zoiaModulesConfig[moduleConfig.id];
try {
const result = await this.mongo.db.collection(collectionName).deleteMany({
deletedAt: {
$ne: null
}
});
// Check result
if (!result || !result.acknowledged) {
response.deleteError();
return;
}
// Send "success" result
response.successJSON();
return;
} catch (e) {
log.error(e);
response.internalServerError(e.message);
}
}
});
71 changes: 71 additions & 0 deletions src/modules/boilerplate/api/apiRecycledList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import recycledListData from "./data/recycledList.json";
import moduleConfig from "../module.json";

export default () => ({
schema: {
body: recycledListData.schema
},
attachValidation: true,
async handler(req) {
const {
log,
response,
auth,
acl
} = req.zoia;
// Check permissions
if (!auth.statusAdmin()) {
response.unauthorizedError();
return;
}
// Validate form
if (req.validationError) {
log.error(null, req.validationError.message);
response.validationError(req.validationError);
return;
}
const {
collectionName
} = req.zoiaModulesConfig[moduleConfig.id];
try {
const options = {
sort: {},
projection: recycledListData.projection
};
const query = {
deletedAt: { $ne: null },
};
if (req.body.searchText && req.body.searchText.length > 1) {
query.$or = recycledListData.search.map(c => {
const sr = {};
sr[c] = {
$regex: req.body.searchText,
$options: "i"
};
return sr;
});
}
const count = await this.mongo.db.collection(collectionName).find(query, options).count();
const limit = req.body.itemsPerPage || req.zoiaConfig.commonTableItemsLimit;
options.limit = limit;
options.skip = (req.body.page - 1) * limit;
options.sort[req.body.sortId] = req.body.sortDirection === "asc" ? 1 : -1;
const data = (await this.mongo.db.collection(collectionName).find(query, options).toArray()).map(i => ({
_id: String(i._id),
deletedAt: !acl.checkPermission(moduleConfig.id, "read", i.deletedAt) ? "***" : i.deletedAt,
title: !acl.checkPermission(moduleConfig.id, "read", i.uid) ? "***" : i.uid,
}));
// Send response
response.successJSON({
data,
count,
limit,
pagesCount: Math.ceil(count / limit),
});
return;
} catch (e) {
log.error(e);
response.internalServerError(e.message);
}
}
});
78 changes: 78 additions & 0 deletions src/modules/boilerplate/api/apiRecycledRestore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import {
ObjectId
} from "mongodb";
import deleteData from "./data/delete.json";
import moduleConfig from "../module.json";

export default () => ({
schema: {
body: deleteData.root
},
attachValidation: true,
async handler(req) {
const {
log,
response,
auth,
acl
} = req.zoia;
// Check permissions
if (!auth.statusAdmin()) {
response.unauthorizedError();
return;
}
// Validate form
if (req.validationError) {
log.error(null, req.validationError.message);
response.validationError(req.validationError);
return;
}
const {
collectionName
} = req.zoiaModulesConfig[moduleConfig.id];
try {
// Build query
const queryDb = {
$or: req.body.ids.map(id => ({
_id: new ObjectId(id)
}))
};
// Get requested data
const dataDb = await this.mongo.db.collection(collectionName).find(queryDb, {
projection: {
uid: 1
}
}).toArray();
// Check permission
let allowed = true;
(dataDb || []).map(i => {
if (allowed && !acl.checkPermission(moduleConfig.id, "delete", i.uid)) {
allowed = false;
}
});
if (!allowed) {
response.requestAccessDeniedError();
return;
}
// Restore requested IDs
const result = await this.mongo.db.collection(collectionName).updateMany(queryDb, {
$set: {
deletedAt: null
}
}, {
upsert: false
});
// Check result
if (!result || !result.acknowledged) {
response.deleteError();
return;
}
// Send "success" result
response.successJSON();
return;
} catch (e) {
log.error(e);
response.internalServerError(e.message);
}
}
});
1 change: 1 addition & 0 deletions src/modules/boilerplate/api/apiSave.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export default () => ({
updateExtras.createdAt = new Date();
}
// Update database
delete data.id;
const update = await this.mongo.db.collection(collectionName).updateOne(id ? {
_id: new ObjectId(id)
} : {
Expand Down
3 changes: 3 additions & 0 deletions src/modules/boilerplate/api/data/delete.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
},
"minItems": 1,
"uniqueItems": true
},
"recycle": {
"type": ["boolean", "null"]
}
},
"required": ["ids"]
Expand Down
40 changes: 40 additions & 0 deletions src/modules/boilerplate/api/data/recycledList.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"schema": {
"type": "object",
"properties": {
"page": {
"type": "number",
"minimum": 1,
"maximum": 999999
},
"sortId": {
"type": "string",
"enum": ["title", "deletedAt"]
},
"sortDirection": {
"type": "string",
"enum": ["asc", "desc"]
},
"searchText": {
"type": "string",
"minLength": 0,
"maxLength": 128
},
"itemsPerPage": {
"type": "number",
"minimum": 0,
"maximum": 1000
},
"autoItemsPerPage": {
"type": "boolean"
}
},
"required": ["page", "sortId", "sortDirection"]
},
"projection": {
"_id": 1,
"uid": 1,
"deletedAt": 1
},
"search": ["uid"]
}
7 changes: 7 additions & 0 deletions src/modules/boilerplate/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@ import apiList from "./apiList";
import apiSave from "./apiSave";
import apiLoad from "./apiLoad";
import apiDelete from "./apiDelete";
import apiRecycledDelete from "./apiRecycledDelete";
import apiRecycledList from "./apiRecycledList";
import apiRecycledRestore from "./apiRecycledRestore";

import moduleConfig from "../module.json";

export default fastify => {
fastify.post(`/api/${moduleConfig.id}/list`, apiList());
fastify.post(`/api/${moduleConfig.id}/edit/save`, apiSave());
fastify.post(`/api/${moduleConfig.id}/edit/load`, apiLoad());
fastify.post(`/api/${moduleConfig.id}/edit/delete`, apiDelete());
fastify.post(`/api/${moduleConfig.id}/list/recycled`, apiRecycledList());
fastify.post(`/api/${moduleConfig.id}/edit/delete/restore`, apiRecycledRestore());
fastify.post(`/api/${moduleConfig.id}/edit/delete/recycled`, apiRecycledDelete());
};
4 changes: 3 additions & 1 deletion src/modules/boilerplate/config.dist.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
"collections": {
"boilerplate": {
"indexesAsc": ["id", "[language].title"],
"indexesDesc": ["id", "[language].title"]
"indexesDesc": ["id", "[language].title"],
"expires": 259200,
"expireField": "deletedAt"
}
}
},
Expand Down
1 change: 1 addition & 0 deletions src/modules/boilerplate/web/admin/dataList/index.marko
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ $ const token = cookies.get(`${out.global.siteId || "zoia3"}.authToken`);
sortId="title"
sortDirection="asc"
checkboxColumn=true
recycleBin=true
on-action-click("onActionClick")
on-top-button-click("onTopButtonClick")
i18n=out.global.i18n
Expand Down
4 changes: 3 additions & 1 deletion src/modules/core/config.dist.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
},
"registry": {
"indexesAsc": [],
"indexesDesc": []
"indexesDesc": [],
"expires": 259200,
"expireField": "deletedAt"
},
"rateLimit": {
"indexesAsc": [],
Expand Down
Loading

0 comments on commit fdf2462

Please sign in to comment.