1
0
mirror of https://gitlab.com/comunic/comunicapiv2 synced 2024-11-26 07:19:23 +00:00

Can update group settings

This commit is contained in:
Pierre HUBERT 2019-12-26 13:49:17 +01:00
parent 7e28722574
commit 0c8ce5c922
10 changed files with 317 additions and 3 deletions

View File

@ -4,6 +4,10 @@ import { GroupsAccessLevel, GroupInfo, GroupVisibilityLevel, GroupPostsCreationL
import { GroupMembershipLevels } from "../entities/GroupMember"; import { GroupMembershipLevels } from "../entities/GroupMember";
import { time } from "../utils/DateUtils"; import { time } from "../utils/DateUtils";
import { LikesHelper, LikesType } from "../helpers/LikesHelper"; import { LikesHelper, LikesType } from "../helpers/LikesHelper";
import { GroupSettings } from "../entities/GroupSettings";
import { removeHTMLNodes, checkURL } from "../utils/StringUtils";
import { findKey } from "../utils/ArrayUtils";
import { checkVirtualDirectoryAvailability, VirtualDirType } from "../utils/VirtualDirsUtils";
/** /**
* Groups API controller * Groups API controller
@ -14,7 +18,7 @@ import { LikesHelper, LikesType } from "../helpers/LikesHelper";
/** /**
* API groups registration levels * API groups registration levels
*/ */
const GROUPS_REGISTRATION_LEVELS = []; const GROUPS_REGISTRATION_LEVELS = {};
GROUPS_REGISTRATION_LEVELS[GroupRegistrationLevel.OPEN_REGISTRATION] = "open"; GROUPS_REGISTRATION_LEVELS[GroupRegistrationLevel.OPEN_REGISTRATION] = "open";
GROUPS_REGISTRATION_LEVELS[GroupRegistrationLevel.MODERATED_REGISTRATION] = "moderated"; GROUPS_REGISTRATION_LEVELS[GroupRegistrationLevel.MODERATED_REGISTRATION] = "moderated";
GROUPS_REGISTRATION_LEVELS[GroupRegistrationLevel.CLOSED_REGISTRATION] = "closed"; GROUPS_REGISTRATION_LEVELS[GroupRegistrationLevel.CLOSED_REGISTRATION] = "closed";
@ -22,7 +26,7 @@ GROUPS_REGISTRATION_LEVELS[GroupRegistrationLevel.CLOSED_REGISTRATION] = "closed
/** /**
* API groups membership levels * API groups membership levels
*/ */
const GROUPS_MEMBERSHIP_LEVELS = []; const GROUPS_MEMBERSHIP_LEVELS = {};
GROUPS_MEMBERSHIP_LEVELS[GroupMembershipLevels.ADMINISTRATOR] = "administrator"; GROUPS_MEMBERSHIP_LEVELS[GroupMembershipLevels.ADMINISTRATOR] = "administrator";
GROUPS_MEMBERSHIP_LEVELS[GroupMembershipLevels.MODERATOR] = "moderator"; GROUPS_MEMBERSHIP_LEVELS[GroupMembershipLevels.MODERATOR] = "moderator";
GROUPS_MEMBERSHIP_LEVELS[GroupMembershipLevels.MEMBER] = "member"; GROUPS_MEMBERSHIP_LEVELS[GroupMembershipLevels.MEMBER] = "member";
@ -34,7 +38,7 @@ GROUPS_MEMBERSHIP_LEVELS[GroupMembershipLevels.VISITOR] = "visitor";
/** /**
* API groups visibility levels * API groups visibility levels
*/ */
const GROUPS_VISIBILITY_LEVELS = []; const GROUPS_VISIBILITY_LEVELS = {};
GROUPS_VISIBILITY_LEVELS[GroupVisibilityLevel.OPEN_GROUP] = "open"; GROUPS_VISIBILITY_LEVELS[GroupVisibilityLevel.OPEN_GROUP] = "open";
GROUPS_VISIBILITY_LEVELS[GroupVisibilityLevel.PRIVATE_GROUP] = "private"; GROUPS_VISIBILITY_LEVELS[GroupVisibilityLevel.PRIVATE_GROUP] = "private";
GROUPS_VISIBILITY_LEVELS[GroupVisibilityLevel.SECRETE_GROUP] = "secrete"; GROUPS_VISIBILITY_LEVELS[GroupVisibilityLevel.SECRETE_GROUP] = "secrete";
@ -144,6 +148,74 @@ export class GroupsController {
h.send(await this.GroupInfoToAPI(group, h, true)); h.send(await this.GroupInfoToAPI(group, h, true));
} }
/**
* Set (update) group settings
*
* @param h Request handler
*/
public static async SetSettings(h: RequestHandler) {
const groupID = await h.postGroupIDWithAccess("id", GroupsAccessLevel.ADMIN_ACCESS);
// Check group visibility
const visibilityKey = findKey(GROUPS_VISIBILITY_LEVELS, h.postString("visibility", 3));
if(visibilityKey == null)
h.error(400, "Group visibility level not recognized!");
// Check group registration level
const registrationKey = findKey(GROUPS_REGISTRATION_LEVELS, h.postString("registration_level", 3));
if(registrationKey == null)
h.error(400, "Group registration level not recognized!");
// Check post creation level
const postLevelKey = findKey(GROUPS_POSTS_LEVELS, h.postString("posts_level", 3));
if(postLevelKey == null)
h.error(400, "Group post creation level not recognized!");
// Check URL
const url = h.postString("url", 0);
if(url.length > 0 && ! checkURL(url))
h.error(401, "Invalid group URL!");
// Check virtual directory
let virtualDirectory = "";
if(h.hasPostString("virtual_directory", 1)) {
virtualDirectory = h.postVirtualDirectory("virtual_directory");
// Check out whether virtual directory is available or not
if(!await checkVirtualDirectoryAvailability(virtualDirectory, groupID, VirtualDirType.GROUP))
h.error(401, "Requested virtual directory is not available!");
}
const settings = new GroupSettings({
// Basic information
id: groupID,
name: removeHTMLNodes(h.postString("name", 3)),
visiblity: <GroupVisibilityLevel>Number(visibilityKey),
registrationLevel: <GroupRegistrationLevel>Number(registrationKey),
postsCreationLevel: <GroupPostsCreationLevel>Number(postLevelKey),
// Useless info
membersCount: -1,
timeCreate: -1,
// Optionnal
description: removeHTMLNodes(h.postString("description", 0)),
// Optionnal
url: url,
// Optionnal
virtualDirectory: virtualDirectory,
});
await GroupsHelper.SetSettings(settings);
h.success("Group settings have been successfully updated!");
}
/** /**
* Turn a GroupInfo object into a valid API object * Turn a GroupInfo object into a valid API object
* *

View File

@ -96,4 +96,6 @@ export const Routes : Route[] = [
{path: "/groups/get_advanced_info", cb: (h) => GroupsController.GetAdvancedInfo(h), needLogin: false}, {path: "/groups/get_advanced_info", cb: (h) => GroupsController.GetAdvancedInfo(h), needLogin: false},
{path: "/groups/get_settings", cb: (h) => GroupsController.GetSettings(h)}, {path: "/groups/get_settings", cb: (h) => GroupsController.GetSettings(h)},
{path: "/groups/set_settings", cb: (h) => GroupsController.SetSettings(h)},
] ]

View File

@ -0,0 +1,11 @@
import { GroupInfo } from "./Group";
/**
* Group settings
*
* @author Pierre HUBERT
*/
export class GroupSettings extends GroupInfo {
}

View File

@ -9,6 +9,7 @@ import * as sharp from 'sharp';
import { UserHelper } from "../helpers/UserHelper"; import { UserHelper } from "../helpers/UserHelper";
import { GroupsAccessLevel } from "./Group"; import { GroupsAccessLevel } from "./Group";
import { GroupsHelper } from "../helpers/GroupsHelper"; import { GroupsHelper } from "../helpers/GroupsHelper";
import { checkVirtualDirectory } from "../utils/VirtualDirsUtils";
/** /**
* Response to a request * Response to a request
@ -67,6 +68,16 @@ export class RequestHandler {
return this.getPostParam(name) != undefined; return this.getPostParam(name) != undefined;
} }
/**
* Check out whether a POST string is present in the request or not
*
* @param name The name of the POST field to check
* @param minLength Minimal length of the parameter
*/
public hasPostString(name: string, minLength: number = 0) : boolean {
return this.hasPostParameter(name) && this.getPostParam(name).length >= minLength;
}
/** /**
* Get an email address included in a post request * Get an email address included in a post request
* *
@ -233,6 +244,21 @@ export class RequestHandler {
return groupID; return groupID;
} }
/**
* Get a virtual directory included in a POST request
*
* @param name The name of the POST variable
* @return The virtual directory, if found as valid
*/
public postVirtualDirectory(name: string) : string {
const dir = this.postString(name);
if(!checkVirtualDirectory(dir))
this.error(401, "Specified directory seems to be invalid!");
return dir;
}
/** /**
* Get information about an uploaded file * Get information about an uploaded file
* *

View File

@ -2,6 +2,7 @@ import { crypt, sha1, randomStr } from "../utils/CryptUtils";
import { APIClient } from "../entities/APIClient"; import { APIClient } from "../entities/APIClient";
import { UserLoginTokens } from "../entities/UserLoginTokens"; import { UserLoginTokens } from "../entities/UserLoginTokens";
import { DatabaseHelper } from "./DatabaseHelper"; import { DatabaseHelper } from "./DatabaseHelper";
import { UserHelper } from "./UserHelper";
/** /**
* Account helper * Account helper
@ -139,4 +140,17 @@ export class AccountHelper {
token2: row.token2 token2: row.token2
}; };
} }
/**
* Check out whether a virtual directory is available or not
*
* @param dir Virtual directory to check
* @param userID Target user ID
*/
public static async CheckUserDirectoryAvailability(dir: string, userID: number = -1) : Promise<boolean> {
const foundUser = await UserHelper.FindByFolder(dir);
return foundUser < 1 || userID == foundUser;
}
} }

View File

@ -3,6 +3,7 @@ import { GroupsAccessLevel, GroupVisibilityLevel, GroupInfo } from "../entities/
import { GroupMembershipLevels, GroupMember } from "../entities/GroupMember"; import { GroupMembershipLevels, GroupMember } from "../entities/GroupMember";
import { NewGroup } from "../entities/NewGroup"; import { NewGroup } from "../entities/NewGroup";
import { time } from "../utils/DateUtils"; import { time } from "../utils/DateUtils";
import { GroupSettings } from "../entities/GroupSettings";
/** /**
* Groups helper * Groups helper
@ -98,6 +99,23 @@ export class GroupsHelper {
return this.DbToGroupInfo(row); return this.DbToGroupInfo(row);
} }
/**
* Update (set) group settings
*
* @param settings Group settings
*/
public static async SetSettings(settings: GroupSettings) {
const dbEntry = this.GroupSettingsToDB(settings);
await DatabaseHelper.UpdateRows({
table: GROUPS_LIST_TABLE,
where: {
id: settings.id
},
set: dbEntry
});
}
/** /**
* Get the visibility level of a group * Get the visibility level of a group
* *
@ -222,6 +240,36 @@ export class GroupsHelper {
return this.DbToGroupMember(data); return this.DbToGroupMember(data);
} }
/**
* Find a group by its virtual directory
*
* @param dir Target directory
* @returns The ID of the target directory / -1 if none found
*/
public static async FindByVirtualDirectory(dir: string) : Promise<number> {
const result = await DatabaseHelper.QueryRow({
table: GROUPS_LIST_TABLE,
where: {
virtual_directory: dir
},
fields: ["id"]
});
return result == null ? -1 : result.id;
}
/**
* Check out whether a virtual directory is available or not
*
* @param dir The directory to check
* @param groupID The ID of the group making the request
*/
public static async CheckDirectoryAvailability(dir: string, groupID: number = -1) : Promise<boolean> {
const currID = await this.FindByVirtualDirectory(dir);
return currID < 1 || currID == groupID;
}
/** /**
* Count the number of members of a group * Count the number of members of a group
@ -277,4 +325,40 @@ export class GroupsHelper {
following: row.following == 1 following: row.following == 1
}); });
} }
/**
* Turn a GroupSettings object into a database entry object
*
* @param settings Group settings object to transform
* @return Generated database entry
*/
private static GroupSettingsToDB(settings: GroupSettings) : Object {
let data = {};
if(settings.name != null)
data['name'] = settings.name;
if(settings.hasLogo)
data["path_logo"] = settings.logo;
if(settings.visiblity != null)
data["visibility"] = settings.visiblity;
if(settings.registrationLevel != null)
data["registration_level"] = settings.registrationLevel;
if(settings.postsCreationLevel != null)
data["posts_level"] = settings.postsCreationLevel;
if(settings.virtualDirectory != null)
data["virtual_directory"] = settings.virtualDirectory;
if(settings.description != null)
data["description"] = settings.description;
if(settings.url != null)
data["url"] = settings.url;
return data;
}
} }

View File

@ -68,6 +68,24 @@ export class UserHelper {
return request.map((e) => e.ID); return request.map((e) => e.ID);
} }
/**
* Search for user by virtual directory
*
* @param dir Target directory
* @returns The ID of the user found / -1 if none found
*/
public static async FindByFolder(dir: string) : Promise<number> {
const result = await DatabaseHelper.QueryRow({
table: TABLE_NAME,
where: {
sous_repertoire: dir
},
fields: ["ID"]
});
return result == null ? -1 : Number(result.ID);
}
private static async DbToUser(row: any) : Promise<User> { private static async DbToUser(row: any) : Promise<User> {
return new User({ return new User({

24
src/utils/ArrayUtils.ts Normal file
View File

@ -0,0 +1,24 @@
/**
* Array utilities
*
* @author Pierre HUBERT
*/
/**
* Find the key matching a given value in an object
*
* @param object Object to search in
* @param value The value to search for
* @returns Matching key, or null if not found
*/
export function findKey(object: Object, value: any): string {
for (const key in object) {
if (!object.hasOwnProperty(key))
continue;
if(object[key] == value)
return key;
}
return null;
}

View File

@ -14,6 +14,20 @@ export function checkMail(emailAddress: string): boolean {
return (emailAddress.match(/^[a-zA-Z0-9_.]+@[a-zA-Z0-9-.]{1,}[.][a-zA-Z]{2,8}$/) === null ? false : true); return (emailAddress.match(/^[a-zA-Z0-9_.]+@[a-zA-Z0-9-.]{1,}[.][a-zA-Z]{2,8}$/) === null ? false : true);
} }
/**
* Check a URL validity
*
* Source: https://gist.github.com/729294
*
* @param {string} url The URL to check
* @return {boolean} TRUE if the URL is valid
*/
export function checkURL(url: string) : boolean {
const regex = /^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/i
return url.match(regex) == null ? false : true;
}
/** /**
* Fix text encoding * Fix text encoding
* *

View File

@ -0,0 +1,49 @@
import { AccountHelper } from "../helpers/AccountHelper";
import { GroupsHelper } from "../helpers/GroupsHelper";
/**
* Virtual directories utilities
*
* @author Pierre HUBERT
*/
export enum VirtualDirType {
USER,
GROUP
}
/**
* Check out whether a virtual directory is valid or not
*
* @param dir The virtual directory to check
*/
export function checkVirtualDirectory(dir: string) : boolean {
if(dir.length < 4) return false;
for(let el of [".html", ".txt", ".php", "à", "â", "é", "ê", "@", "/", "\"", "'", '"', "<", ">", "?", "&", "#"])
if(dir.includes(el))
return false;
return true;
}
/**
* Check the availability of a virtual directory
*
* @param dir Directory to check
* @param id The ID of the element to check
* @param type The type of the element
*/
export async function checkVirtualDirectoryAvailability(dir: string, id: number, type: VirtualDirType) : Promise<boolean> {
if(!checkVirtualDirectory(dir)) return false;
if(type == VirtualDirType.USER) {
return await AccountHelper.CheckUserDirectoryAvailability(dir, id)
&& await GroupsHelper.CheckDirectoryAvailability(dir);
}
else {
return await AccountHelper.CheckUserDirectoryAvailability(dir)
&& await GroupsHelper.CheckDirectoryAvailability(dir, id);
}
}