Upload All
This commit is contained in:
16
models/renderModels/change_password.js
Normal file
16
models/renderModels/change_password.js
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
const User = require("../mongooseSchemas/User");
|
||||
|
||||
/**
|
||||
* 頁面「更變密碼」的插值函式。
|
||||
* @param {BasicLayout} renderData 基本插值物件。
|
||||
* @param {string} route 路由路徑。
|
||||
* @param {Express.Session} session Express的Session物件。
|
||||
* @param {CallbackFunction} callback 回呼函式。
|
||||
*/
|
||||
function ChangePasswordRender(renderData, route, session, callback) {
|
||||
// 頁面「更變密碼」不需要任何插值。
|
||||
callback(null, true);
|
||||
}
|
||||
|
||||
module.exports.Render = ChangePasswordRender;
|
72
models/renderModels/drawing.js
Normal file
72
models/renderModels/drawing.js
Normal file
@ -0,0 +1,72 @@
|
||||
|
||||
const User = require("../mongooseSchemas/User");
|
||||
const Painting = require("../mongooseSchemas/Painting");
|
||||
const Participation = require("../mongooseSchemas/Participation");
|
||||
|
||||
/** 預設的新畫作基本資料。 */
|
||||
const defaultPaintingData = {
|
||||
id: null,
|
||||
paintingName: "",
|
||||
description: "",
|
||||
tags: [],
|
||||
viewAuthority: 0,
|
||||
createdTime: "無",
|
||||
lastModified: "無",
|
||||
activity: null,
|
||||
isFinished: false,
|
||||
isLocked: false
|
||||
};
|
||||
|
||||
/**
|
||||
* 頁面「繪圖創作」的插值函式。
|
||||
* @param {BasicLayout} renderData 基本插值資料物件。
|
||||
* @param {string} route 路由路徑。
|
||||
* @param {Express.Session} session Express的Session物件。
|
||||
* @param {CallbackFunction} callback 回呼函式。
|
||||
*/
|
||||
function DrawingRender(renderData, route, session, callback) {
|
||||
let paintingId = route.substr(9);
|
||||
let datas = renderData.datas;
|
||||
// 若使用者有登入
|
||||
if (renderData.hasLogin) {
|
||||
// 先尋找使用者資料
|
||||
User.findOne({"username": renderData.username})
|
||||
.select("autoSaveEnable tags paintings")
|
||||
.populate({ path: "paintings", select: { "id": 1 } })
|
||||
.exec((err, userDocs) => {
|
||||
if (err) return callback(err, null);
|
||||
// 先將使用者的自動儲存設定、所有標填上
|
||||
datas.isAutoSave = userDocs.autoSaveEnable;
|
||||
datas.userTags = userDocs.tags;
|
||||
|
||||
// 若沒有指定圖畫ID,將預設的畫作資料填上後回呼。
|
||||
if (!paintingId) {
|
||||
datas.painting = defaultPaintingData;
|
||||
return callback(null, true);
|
||||
}
|
||||
|
||||
// 若該圖畫不屬於使用者的話,就回乎錯誤Error_PaintingNotExist。
|
||||
if (!userDocs.IsPaintingsOwner(paintingId)) {
|
||||
return callback(Painting.GetError_PaintingNotExist(), null);
|
||||
}
|
||||
|
||||
// 若該圖畫屬於使用者,則將目標畫作的基本資料填入,然後回呼true。
|
||||
Painting.GetDrawingPageInfoById(paintingId, (err, paintingInfo) => {
|
||||
if (err) return callback(err, null);
|
||||
datas.painting = paintingInfo;
|
||||
callback(null, true);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
// 若使用者沒有登入
|
||||
else {
|
||||
datas.isAutoSave = false;
|
||||
datas.userTags = [];
|
||||
datas.painting = defaultPaintingData;
|
||||
callback(null, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports.Render = DrawingRender;
|
26
models/renderModels/edit_personal_info.js
Normal file
26
models/renderModels/edit_personal_info.js
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
const User = require("../mongooseSchemas/User");
|
||||
|
||||
/**
|
||||
* 頁面「編輯個人資料」的插值函式。
|
||||
* @param {BasicLayout} renderData 插值物件。
|
||||
* @param {string} route 路由路徑。
|
||||
* @param {Express.Session} session Express的Session物件。
|
||||
* @param {CallbackFunction} callback 回呼函式。
|
||||
*/
|
||||
function EditPersonalInfoRender(renderData, route, session, callback) {
|
||||
User.findOne({"username": renderData.username})
|
||||
.exec((err, userDocs) => {
|
||||
if (err) return callback(err, null);
|
||||
if (userDocs) {
|
||||
renderData.datas = userDocs.personalInfo;
|
||||
callback(null, true);
|
||||
}
|
||||
else {
|
||||
callback(User.Error_UserNotExist(), null);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
module.exports.Render = EditPersonalInfoRender;
|
27
models/renderModels/feedback.js
Normal file
27
models/renderModels/feedback.js
Normal file
@ -0,0 +1,27 @@
|
||||
var User = require("../mongooseSchemas/User");
|
||||
/**
|
||||
* 頁面「意見回饋」的插值函式。
|
||||
* @param {BasicLayout} renderData 基本插值物件。
|
||||
* @param {string} route 路由路徑。
|
||||
* @param {Express.Session} session Express的Session物件。
|
||||
* @param {CallbackFunction} callback 回呼函式。
|
||||
*/
|
||||
function FeedBackRender(renderData, route, session, callback) {
|
||||
renderData.datas.currentDate = (new Date()).toLocaleDateString();
|
||||
// 如果使用者沒有登入,則設定hasPostFeedback為false,並呼叫回呼函式。
|
||||
if (!renderData.hasLogin) {
|
||||
renderData.datas.hasPostFeedback = false;
|
||||
callback(null, true);
|
||||
return;
|
||||
}
|
||||
// 如果使用者有登入,則尋找資料庫中指定的使用者資料的「hasPostFeedback」欄位。
|
||||
User.findOne({ "username": renderData.username }).select("hasPostFeedback").exec(function (err, userDoc) {
|
||||
if (err) {
|
||||
callback(err, null);
|
||||
return;
|
||||
}
|
||||
renderData.datas.hasPostFeedback = userDoc.hasPostFeedback;
|
||||
callback(null, true);
|
||||
});
|
||||
}
|
||||
module.exports.Render = FeedBackRender;
|
30
models/renderModels/gallery.js
Normal file
30
models/renderModels/gallery.js
Normal file
@ -0,0 +1,30 @@
|
||||
let PaintingSpotlight = require("../mongooseSchemas/PaintingSpotlight");
|
||||
let Season = require("../mongooseSchemas/Season");
|
||||
/**
|
||||
* 頁面「傑作藝廊」的插值函式。
|
||||
* @param {BasicLayout} renderData 基本插值物件。
|
||||
* @param {string} route 路由路徑。
|
||||
* @param {Express.Session} session Express的Session物件。
|
||||
* @param {CallbackFunction} callback 回呼函式。傳回錯誤訊息或資料插值設定是否成功。
|
||||
*/
|
||||
function GalleryRender(renderData, route, session, callback) {
|
||||
// 先取得傑作藝廊中的精選輯
|
||||
PaintingSpotlight.GetCarouselInfo("gallery", function (err, carouselInfo) {
|
||||
if (err || !carouselInfo) {
|
||||
callback(err, null);
|
||||
return;
|
||||
}
|
||||
renderData.datas.paintings = carouselInfo.paintings;
|
||||
// 再取得活動相關的訊息
|
||||
Season.GetGalleryNeedInfo(function (err, seasonsInfo) {
|
||||
if (err) {
|
||||
callback(err, null);
|
||||
}
|
||||
else {
|
||||
renderData.datas.seasons = seasonsInfo;
|
||||
callback(err, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
module.exports.Render = GalleryRender;
|
20
models/renderModels/index.js
Normal file
20
models/renderModels/index.js
Normal file
@ -0,0 +1,20 @@
|
||||
var PaintingSpotlight = require("../mongooseSchemas/PaintingSpotlight");
|
||||
/**
|
||||
* 取得首頁的插值資料。
|
||||
* @param {BasicLayout} renderData 插值物件。
|
||||
* @param {string} route 路由路徑。
|
||||
* @param {Express.Session} session Express的Session物件。
|
||||
* @param {CallbackFunction} callback 回呼函式。
|
||||
*/
|
||||
function IndexRender(renderData, route, session, callback) {
|
||||
PaintingSpotlight.GetCarouselInfo("index", function (err, infos) {
|
||||
if (err) {
|
||||
callback(err, null);
|
||||
}
|
||||
else {
|
||||
renderData.datas = infos;
|
||||
callback(null, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
module.exports.Render = IndexRender;
|
12
models/renderModels/login.js
Normal file
12
models/renderModels/login.js
Normal file
@ -0,0 +1,12 @@
|
||||
/**
|
||||
* 頁面「登入」的插值函式。
|
||||
* @param {BasicLayout} renderData 基本插值物件。
|
||||
* @param {string} route 路由路徑。
|
||||
* @param {Express.Session} session Express的Session物件。
|
||||
* @param {CallbackFunction} callback 回呼函式。
|
||||
*/
|
||||
function LoginRender(renderData, route, session, callback) {
|
||||
// 登入頁面目前不需要做任何插值
|
||||
callback(null, true);
|
||||
}
|
||||
module.exports.Render = LoginRender;
|
101
models/renderModels/message_form.js
Normal file
101
models/renderModels/message_form.js
Normal file
@ -0,0 +1,101 @@
|
||||
|
||||
/**
|
||||
* 設定轉跳訊息頁面下的插值處理。
|
||||
* @param {BasicLayout} renderData 插值物件。
|
||||
* @param {string} route 路由路徑。
|
||||
* @param {Express.Session} session Express的Session物件。
|
||||
* @param {CallbackFunction} callback 回呼函式。
|
||||
*/
|
||||
function MessageFormRender(renderData, route, session, callback) {
|
||||
switch(true) {
|
||||
case route == "/signupmsg": // 註冊成功的轉跳頁面
|
||||
renderData.datas.title = "註冊成功!";
|
||||
renderData.datas.content = "您現在可以用您所註冊的帳號登入了!";
|
||||
renderData.datas.button1 = "登入";
|
||||
renderData.datas.script = "$('#btnAction1').on('click', () => window.location.replace('/login'));";
|
||||
break;
|
||||
case route.includes("/homenotexist/"): // 在「個人頁面」下找不到目標使用者時的轉跳頁面
|
||||
// 確認使用者名稱存在於路徑中
|
||||
if (route.length > 14) {
|
||||
let username = route.substr(14); // 取得找不到的「使用者名稱」
|
||||
renderData.datas.title = "找不到 “" + username + "” 的個人頁面!"
|
||||
}
|
||||
else {
|
||||
renderData.datas.title = "找不到指定使用者的個人頁面!";
|
||||
}
|
||||
renderData.datas.content = "很抱歉,您所尋找的使用者並不存在!";
|
||||
renderData.datas.button1 = "首頁";
|
||||
renderData.datas.script = "$('#btnAction1').on('click', () => window.location.replace('/'));";
|
||||
break;
|
||||
|
||||
case route == "/personalinfo_updated": // 在「編輯個人資料」頁面下,成功編輯個人資料後的跳轉提示頁面
|
||||
renderData.datas.title = "個人資料修改成功!";
|
||||
renderData.datas.content = "您的個人資料已成功更新!";
|
||||
renderData.datas.button1 = "返回";
|
||||
renderData.datas.script = "$('#btnAction1').on('click', () => window.location.replace('/home/" + renderData.username + "'));";
|
||||
break;
|
||||
|
||||
case route == "/send_sitemail_successfully": // 在「撰寫站內訊息」頁面下,成功編輯個人資料後的轉跳頁面。
|
||||
renderData.datas.title = "站內信發送成功!";
|
||||
renderData.datas.content = "您的站內信件已成功寄送!";
|
||||
renderData.datas.button1 = "返回";
|
||||
renderData.datas.script = "$('#btnAction1').on('click', () => window.location.replace('/home/" + renderData.username + "'));";
|
||||
break;
|
||||
case route == "/newpw_success": // 在「更變密碼」頁面下,成功更改密碼後的轉跳頁面。
|
||||
renderData.datas.title = "密碼更改成功!";
|
||||
renderData.datas.content = "您的密碼已經更改成功!";
|
||||
renderData.datas.button1 = "返回";
|
||||
renderData.datas.script = "$('#btnAction1').on('click', () => window.location.replace('/home/" + renderData.username + "'));";
|
||||
break;
|
||||
case route == "/painting_finished": // 在「繪圖創作」頁面下,成功完成畫作之後的跳轉頁面。
|
||||
renderData.datas.title = "作品已完成!";
|
||||
renderData.datas.content = "您的畫作已經完成,您可以返回去欣賞您的畫作!";
|
||||
renderData.datas.button1 = "返回";
|
||||
renderData.datas.script = "$('#btnAction1').on('click', () => window.location.replace('/home/" + renderData.username + "'));";
|
||||
break;
|
||||
case route == "/painting_not_exist": // 在找不到指定的圖畫作品之下的跳轉頁面
|
||||
renderData.datas.title = "找不到您的圖畫作品!";
|
||||
renderData.datas.content = "請確認您所指定的圖畫作品是否所屬於您,或是該作品是否存在。";
|
||||
renderData.datas.button1 = "返回";
|
||||
renderData.datas.script = "$('#btnAction1').on('click', () => window.location.replace('/home/" + renderData.username + "'));";
|
||||
break;
|
||||
case route == "/painting_deleted": // 成功刪除指定圖畫作品後的跳轉頁面
|
||||
renderData.datas.title = "圖畫作品刪除成功!";
|
||||
renderData.datas.content = "您指定的圖畫作品已刪除成功!" + (session.paintingDeleted_Activity ? "請注意,投稿至活動上的圖畫不會被刪除!" : "");
|
||||
renderData.datas.button1 = "返回";
|
||||
renderData.datas.script = "$('#btnAction1').on('click', () => window.location.replace('/home/" + renderData.username + "'));";
|
||||
// 若有被標記,則刪除paintingDeleted_Activity
|
||||
if (session.paintingDeleted_Activity)
|
||||
delete session.paintingDeleted_Activity;
|
||||
break;
|
||||
case route == "/newtheme/successful": // 當成功處理了「投稿新主題」後的轉跳頁面
|
||||
renderData.datas.title = "新主題投稿成功!";
|
||||
renderData.datas.content = "您所投稿的新主題已成功地上傳!請等待最新一季的結果。";
|
||||
renderData.datas.button1 = "返回首頁";
|
||||
renderData.datas.script = "$('#btnAction1').on('click', () => window.location.replace('/'));";
|
||||
break;
|
||||
case route == "/newtheme": // 當使用者在「投稿主題」路由上,但卻已經有投稿過主題的轉跳頁面
|
||||
renderData.datas.title = "您已經投稿過新主題了!";
|
||||
renderData.datas.content = "請等待下一次的「投稿主題」活動再進行發起主題的動作!。";
|
||||
renderData.datas.button1 = "返回";
|
||||
renderData.datas.script = "$('#btnAction1').on('click', () => window.history.back() );";
|
||||
break;
|
||||
case route == "/votetheme/success": // 當成功處理了「主題票選」後的轉跳頁面
|
||||
renderData.datas.title = "候選主題投票成功!";
|
||||
renderData.datas.content = "您的投票資料已經成功送出!請等待下一次的主題投票。";
|
||||
renderData.datas.button1 = "返回首頁";
|
||||
renderData.datas.script = "$('#btnAction1').on('click', () => window.location.replace('/'));";
|
||||
break;
|
||||
case route == "/votetheme": // 當使用者在「主題票選」路由上,但卻已經有票選過主題之後的跳轉頁面
|
||||
renderData.datas.title = "您已經對候選主題票選過了!";
|
||||
renderData.datas.content = "請等待下一次的「主題票選」活動再進行主題票選的動作!";
|
||||
renderData.datas.button1 = "返回";
|
||||
renderData.datas.script = "$('#btnAction1').on('click', () => window.history.back() );";
|
||||
break;
|
||||
default: // 其他未定義的伺服器訊息
|
||||
return callback(new Error("未定義的對應伺服訊息插值資料。"), null);
|
||||
}
|
||||
return callback(false, true);
|
||||
}
|
||||
|
||||
module.exports.Render = MessageFormRender;
|
89
models/renderModels/personal_page.js
Normal file
89
models/renderModels/personal_page.js
Normal file
@ -0,0 +1,89 @@
|
||||
|
||||
const User = require("../mongooseSchemas/User");
|
||||
const Painting = require("../mongooseSchemas/Painting");
|
||||
const SiteMail = require("../mongooseSchemas/SiteMail");
|
||||
|
||||
/**
|
||||
* 取得「個人頁面」的插值資料。
|
||||
* @param {BasicLayout} renderData 插值物件。
|
||||
* @param {string} route 路由路徑。
|
||||
* @param {Express.Session} session Express的Session物件。
|
||||
* @param {CallbackFunction} callback 回呼函式。
|
||||
*/
|
||||
function PersonalPageRender(renderData, route, session, callback) {
|
||||
let paramUsername = route.substr(6); // 取得路徑中所選的目標個人頁面的使用者名稱
|
||||
|
||||
// 初步地檢查是否為合法的使用者名稱長度。若是,則進一步地去查詢相關資料
|
||||
if (4 <= paramUsername.length && paramUsername.length <= 16) {
|
||||
// 定義Populate Query : 套用的有paintings, siteMsg, siteMail 與 friendList
|
||||
let populateQuery = [
|
||||
{ path: "paintings", select: { "id": 1, "name": 1, "links": 1, "description": 1, "viewAuthority": 1, "tags": 1 } },
|
||||
{ path: "friendList", select: { "username": 1 } },
|
||||
{ path: "siteMsg.refId" },
|
||||
{ path: "siteMail" }
|
||||
];
|
||||
|
||||
// 嘗試尋找指定的使用者,並做populate來取得相關連的資料
|
||||
User.findOne({"username": paramUsername})
|
||||
.populate(populateQuery)
|
||||
.exec((err, userDocs) => {
|
||||
// 如果找到使用者的話,將需要的資料填入插值物件中
|
||||
if (userDocs) {
|
||||
// 定義 datas 物件,並將一些不需要過濾的資料先加入
|
||||
let datas = {
|
||||
isOwner: (renderData.username == userDocs.username), // 若目前正瀏覽的使用者與目標使用者的相同,則為這個個人頁面的擁有者(true),反之為否(false)
|
||||
username: userDocs.username, // 使用者名稱
|
||||
nickname: userDocs.personalInfo.nickname, // 暱稱
|
||||
motto: userDocs.personalInfo.motto, // 短言
|
||||
userPhotoURL: userDocs.personalInfo.photo, // 個人相片 (連結路徑)
|
||||
autoSaveEnable: userDocs.autoSaveEnable, // 自動儲存
|
||||
userTags: userDocs.tags, // 作品標籤
|
||||
friendList: [], // 好友清單
|
||||
siteMsg: [], // 網站訊息
|
||||
paintings: [], // 作品集
|
||||
siteMail: null // 站內信
|
||||
};
|
||||
|
||||
// 循環將 friendList 加入 datas 中
|
||||
for (let i = 0, list = userDocs.friendList; i < list.length; i++)
|
||||
datas.friendList.push(list[i].username);
|
||||
|
||||
// 循環將 siteMail 加入 datas 中
|
||||
for (let list = userDocs.siteMsg, i = list.length - 1; i >= 0; i--) {
|
||||
// 若為伺服訊息,則引用連接的伺服訊息資料
|
||||
if (list[i].isServerMessage) {
|
||||
datas.siteMsg.push({ title: list[i].refId.title, content: list[i].refId.content });
|
||||
}
|
||||
else {
|
||||
datas.siteMsg.push({ title: list[i].title, content: list[i].content });
|
||||
}
|
||||
}
|
||||
|
||||
datas.siteMail = userDocs.siteMail; // 將 siteMail 加入 datas 中
|
||||
|
||||
// 循環將 paintings 加入 datas 中
|
||||
let isFriend = userDocs.IsUsersFriend(session.passport.user); // 取得目前使用者對目標使用者而言的權限
|
||||
for (let i = 0, list = userDocs.paintings; i < list.length; i++) {
|
||||
// 若 觀看權限為「公開」 或 使用者為目標使用者的朋友且觀看權限為「半公開」 或 該使用者即為擁有者,則將幅畫資訊加入
|
||||
if (list[i].viewAuthority == 0 || isFriend && list[i].viewAuthority == 1 || datas.isOwner) {
|
||||
datas.paintings.push(list[i]);
|
||||
}
|
||||
}
|
||||
|
||||
renderData.datas = datas; // 最後將 datas 複寫至 renderData.datas
|
||||
callback(null, true);
|
||||
}
|
||||
// 若沒有找到,則將錯誤「找不到目標使用者」回呼至上層路由
|
||||
else {
|
||||
callback(User.Error_UserNotExist(), null);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
// 如果不為合法的使用者名稱長度,則將錯誤「找不到目標使用者」回呼至上層路由。
|
||||
else {
|
||||
callback(User.Error_UserNotExist(), null);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.Render = PersonalPageRender;
|
166
models/renderModels/showcase.js
Normal file
166
models/renderModels/showcase.js
Normal file
@ -0,0 +1,166 @@
|
||||
|
||||
const User = require("../mongooseSchemas/User");
|
||||
const Painting = require("../mongooseSchemas/Painting");
|
||||
const ParticipantInfo = require("../mongooseSchemas/ParticipantInfo");
|
||||
|
||||
/**
|
||||
* 以主使用者與被檢查的使用者之間的關係,取得觀看權限數值。
|
||||
* @param {User} mainUser 主要使用者的資料。
|
||||
* @param {string} otherUser_id 目標要查詢的使用者_id。
|
||||
* @return {number} 觀看權限數值。
|
||||
*/
|
||||
function GetAuthorityNumber(mainUser, otherUser_id) {
|
||||
if (mainUser._id.equals(otherUser_id)) { // 若mainUser與otherUser為同一人,則回傳2。
|
||||
return 2;
|
||||
}
|
||||
else if (mainUser.IsUsersFriend(otherUser_id)) { // 若otherUser為mainUser的好友,則回傳1。
|
||||
return 1;
|
||||
}
|
||||
else { // 若不為好友,則回傳0。
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef PaintingRange
|
||||
* @prop {number} n 表示第n個區間。
|
||||
* @prop {string[]} range_id 表示在第n個區間中的圖畫Id。
|
||||
*/
|
||||
/**
|
||||
* 取得目標要尋找的圖畫Id座落的區間與該區間中第一個圖畫Id。
|
||||
* @param {Painting[]} list 全域的資料。為圖畫陣列。
|
||||
* @param {string} id 要尋找座落在哪區間的資料。為圖畫Id。
|
||||
* @param {number} viewAutho 觀看權限數值。
|
||||
* @return {PaintingRange} 回傳第N個區間整數與該區間中的第一個圖畫Id。為 {n, range_id}。
|
||||
*/
|
||||
function GetRangeIndex(list, id, viewAutho) {
|
||||
let viewList = list.filter((docs => docs.viewAuthority <= viewAutho)); // 用「訪問權限」與觀看權限數值,過濾原本的畫作清單,成新的畫作清單。
|
||||
let length = viewList.length; // 取得清單內容長度
|
||||
// 尋找目標畫作在清單中的索引位置
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (list[i].id == id) {
|
||||
// 以十個畫作為一組,取得目前在第n組
|
||||
let n = Math.floor(i / 10);
|
||||
|
||||
// 回傳n,與第n組中 (n * 10) ~ (n * 10 + 9) 之間的畫作_id清單。
|
||||
return { n: n, range_id: list.slice(n * 10, n * 10 + 10).filter(docs => docs._id) };
|
||||
}
|
||||
}
|
||||
// 若沒找到則回傳null。
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 為「個人藝廊」模式下的插值方法。
|
||||
* @param {BasicLayout} renderData 基本差值物件。
|
||||
* @param {string[]} params 路由路徑中的每項參數。
|
||||
* @param {Express.Session} session Express的Session物件。
|
||||
* @param {CallbackFunction} callback 回呼函式。
|
||||
*/
|
||||
function PersonalShowcaseRender(renderData, params, session, callback) {
|
||||
let username = params[2]; // 此個人藝廊的使用者
|
||||
let paintingId = params[3]; // 指定要瀏覽的畫作Id
|
||||
let tag = params[4]; // 指定畫作的標籤群組(可有可無)
|
||||
let datas = renderData.datas;
|
||||
|
||||
datas.isActivity = false;
|
||||
datas.themeTitle = null;
|
||||
datas.themeOriginator = null;
|
||||
datas.artist = username;
|
||||
datas.tag = tag;
|
||||
datas.paintings = []; // 建立畫作清單欄位
|
||||
|
||||
// 建立 Populate Query
|
||||
let populateQuery = {
|
||||
path: "paintings",
|
||||
select: { "id": 1, "viewAuthority": 1 },
|
||||
match: { "isLocked": false }
|
||||
};
|
||||
|
||||
// 如果有指定tag,則將其加入到populateQuery之中作為條件
|
||||
// 比對每個畫作的標籤中是否有包含tag。
|
||||
if (tag)
|
||||
populateQuery.match.tags = tag;
|
||||
|
||||
// 以username尋找目標使用者的資料,其中選取paintings欄位,然後再以populateQuery做畫作連結
|
||||
User.findOne({"username": username})
|
||||
.select("personalInfo.photo paintings friendList")
|
||||
.populate(populateQuery)
|
||||
.exec((err, userDocs) => {
|
||||
if (err) callback(err, null);
|
||||
if (!userDocs) callback(User.Error_UserNotExist(), null); // 若找不到使用者,則帶著相對應的錯誤回呼
|
||||
let ownersPhotoURL = userDocs.personalInfo.photo; // 擁有此展示藝廊的人的頭像照片
|
||||
let paintingList = userDocs.paintings; // 取得畫作Id清單
|
||||
let viewAuth = GetAuthorityNumber(userDocs, session.passport.user); // 取得觀看權限數值
|
||||
let rangeInfo = GetRangeIndex(paintingList, paintingId, viewAuth); // 取得區間資料
|
||||
|
||||
// 如果目標畫作不在清單之中的話,則回呼錯誤 **仍要再改
|
||||
if (!rangeInfo) return callback(new Error("找不到對應的畫作。"), null);
|
||||
|
||||
// 以區間尋找畫作資訊
|
||||
Painting.find({ "_id": { $in: rangeInfo.range_id } })
|
||||
.populate([{path: "ratings"}, {path: "comments"}])
|
||||
.exec((err, paintingDocs) => {
|
||||
if (err) callback(err, null);
|
||||
|
||||
// 循每一個畫作資料,取其中的欄位資料加入至 datas.paintings 中
|
||||
paintingDocs.forEach((docs) => {
|
||||
datas.paintings.push({
|
||||
id: docs.id,
|
||||
links: docs.links,
|
||||
name: docs.name,
|
||||
description: docs.description,
|
||||
artistInfo: { name: username, photoURL: ownersPhotoURL},
|
||||
totalScore: docs.totalScore,
|
||||
userScore: docs.FindRatingScoreByUsername(renderData.username),
|
||||
comments: docs.comments
|
||||
});
|
||||
});
|
||||
|
||||
// 最後,取得當前使用者的個人照片
|
||||
User.findOne({"_id": session.passport.user}, "personalInfo.photo", (err, guestUserDocs) => {
|
||||
if (err) return callback(err, null);
|
||||
if (!guestUserDocs) return callback(User.Error_UserNotExist(), null);
|
||||
datas.photoURL = guestUserDocs.personalInfo.photo; // 取得當前使用者的個人照片
|
||||
|
||||
callback(null, true);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 為「活動藝廊」模式下的差值方法。
|
||||
* @param {BasicLayout} renderData 基本插值物件。
|
||||
* @param {string[]} params 路由路徑中的每項參數。
|
||||
* @param {Express.Session} session Express的Session物件。
|
||||
* @param {CallbackFunction} callback 回呼函式。
|
||||
*/
|
||||
function ActivityShowcaseRender(renderData, params, session, callback) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {BasicLayout} renderData 基本插值物件。
|
||||
* @param {string} route 路由路徑。
|
||||
* @param {Express.Session} session Express的Session物件。
|
||||
* @param {CallbackFunction} callback 回呼函式。
|
||||
*/
|
||||
function ShowcaseRender(renderData, route, session, callback) {
|
||||
let params = route.split("/").slice(1);
|
||||
switch(params[1]) {
|
||||
case "personal":
|
||||
PersonalShowcaseRender(renderData, params, session, callback);
|
||||
break;
|
||||
case "activity":
|
||||
ActivityShowcaseRender(renderData, params, session, callback);
|
||||
break;
|
||||
default:
|
||||
callback(new Error("未定義的展示藝廊模式。"), null);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.Render = ShowcaseRender;
|
12
models/renderModels/signup.js
Normal file
12
models/renderModels/signup.js
Normal file
@ -0,0 +1,12 @@
|
||||
/**
|
||||
* 頁面「註冊」的插值函式。
|
||||
* @param {BasicLayout} renderData 基本插值物件。
|
||||
* @param {string} route 路由路徑。
|
||||
* @param {Express.Session} session Express的Session物件。
|
||||
* @param {CallbackFunction} callback 回呼函式。
|
||||
*/
|
||||
function SignUpRender(renderData, route, session, callback) {
|
||||
// 登入頁面目前不需要做任何插值
|
||||
callback(null, true);
|
||||
}
|
||||
module.exports.Render = SignUpRender;
|
14
models/renderModels/submit_theme.js
Normal file
14
models/renderModels/submit_theme.js
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
/**
|
||||
* 以基本插值資料、路由路徑做資料源,設定在submit_theme頁面下該插入什麼資料值。
|
||||
* @param {BasicLayout} renderData 插值物件。
|
||||
* @param {string} route 路由路徑。
|
||||
* @param {Express.Session} session Express的Session物件。
|
||||
* @param {CallbackFunction} callback 回呼函式。
|
||||
*/
|
||||
function SubmitThemeRender(renderData, route, session, callback) {
|
||||
// 此頁面不需要任何插值資料
|
||||
callback(null, true);
|
||||
}
|
||||
|
||||
module.exports.Render = SubmitThemeRender;
|
21
models/renderModels/theme.js
Normal file
21
models/renderModels/theme.js
Normal file
@ -0,0 +1,21 @@
|
||||
var Season = require("../mongooseSchemas/Season");
|
||||
/**
|
||||
* 頁面「畫作主題」的插值函式。
|
||||
* @param {BasicLayout} renderData 基本插值物件。
|
||||
* @param {string} route 路由路徑。
|
||||
* @param {Express.Session} session Express的Session物件。
|
||||
* @param {CallbackFunction} callback 回呼函式。
|
||||
*/
|
||||
function ThemeRender(renderData, route, session, callback) {
|
||||
// 先取得「畫作主題」頁面所需要的季資訊
|
||||
// 若出現錯誤,則回呼錯誤訊息;若取得成功,則回呼true已表示成功。
|
||||
Season.GetThemePageNeedInfo(function (err, seasonDatas) {
|
||||
if (err) {
|
||||
callback(err, null);
|
||||
return;
|
||||
}
|
||||
renderData.datas = seasonDatas;
|
||||
callback(null, true);
|
||||
});
|
||||
}
|
||||
module.exports.Render = ThemeRender;
|
42
models/renderModels/typescripts/feedback.ts
Normal file
42
models/renderModels/typescripts/feedback.ts
Normal file
@ -0,0 +1,42 @@
|
||||
|
||||
const User : any = require("../mongooseSchemas/User");
|
||||
|
||||
/**
|
||||
* 由DataRender所定義的基本差值物件。這裡僅列出必要的datas屬性與會用到的屬性。
|
||||
*/
|
||||
interface BasicLayout {datas: any, hasLogin: boolean, username: string}
|
||||
|
||||
/**
|
||||
* A Callback function.
|
||||
* @callback CallbackFunction
|
||||
* @param {Object} err 錯誤資訊物件。
|
||||
* @param {Object} obj 成功時所回傳的物件。
|
||||
*/
|
||||
interface CallbackFunction { (err: Object, obj: Object) : void }
|
||||
|
||||
/**
|
||||
* 頁面「意見回饋」的插值函式。
|
||||
* @param {BasicLayout} renderData 基本插值物件。
|
||||
* @param {CallbackFunction} callback 回呼函式。
|
||||
*/
|
||||
function FeedBackRender(renderData: BasicLayout, callback: CallbackFunction) : void
|
||||
{
|
||||
renderData.datas.currentDate = (new Date()).toLocaleDateString();
|
||||
// 如果使用者沒有登入,則設定hasPostFeedback為false,並呼叫回呼函式。
|
||||
if (!renderData.hasLogin) {
|
||||
renderData.datas.hasPostFeedback = false;
|
||||
callback(null, true);
|
||||
return;
|
||||
}
|
||||
// 如果使用者有登入,則尋找資料庫中指定的使用者資料的「hasPostFeedback」欄位。
|
||||
User.findOne({"username" : renderData.username}).select("hasPostFeedback").exec((err, property) => {
|
||||
if (err) {
|
||||
callback(err, null);
|
||||
return;
|
||||
}
|
||||
renderData.datas.hasPostFeedback = property;
|
||||
callback(null, true);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.Render = FeedBackRender;
|
94
models/renderModels/typescripts/gallery.ts
Normal file
94
models/renderModels/typescripts/gallery.ts
Normal file
@ -0,0 +1,94 @@
|
||||
|
||||
const PaintingSpotlight = require("../mongooseSchemas/PaintingSpotlight");
|
||||
const Season = require("../mongooseSchemas/Season");
|
||||
|
||||
/**
|
||||
* 由DataRender所定義的基本差值物件。這裡僅列出必要的datas屬性
|
||||
*/
|
||||
interface BasicLayout {datas: any}
|
||||
|
||||
/**
|
||||
* A Callback function.
|
||||
* @callback CallbackFunction
|
||||
* @param {Object} err 錯誤資訊物件。
|
||||
* @param {Object} obj 成功時所回傳的物件。
|
||||
*/
|
||||
interface CallbackFunction { (err: Object, obj: Object) : void }
|
||||
|
||||
/**
|
||||
* 繪圖展示上的簡短訊息。
|
||||
* @prop {string} links 此畫作的圖片連結。
|
||||
* @prop {string} name 此畫作的名稱。
|
||||
* @prop {string} description 此畫作的敘述。
|
||||
* @prop {string} artist 畫作的作者。
|
||||
*/
|
||||
interface PaintingInfo {
|
||||
links : string
|
||||
name : string,
|
||||
description : string,
|
||||
artist : string
|
||||
}
|
||||
|
||||
/**
|
||||
* 表示畫作的參賽相關訊息。
|
||||
* @prop {number} rank 此畫作的名次。
|
||||
* @prop {string} artist 畫作的作者。
|
||||
* @prop {string} paintingName 畫作的名稱。
|
||||
* @prop {Date} postTime 此畫作的參賽時間。
|
||||
*/
|
||||
interface ParticipantInfo {
|
||||
rank : number,
|
||||
artist : string,
|
||||
paintingName : string,
|
||||
postTime : Date
|
||||
}
|
||||
|
||||
/**
|
||||
* 有關主題的相關訊息。
|
||||
* @prop {number} order 主題與主題之間的識別號碼(用於版面先後排序用)。
|
||||
* @prop {string} title 主題的標題。
|
||||
* @prop {ParticipantInfo[]} participants 此主題的所有參賽畫作資訊。
|
||||
*/
|
||||
interface ThemeInfo {
|
||||
order : number,
|
||||
title : string,
|
||||
participants : ParticipantInfo[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 有關一季活動之中的相關訊息。
|
||||
* @prop {number} nth 表示目前是第nth季
|
||||
* @prop {ThemeInfo[]} 儲存這一季之中所有的活動。
|
||||
*/
|
||||
interface SeasonInfo {
|
||||
nth : number,
|
||||
themes : ThemeInfo[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 頁面「傑作藝廊」的插值函式。
|
||||
* @param {BasicLayout} renderData 基本插值物件。
|
||||
* @param {CallbackFunction} callback 回呼函式。傳回錯誤訊息或資料插值設定是否成功。
|
||||
*/
|
||||
function GalleryRender(renderData : BasicLayout, callback : CallbackFunction) : void {
|
||||
// 先取得傑作藝廊中的精選輯
|
||||
PaintingSpotlight.GetCarouselInfo("gallery", (err, carouselInfo) => {
|
||||
if (err || !carouselInfo) {
|
||||
callback(err, null);
|
||||
return;
|
||||
}
|
||||
renderData.datas.paintings = carouselInfo.paintings;
|
||||
// 再取得活動相關的訊息
|
||||
Season.GetGalleryNeedInfo((err, seasonsInfo) => {
|
||||
if (err){
|
||||
callback(err, null);
|
||||
}
|
||||
else {
|
||||
renderData.datas.seasons = seasonsInfo;
|
||||
callback(err, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.Render = GalleryRender;
|
50
models/renderModels/typescripts/index.ts
Normal file
50
models/renderModels/typescripts/index.ts
Normal file
@ -0,0 +1,50 @@
|
||||
let PaintingSpotlight = require("../mongooseSchemas/PaintingSpotlight");
|
||||
|
||||
/**
|
||||
* 由DataRender所定義的基本差值物件。這裡僅列出必要的datas屬性。
|
||||
*/
|
||||
interface BasicLayout {datas: any}
|
||||
|
||||
/**
|
||||
* A Callback function.
|
||||
* @callback CallbackFunction
|
||||
* @param {Object} err 錯誤資訊物件。
|
||||
* @param {Object} obj 成功時所回傳的物件。
|
||||
*/
|
||||
interface CallbackFunction { (err: Object, obj: Object) : void }
|
||||
|
||||
/**
|
||||
* Painting Information Interface
|
||||
*/
|
||||
interface PaintingInfo {
|
||||
links : string
|
||||
name : string,
|
||||
description : string,
|
||||
artist : string
|
||||
}
|
||||
|
||||
/**
|
||||
* Data layout of index.
|
||||
*/
|
||||
interface IndexLayout {
|
||||
paintings : PaintingInfo[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得首頁的插值資料。
|
||||
* @param {BasicLayout} renderData 插值物件。
|
||||
* @param {CallbackFunction} callback 回呼函式。
|
||||
*/
|
||||
function IndexRender(renderData : BasicLayout, callback : CallbackFunction) : void {
|
||||
PaintingSpotlight.GetCarouselInfo("index", (err, infos) => {
|
||||
if (err) {
|
||||
callback(err, null);
|
||||
}
|
||||
else {
|
||||
renderData.datas = infos;
|
||||
callback(null, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.Render = IndexRender;
|
26
models/renderModels/typescripts/login.ts
Normal file
26
models/renderModels/typescripts/login.ts
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
/**
|
||||
* 由DataRender所定義的基本差值物件。這裡僅列出必要的datas屬性。
|
||||
*/
|
||||
interface BasicLayout {datas: any}
|
||||
|
||||
/**
|
||||
* A Callback function.
|
||||
* @callback CallbackFunction
|
||||
* @param {Object} err 錯誤資訊物件。
|
||||
* @param {Object} obj 成功時所回傳的物件。
|
||||
*/
|
||||
interface CallbackFunction { (err: Object, obj: Object) : void }
|
||||
|
||||
/**
|
||||
* 頁面「登入」的插值函式。
|
||||
* @param {BasicLayout} renderData 基本插值物件。
|
||||
* @param {CallbackFunction} callback 回呼函式。
|
||||
*/
|
||||
function LoginRender(renderData: BasicLayout, callback: CallbackFunction) : void
|
||||
{
|
||||
// 登入頁面目前不需要做任何插值
|
||||
callback(null, true);
|
||||
}
|
||||
|
||||
module.exports.Render = LoginRender;
|
26
models/renderModels/typescripts/signup.ts
Normal file
26
models/renderModels/typescripts/signup.ts
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
/**
|
||||
* 由DataRender所定義的基本差值物件。這裡僅列出必要的datas屬性。
|
||||
*/
|
||||
interface BasicLayout {datas: any}
|
||||
|
||||
/**
|
||||
* A Callback function.
|
||||
* @callback CallbackFunction
|
||||
* @param {Object} err 錯誤資訊物件。
|
||||
* @param {Object} obj 成功時所回傳的物件。
|
||||
*/
|
||||
interface CallbackFunction { (err: Object, obj: Object) : void }
|
||||
|
||||
/**
|
||||
* 頁面「註冊」的插值函式。
|
||||
* @param {BasicLayout} renderData 基本插值物件。
|
||||
* @param {CallbackFunction} callback 回呼函式。
|
||||
*/
|
||||
function SignUpRender(renderData: BasicLayout, callback: CallbackFunction) : void
|
||||
{
|
||||
// 登入頁面目前不需要做任何插值
|
||||
callback(null, true);
|
||||
}
|
||||
|
||||
module.exports.Render = SignUpRender;
|
70
models/renderModels/typescripts/theme.ts
Normal file
70
models/renderModels/typescripts/theme.ts
Normal file
@ -0,0 +1,70 @@
|
||||
|
||||
const Season : any = require("../mongooseSchemas/Season");
|
||||
|
||||
/**
|
||||
* 由DataRender所定義的基本差值物件。這裡僅列出必要的datas屬性
|
||||
*/
|
||||
interface BasicLayout {datas: any}
|
||||
|
||||
/**
|
||||
* A Callback function.
|
||||
* @callback CallbackFunction
|
||||
* @param {Object} err 錯誤資訊物件。
|
||||
* @param {Object} obj 成功時所回傳的物件。
|
||||
*/
|
||||
interface CallbackFunction { (err: Object, obj: Object) : void }
|
||||
|
||||
/**
|
||||
* 有關主題的相關訊息。
|
||||
* @prop {number} order 主題與主題之間的識別號碼(用於版面先後排序用)。
|
||||
* @prop {string} title 主題的標題。
|
||||
* @prop {string} narrative 主題的敘述。
|
||||
* @prop {string} imageURL 主題縮圖連結。
|
||||
* @prop {string} originator 主題發起人。
|
||||
* @prop {number} participantCount 投稿人數。
|
||||
* @prop {number} views 瀏覽此主題的人次數。
|
||||
* @prop {number} commentCount 主題中相關留言人數。
|
||||
*/
|
||||
interface ThemeInfo {
|
||||
order : number,
|
||||
title : string,
|
||||
narrative : string,
|
||||
imageURL : string,
|
||||
originator : string,
|
||||
participantCount : number,
|
||||
views : number,
|
||||
commentCount : number
|
||||
}
|
||||
|
||||
/**
|
||||
* 有關一季活動之中的相關訊息。
|
||||
* @prop {number} nth 表示目前是第nth季
|
||||
* @prop {Date} endTime 表示該季的結束時間。在資料取得中只有上一季有這個欄位
|
||||
* @prop {ThemeInfo[]} 儲存這一季之中所有的活動。
|
||||
*/
|
||||
interface SeasonInfo {
|
||||
nth : number,
|
||||
endTime : Date,
|
||||
themes : ThemeInfo[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 頁面「畫作主題」的插值函式。
|
||||
* @param renderData 基本插值物件。
|
||||
* @param callback 回呼函式。
|
||||
*/
|
||||
function ThemeRender(renderData: BasicLayout, callback: CallbackFunction) : void
|
||||
{
|
||||
// 先取得「畫作主題」頁面所需要的季資訊
|
||||
// 若出現錯誤,則回呼錯誤訊息;若取得成功,則回呼true已表示成功。
|
||||
Season.GetThemePageNeedInfo((err, seasonDatas) => {
|
||||
if (err) {
|
||||
callback(err, null);
|
||||
return;
|
||||
}
|
||||
renderData.datas = seasonDatas;
|
||||
callback(null, true);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.Render = ThemeRender;
|
44
models/renderModels/vote_theme.js
Normal file
44
models/renderModels/vote_theme.js
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
const NewTheme = require("../mongooseSchemas/NewTheme");
|
||||
const ServerStatus = require("../../ServerStatus");
|
||||
|
||||
/**
|
||||
* 以基本插值資料、路由路徑做資料源,設定在write_message頁面下該插入什麼資料值。
|
||||
* @param {BasicLayout} renderData 基本插值物件。
|
||||
* @param {string} route 路由路徑。
|
||||
* @param {Express.Session} session Express的Session物件。
|
||||
* @param {CallbackFunction} callback 回呼函式。
|
||||
*/
|
||||
function VoteThemeRenderer(renderData, route, session, callback) {
|
||||
let status = ServerStatus.status; // 取得伺服器的狀態資料
|
||||
let datas = renderData.datas;
|
||||
|
||||
datas.title = "第" + (status.currentSeason + 1) + "季主題票選";
|
||||
datas.voteCount = status.voteCount; // 取得在主題候選之中,使用者的手中有多少票
|
||||
|
||||
// 將候選主題全部找出,並依建立時間來進行排列
|
||||
NewTheme.find({})
|
||||
.sort({ "createdTime": 1 })
|
||||
.exec((err, newThemeDocs) => {
|
||||
if (err) return callback(err, null);
|
||||
|
||||
// 將所有頁面所要的候選主題資料加入到 themes 中
|
||||
let themes = [];
|
||||
newThemeDocs.forEach((docs, index) => {
|
||||
themes.push({
|
||||
id: index,
|
||||
title: docs.title,
|
||||
narrative: docs.narrative,
|
||||
imageURL: docs.image,
|
||||
originator: docs.sponsor
|
||||
});
|
||||
});
|
||||
|
||||
// 隨後再將 themes 加入到 datas 之上
|
||||
datas.themes = themes;
|
||||
callback(null, true);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
module.exports.Render = VoteThemeRenderer;
|
35
models/renderModels/write_message.js
Normal file
35
models/renderModels/write_message.js
Normal file
@ -0,0 +1,35 @@
|
||||
|
||||
let User = require("../mongooseSchemas/User");
|
||||
|
||||
/**
|
||||
* 以基本插值資料、路由路徑做資料源,設定在write_message頁面下該插入什麼資料值。
|
||||
* @param {BasicLayout} renderData 插值物件。
|
||||
* @param {string} route 路由路徑。
|
||||
* @param {Express.Session} session Express的Session物件。
|
||||
* @param {CallbackFunction} callback 回呼函式。
|
||||
*/
|
||||
function WriteMessageRender(renderData, route, session, callback) {
|
||||
let populateQuery = { path: "friendList", select: { "username": 1 } }; // 建立Populate Query,連結好友清單中的「Username」欄位。
|
||||
let recipient = route.substr(15); // 取得指定的使用者
|
||||
|
||||
// 尋找目前使用者的好友清單內的所有好友的使用者名稱
|
||||
User.findOne({"username": renderData.username})
|
||||
.populate(populateQuery)
|
||||
.exec((err, docs) => {
|
||||
// 若有錯誤,則將錯誤回呼並返回
|
||||
if (err) return callback(err);
|
||||
|
||||
// 將收件者加入插值物件中
|
||||
renderData.datas.recipient = recipient;
|
||||
|
||||
// 循環取得好友清單所有的使用者名稱
|
||||
let friendsUsernames = [];
|
||||
for (let friend of docs.friendList)
|
||||
friendsUsernames.push(friend.username);
|
||||
renderData.datas.friendList = friendsUsernames;
|
||||
callback(null, true);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
module.exports.Render = WriteMessageRender;
|
Reference in New Issue
Block a user