Compare commits

..

10 Commits

Author SHA1 Message Date
95ce447496 0.12.5
Some checks failed
Build release Docker image / Build Docker Images (push) Failing after 2m4s
2025-11-16 18:09:48 +01:00
fa993f75b4 0.12.5 : add message 2025-11-16 18:09:37 +01:00
c33debf153 0.12.4 : improve find user 2025-11-15 18:13:24 +01:00
b2e1ab0361 0.12.3 : change updateMe to handle components 2025-11-13 23:01:28 +01:00
1e642d194e 0.12.2 : fix update user 2025-11-12 21:12:29 +01:00
b8a2bc0aef 0.12.1 : update user 2025-11-12 15:49:52 +01:00
d64c647865 0.12.0 : modify user 2025-11-11 19:09:04 +01:00
2bc35b639b 0.11.21 : change on event and group 2025-11-11 02:04:43 +01:00
14e013bac1 0.11.20 : modify event controller for group owner 2025-11-10 23:10:23 +01:00
539c9cd104 0.11.19 : add simple notification 2025-11-10 14:53:54 +01:00
28 changed files with 15343 additions and 403 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "harmony-back", "name": "harmony-back",
"version": "0.11.18", "version": "0.12.5",
"private": true, "private": true,
"description": "A Strapi application", "description": "A Strapi application",
"scripts": { "scripts": {

View File

@@ -0,0 +1,39 @@
{
"kind": "collectionType",
"collectionName": "chat_conversation_members",
"info": {
"singularName": "chat-conversation-member",
"pluralName": "chat-conversation-members",
"displayName": "ChatConversationMember"
},
"options": {
"draftAndPublish": false
},
"pluginOptions": {},
"attributes": {
"user": {
"type": "relation",
"relation": "oneToOne",
"target": "plugin::users-permissions.user"
},
"role": {
"type": "enumeration",
"enum": [
"member",
"admin",
"owner"
]
},
"joinedAt": {
"type": "datetime"
},
"lastReadAt": {
"type": "datetime"
},
"conversation": {
"type": "relation",
"relation": "oneToOne",
"target": "api::chat-conversation.chat-conversation"
}
}
}

View File

@@ -0,0 +1,7 @@
/**
* chat-conversation-member controller
*/
import { factories } from '@strapi/strapi'
export default factories.createCoreController('api::chat-conversation-member.chat-conversation-member');

View File

@@ -0,0 +1,7 @@
/**
* chat-conversation-member router
*/
import { factories } from '@strapi/strapi';
export default factories.createCoreRouter('api::chat-conversation-member.chat-conversation-member');

View File

@@ -0,0 +1,7 @@
/**
* chat-conversation-member service
*/
import { factories } from '@strapi/strapi';
export default factories.createCoreService('api::chat-conversation-member.chat-conversation-member');

View File

@@ -0,0 +1,37 @@
{
"kind": "collectionType",
"collectionName": "chat_conversations",
"info": {
"singularName": "chat-conversation",
"pluralName": "chat-conversations",
"displayName": "ChatConversation",
"description": ""
},
"options": {
"draftAndPublish": false
},
"pluginOptions": {},
"attributes": {
"title": {
"type": "string"
},
"isGroup": {
"type": "boolean"
},
"creator": {
"type": "relation",
"relation": "oneToOne",
"target": "plugin::users-permissions.user"
},
"messages": {
"type": "relation",
"relation": "oneToMany",
"target": "api::chat-message.chat-message"
},
"users": {
"type": "relation",
"relation": "oneToMany",
"target": "plugin::users-permissions.user"
}
}
}

View File

@@ -0,0 +1,7 @@
/**
* chat-conversation controller
*/
import { factories } from '@strapi/strapi'
export default factories.createCoreController('api::chat-conversation.chat-conversation');

View File

@@ -0,0 +1,7 @@
/**
* chat-conversation router
*/
import { factories } from '@strapi/strapi';
export default factories.createCoreRouter('api::chat-conversation.chat-conversation');

View File

@@ -0,0 +1,7 @@
/**
* chat-conversation service
*/
import { factories } from '@strapi/strapi';
export default factories.createCoreService('api::chat-conversation.chat-conversation');

View File

@@ -0,0 +1,31 @@
{
"kind": "collectionType",
"collectionName": "chat_messages",
"info": {
"singularName": "chat-message",
"pluralName": "chat-messages",
"displayName": "ChatMessage"
},
"options": {
"draftAndPublish": false
},
"pluginOptions": {},
"attributes": {
"sender": {
"type": "relation",
"relation": "oneToOne",
"target": "plugin::users-permissions.user"
},
"content": {
"type": "string",
"required": true
},
"isEdited": {
"type": "boolean",
"default": false
},
"deletedAt": {
"type": "datetime"
}
}
}

View File

@@ -0,0 +1,7 @@
/**
* chat-message controller
*/
import { factories } from '@strapi/strapi'
export default factories.createCoreController('api::chat-message.chat-message');

View File

@@ -0,0 +1,7 @@
/**
* chat-message router
*/
import { factories } from '@strapi/strapi';
export default factories.createCoreRouter('api::chat-message.chat-message');

View File

@@ -0,0 +1,7 @@
/**
* chat-message service
*/
import { factories } from '@strapi/strapi';
export default factories.createCoreService('api::chat-message.chat-message');

View File

@@ -23,8 +23,10 @@ export default ({ strapi }: { strapi: Core.Strapi }) =>
} }
try { try {
const groupId = ctx.request.body.data.group;
// Format tags from array of strings to array of component objects // Format tags from array of strings to array of component objects
const data = ctx.request.body.data; const data = ctx.request.body.data;
delete data.group;
if (data.tags && Array.isArray(data.tags)) { if (data.tags && Array.isArray(data.tags)) {
data.tags = data.tags data.tags = data.tags
.filter((tag: any) => { .filter((tag: any) => {
@@ -51,14 +53,17 @@ export default ({ strapi }: { strapi: Core.Strapi }) =>
} }
// 2. Create the owner relationship // 2. Create the owner relationship
const contextType = groupId && groupId !== 0 ? "group" : "user";
const contextId = groupId && groupId !== 0 ? groupId : userId;
const ownerRelationship = await strapi.db const ownerRelationship = await strapi.db
.query("api::event-relationship.event-relationship") .query("api::event-relationship.event-relationship")
.create({ .create({
data: { data: {
author: userId, author: userId,
event: event.id, event: event.id,
contextType: "user", contextType,
contextId: userId, contextId,
relation: "owner", relation: "owner",
metas: { metas: {
createdAt: new Date().toISOString(), createdAt: new Date().toISOString(),
@@ -70,6 +75,16 @@ export default ({ strapi }: { strapi: Core.Strapi }) =>
`Event ${event.id} created by user ${userId} with owner relationship ${ownerRelationship.id}` `Event ${event.id} created by user ${userId} with owner relationship ${ownerRelationship.id}`
); );
if (contextType === "group") {
await strapi
.service("api::group.group")
.addActivity(
contextId,
ctx.state.user?.username || `User ${userId}`,
`Evènement créé : ${event.title}`
);
}
ctx.send({ data: event }, 201); ctx.send({ data: event }, 201);
} catch (error) { } catch (error) {
strapi.log.error("Error in create event controller:", error); strapi.log.error("Error in create event controller:", error);

View File

@@ -4,7 +4,7 @@ import type { Context } from "koa";
export default ({ strapi }: { strapi: Core.Strapi }) => export default ({ strapi }: { strapi: Core.Strapi }) =>
async function feed(ctx: Context) { async function feed(ctx: Context) {
const userId = ctx.state.user?.id; const userId = ctx.state.user?.id;
const { contextId, contextType } = ctx.query;
if (!userId) return ctx.badRequest("userId is required"); if (!userId) return ctx.badRequest("userId is required");
// Récupérer et valider la query avec les fonctions de Strapi // Récupérer et valider la query avec les fonctions de Strapi
@@ -26,7 +26,12 @@ export default ({ strapi }: { strapi: Core.Strapi }) =>
{ auth: ctx.state.auth } { auth: ctx.state.auth }
); );
const { _limit = 20, _start = 0 } = sanitizedQueryParams; const {
_limit = 20,
_start = 0,
contextType,
contextId,
} = sanitizedQueryParams;
limit = Math.max(1, Math.min(parseInt(String(_limit)) || 20, 100)); // Entre 1 et 100 limit = Math.max(1, Math.min(parseInt(String(_limit)) || 20, 100)); // Entre 1 et 100
start = Math.max(0, parseInt(String(_start)) || 0); // Minimum 0 start = Math.max(0, parseInt(String(_start)) || 0); // Minimum 0
} catch (error) { } catch (error) {
@@ -100,11 +105,15 @@ export default ({ strapi }: { strapi: Core.Strapi }) =>
.map((membership) => membership.group.id) .map((membership) => membership.group.id)
.filter((groupId) => !groupIds.includes(groupId)); // Exclure mes propres groupes .filter((groupId) => !groupIds.includes(groupId)); // Exclure mes propres groupes
// 4⃣ Récupérer le count total pour la pagination // Construire la condition where en fonction des paramètres contextId et contextType
const totalCount = await strapi.db const whereCondition =
.query("api::event-relationship.event-relationship") contextId && contextType
.count({ ? {
where: { relation: "owner",
contextType,
contextId: parseInt(String(contextId)),
}
: {
relation: "owner", relation: "owner",
$or: [ $or: [
// EventRelationships de l'utilisateur courant // EventRelationships de l'utilisateur courant
@@ -120,30 +129,20 @@ export default ({ strapi }: { strapi: Core.Strapi }) =>
// EventRelationships système // EventRelationships système
{ contextType: "system" }, { contextType: "system" },
], ],
}, };
// 4⃣ Récupérer le count total pour la pagination
const totalCount = await strapi.db
.query("api::event-relationship.event-relationship")
.count({
where: whereCondition,
}); });
// 4⃣ Récupérer le feed avec pagination // 4⃣ Récupérer le feed avec pagination
const feed = await strapi.db const feed = await strapi.db
.query("api::event-relationship.event-relationship") .query("api::event-relationship.event-relationship")
.findMany({ .findMany({
where: { where: whereCondition,
relation: "owner",
$or: [
// EventRelationships de l'utilisateur courant
{ author: { id: parseInt(userId) } },
// EventRelationships des amis
{ author: { id: friendIds } },
// EventRelationships des contacts suivis
{ author: { id: followIds } },
// EventRelationships des groupes de l'utilisateur
{ contextType: "group", contextId: groupIds },
// EventRelationships des groupes des amis
{ contextType: "group", contextId: friendsGroupIds },
// EventRelationships système
{ contextType: "system" },
],
},
...queryParams, ...queryParams,
}); });

View File

@@ -31,7 +31,9 @@
"admin", "admin",
"owner", "owner",
"follow", "follow",
"pending" "pending",
"invited",
"rejected"
] ]
} }
} }

View File

@@ -83,6 +83,25 @@ export default factories.createCoreController(
); );
membershipMeta.success = true; membershipMeta.success = true;
membershipMeta.membershipId = membership?.id as number; membershipMeta.membershipId = membership?.id as number;
try {
await strapi.service("api::notification.notification").createNotification({
title: "Groupe créé",
message: `Vous avez créé un nouveau groupe`,
type: "success",
target_user: userId,
source: "group-create",
payload: {
groupId,
membershipId: membership?.id,
},
});
} catch (notificationErr: any) {
strapi.log.warn(
"[group.create] Erreur création notification:",
notificationErr
);
}
} }
} catch (err: any) { } catch (err: any) {
strapi.log.error( strapi.log.error(
@@ -97,5 +116,98 @@ export default factories.createCoreController(
return response; return response;
}, },
async invite(ctx) {
const body = ctx.request.body as any;
const data =
typeof body?.data === "string"
? JSON.parse(body.data)
: body?.data || {};
const groupId = ctx.params.id;
const userIds = data?.usersIds || [];
if (!groupId) {
return ctx.badRequest("Missing groupId");
}
if (!Array.isArray(userIds) || userIds.length === 0) {
return ctx.badRequest("userIds must be a non-empty array");
}
const results = {
success: [],
errors: [],
};
for (const userId of userIds) {
try {
const existingMembership = await strapi.entityService.findMany(
"api::group-membership.group-membership",
{
filters: {
user: userId,
group: groupId,
},
}
);
if (existingMembership && existingMembership.length > 0) {
results.errors.push({
userId,
error: "User is already a member of this group",
});
continue;
}
const membership = await strapi.entityService.create(
"api::group-membership.group-membership",
{
data: {
user: userId,
group: groupId,
role: "invited",
},
}
);
try {
await strapi.service("api::notification.notification").createNotification({
title: "Invitation de groupe",
message: `Vous avez été invité à rejoindre un groupe`,
type: "info",
target_user: userId,
source: "group-invite",
payload: {
groupId,
membershipId: membership?.id,
},
});
} catch (notificationErr: any) {
strapi.log.warn(
"[group.invite] Erreur création notification:",
notificationErr
);
}
results.success.push({
userId,
membershipId: membership?.id,
});
} catch (err: any) {
strapi.log.error(
"[group.invite] Erreur création GroupMembership:",
err
);
results.errors.push({
userId,
error: err?.message || "Unknown error",
});
}
}
ctx.body = {
data: results,
};
},
}) })
); );

View File

@@ -0,0 +1,13 @@
/**
* Custom post routes
*/
export default {
routes: [
{
method: "POST",
path: "/group/:id/invite",
handler: "group.invite",
},
],
};

View File

@@ -4,4 +4,25 @@
import { factories } from '@strapi/strapi'; import { factories } from '@strapi/strapi';
export default factories.createCoreService('api::group.group'); export default factories.createCoreService('api::group.group', ({ strapi }) => ({
async addActivity(groupId: number, userName: string, message: string) {
const group = await strapi.entityService.findOne('api::group.group', groupId, {
populate: ['activities'],
}) as any;
if (!group) {
throw new Error(`Group with id ${groupId} not found`);
}
const activities = group.activities || [];
activities.push({
activityUser: userName,
activityMessage: message,
activityDate: new Date(),
});
return await strapi.entityService.update('api::group.group', groupId, {
data: { activities },
});
},
}));

View File

@@ -0,0 +1,22 @@
{
"collectionName": "components_configuration_parameters",
"info": {
"displayName": "parameter",
"icon": "cog"
},
"options": {},
"attributes": {
"emailNotification": {
"type": "boolean",
"default": false
},
"pushNotification": {
"type": "boolean",
"default": false
},
"sendNewsgroup": {
"type": "boolean",
"default": false
}
}
}

View File

@@ -0,0 +1,35 @@
{
"collectionName": "components_configuration_privacies",
"info": {
"displayName": "privacy",
"icon": "key",
"description": ""
},
"options": {},
"attributes": {
"profileVisibility": {
"type": "enumeration",
"enum": [
"public",
"contacts",
"private"
]
},
"allowMessage": {
"type": "boolean",
"default": true
},
"allowInvite": {
"type": "boolean",
"default": true
},
"allowTag": {
"type": "boolean",
"default": true
},
"showOnline": {
"type": "boolean",
"default": true
}
}
}

View File

@@ -14,6 +14,28 @@
}, },
"activityDate": { "activityDate": {
"type": "datetime" "type": "datetime"
},
"activityAvatar": {
"type": "media",
"multiple": false,
"required": false,
"allowedTypes": [
"images",
"files",
"videos",
"audios"
]
},
"activityType": {
"type": "enumeration",
"enum": [
"user",
"group",
"choral",
"message",
"post",
"contact"
]
} }
} }
} }

View File

@@ -0,0 +1,17 @@
{
"collectionName": "components_user_languages",
"info": {
"displayName": "language",
"icon": "feather",
"description": ""
},
"options": {},
"attributes": {
"language": {
"type": "string"
},
"level": {
"type": "integer"
}
}
}

View File

@@ -73,22 +73,6 @@
"images" "images"
] ]
}, },
"nbFollowers": {
"type": "integer",
"default": 0
},
"nbFollowing": {
"type": "integer",
"default": 0
},
"nbPosts": {
"type": "integer",
"default": 0
},
"nbSaved": {
"type": "integer",
"default": 0
},
"choralOwner": { "choralOwner": {
"type": "relation", "type": "relation",
"relation": "oneToOne", "relation": "oneToOne",
@@ -224,6 +208,36 @@
"relation": "oneToMany", "relation": "oneToMany",
"target": "api::post-ownership.post-ownership", "target": "api::post-ownership.post-ownership",
"mappedBy": "author" "mappedBy": "author"
},
"activities": {
"type": "component",
"repeatable": true,
"component": "group.activity"
},
"bio": {
"type": "text"
},
"experience": {
"type": "integer"
},
"tags": {
"type": "json"
},
"languages": {
"type": "json"
},
"parameter": {
"type": "component",
"repeatable": false,
"component": "configuration.parameter"
},
"privacy": {
"type": "component",
"repeatable": false,
"component": "configuration.privacy"
},
"education": {
"type": "string"
} }
} }
} }

View File

@@ -33,12 +33,10 @@ module.exports = (plugin) => {
throw new Error("No access_token."); throw new Error("No access_token.");
} }
// Get the profile.
const profile = await getProfile(provider, query); const profile = await getProfile(provider, query);
const email = lod.toLower(profile.email); const email = lod.toLower(profile.email);
// We need at least the mail.
if (!email) { if (!email) {
throw new Error("Email was not available."); throw new Error("Email was not available.");
} }
@@ -70,15 +68,13 @@ module.exports = (plugin) => {
throw new Error("Email is already taken."); throw new Error("Email is already taken.");
} }
// Retrieve default role.
const defaultRole = await strapi.db const defaultRole = await strapi.db
.query("plugin::users-permissions.role") .query("plugin::users-permissions.role")
.findOne({ where: { type: advancedSettings.default_role } }); .findOne({ where: { type: advancedSettings.default_role } });
// Create the new user.
const newUser = { const newUser = {
...profile, ...profile,
email, // overwrite with lowercased email email,
provider, provider,
role: defaultRole.id, role: defaultRole.id,
confirmed: true, confirmed: true,
@@ -93,6 +89,243 @@ module.exports = (plugin) => {
}; };
}; };
const originalMe = plugin.controllers.user.me;
plugin.controllers.user.me = async (ctx) => {
const fullUser = await strapi.db
.query("plugin::users-permissions.user")
.findOne({
where: { id: ctx.state.user.id },
populate: true,
});
if (fullUser.contacts && Array.isArray(fullUser.contacts)) {
fullUser.contacts = fullUser.contacts.filter(
(contact) => contact.state === "accepted"
);
}
if (fullUser.post_ownerships && Array.isArray(fullUser.post_ownerships)) {
fullUser.post_ownerships = fullUser.post_ownerships.filter(
(ownership) =>
ownership.contextType === "user" && ownership.relation === "owner"
);
}
if (
fullUser.group_memberships &&
Array.isArray(fullUser.group_memberships)
) {
fullUser.group_memberships = fullUser.group_memberships.filter(
(membership) => ["member", "admin", "owner"].includes(membership.role)
);
}
const user = ctx.state.user;
const userId = user.id;
const [
contactsCount,
groupsCount,
postsCount,
eventsCount,
followersCount,
followingCount,
] = await Promise.all([
strapi.db.query("api::contact.contact").count({
where: {
$or: [{ owner: userId }, { user: userId }],
},
}),
strapi.db.query("api::group-membership.group-membership").count({
where: {
user: userId,
role: { $in: ["owner", "member", "admin"] },
},
}),
strapi.db
.query("api::post-ownership.post-ownership")
.count({ where: { author: userId } }),
strapi.db
.query("api::event-relationship.event-relationship")
.count({ where: { author: userId } }),
strapi.db.query("api::contact.contact").count({
where: {
user: userId,
state: "follow",
},
}),
strapi.db.query("api::contact.contact").count({
where: {
owner: userId,
state: "follow",
},
}),
]);
const result = {
...JSON.parse(JSON.stringify(fullUser)),
stats: {
contacts: contactsCount,
groups: groupsCount,
posts: postsCount,
events: eventsCount,
followers: followersCount,
following: followingCount,
},
};
return result;
};
const originalFindOne = plugin.controllers.user.findOne;
plugin.controllers.user.findOne = async (ctx) => {
// 1⃣ Appel du controller d'origine (permissions, policies)
await originalFindOne(ctx);
// 2⃣ Récupère l'utilisateur renvoyé
let user = ctx.body;
if (!user) {
return ctx.notFound("User not found");
}
// 4⃣ Ajoute un champ calculé
user.profileCompleted = Boolean(user.username && user.surname);
// 3⃣ Supprime les champs sensibles
const sensitive = ["password", "resetPasswordToken", "confirmationToken"];
sensitive.forEach((key) => delete user[key]); //post_ownerships
// 5⃣ Refetch avec relations peuplées
try {
const populatedUser = await strapi.entityService.findOne(
"plugin::users-permissions.user",
user.id,
{
populate: {
post_ownerships: {
populate: {
post: {
populate: {
media: true,
},
},
},
},
contacts: {
populate: {
owner: {
populate: {
avatar: true,
},
},
user: {
populate: {
avatar: true,
},
},
},
},
group_memberships: {
populate: {
group: {
populate: {
banner: true,
},
},
},
},
activities: true,
},
}
);
// Fusionne les données originales (permissions/serialization) avec les relations
user = { ...user, ...populatedUser };
if (user.contacts && Array.isArray(user.contacts)) {
user.contacts = user.contacts.filter(
(contact) => contact.state === "accepted"
);
}
if (user.post_ownerships && Array.isArray(user.post_ownerships)) {
user.post_ownerships = user.post_ownerships.filter(
(ownership) =>
ownership.contextType === "user" && ownership.relation === "owner"
);
}
if (user.group_memberships && Array.isArray(user.group_memberships)) {
user.group_memberships = user.group_memberships.filter((membership) =>
["member", "admin", "owner"].includes(membership.role)
);
}
const eventRelationships = await strapi.db
.query("api::event-relationship.event-relationship")
.findMany({
where: { author: user.id, relation: "owner", contextType: "user" },
populate: {
event: true,
},
});
user.event_relationships = eventRelationships || [];
const [
contactsCount,
groupsCount,
postsCount,
eventsCount,
followersCount,
followingCount,
] = await Promise.all([
strapi.db.query("api::contact.contact").count({
where: {
$or: [{ owner: user.id }, { user: user.id }],
},
}),
strapi.db.query("api::group-membership.group-membership").count({
where: {
user: user.id,
role: { $in: ["owner", "member", "admin"] },
},
}),
strapi.db
.query("api::post-ownership.post-ownership")
.count({ where: { author: user.id } }),
strapi.db
.query("api::event-relationship.event-relationship")
.count({ where: { author: user.id } }),
strapi.db.query("api::contact.contact").count({
where: {
user: user.id,
state: "follow",
},
}),
strapi.db.query("api::contact.contact").count({
where: {
owner: user.id,
state: "follow",
},
}),
]);
user.stats = {
contacts: contactsCount,
groups: groupsCount,
posts: postsCount,
events: eventsCount,
followers: followersCount,
following: followingCount,
};
} catch (err) {
console.error("Erreur populate relations user:", err);
// fallback : retourne juste l'utilisateur original
}
ctx.body = user;
return ctx;
};
const uploadImage = async (ctx, label: string, username: string) => { const uploadImage = async (ctx, label: string, username: string) => {
const key = `${label}Image`; const key = `${label}Image`;
if (ctx.request.files[key]) { if (ctx.request.files[key]) {
@@ -122,7 +355,6 @@ module.exports = (plugin) => {
plugin.controllers.user.updateMe = async (ctx) => { plugin.controllers.user.updateMe = async (ctx) => {
const user = ctx.state.user; const user = ctx.state.user;
// User has to be logged in to update themselves
if (!user) { if (!user) {
return ctx.unauthorized(); return ctx.unauthorized();
} }
@@ -139,9 +371,31 @@ module.exports = (plugin) => {
"voice", "voice",
"job", "job",
"dob", "dob",
"phone",
"bio",
"experience",
"tags",
"languages",
"parameter",
"privacy",
]); ]);
// Make sure there is no duplicate user with the same username /*
if (data.tags) {
newData.tags = data.tags.map((tag) => ({
__component: "social.tags",
text: tag.text,
}));
}
if (data.languages) {
newData.languages = data.languages.map((lang) => ({
__component: "user.language",
language: lang.language,
level: lang.level,
}));
}*/
if (newData.username) { if (newData.username) {
const userWithSameUsername = await strapi const userWithSameUsername = await strapi
.query("plugin::users-permissions.user") .query("plugin::users-permissions.user")
@@ -152,7 +406,6 @@ module.exports = (plugin) => {
} }
} }
// Make sure there is no duplicate user with the same email
if (newData.email) { if (newData.email) {
const userWithSameEmail = await strapi const userWithSameEmail = await strapi
.query("plugin::users-permissions.user") .query("plugin::users-permissions.user")
@@ -167,12 +420,26 @@ module.exports = (plugin) => {
ctx.request.body = newData; ctx.request.body = newData;
ctx.params = { id: user.id }; ctx.params = { id: user.id };
const keysExcludingParameterAndPrivacy = Object.keys(newData).filter(
(key) => key !== "parameter" && key !== "privacy"
);
if (keysExcludingParameterAndPrivacy.length === 0) {
await strapi.entityService
.update("plugin::users-permissions.user", user.id, {
data: newData,
})
.then((res) => {
ctx.response.status = 200;
ctx.response.body = res;
return ctx;
});
} else {
const avatarId = await uploadImage(ctx, "avatar", newData.username); const avatarId = await uploadImage(ctx, "avatar", newData.username);
if (avatarId != 0) ctx.request.body.avatar = avatarId; if (avatarId != 0) ctx.request.body.avatar = avatarId;
const backgroundId = await uploadImage(ctx, "background", newData.username);
if (backgroundId != 0) ctx.request.body.background = backgroundId;
return plugin.controllers.user.update(ctx); return plugin.controllers.user.update(ctx);
}
}; };
plugin.routes["content-api"].routes.push({ plugin.routes["content-api"].routes.push({

View File

@@ -19,6 +19,39 @@ export interface AddressFullAddress extends Struct.ComponentSchema {
}; };
} }
export interface ConfigurationParameter extends Struct.ComponentSchema {
collectionName: 'components_configuration_parameters';
info: {
displayName: 'parameter';
icon: 'cog';
};
attributes: {
emailNotification: Schema.Attribute.Boolean &
Schema.Attribute.DefaultTo<false>;
pushNotification: Schema.Attribute.Boolean &
Schema.Attribute.DefaultTo<false>;
sendNewsgroup: Schema.Attribute.Boolean & Schema.Attribute.DefaultTo<false>;
};
}
export interface ConfigurationPrivacy extends Struct.ComponentSchema {
collectionName: 'components_configuration_privacies';
info: {
description: '';
displayName: 'privacy';
icon: 'key';
};
attributes: {
allowInvite: Schema.Attribute.Boolean & Schema.Attribute.DefaultTo<true>;
allowMessage: Schema.Attribute.Boolean & Schema.Attribute.DefaultTo<true>;
allowTag: Schema.Attribute.Boolean & Schema.Attribute.DefaultTo<true>;
profileVisibility: Schema.Attribute.Enumeration<
['public', 'contacts', 'private']
>;
showOnline: Schema.Attribute.Boolean & Schema.Attribute.DefaultTo<true>;
};
}
export interface GroupActivity extends Struct.ComponentSchema { export interface GroupActivity extends Struct.ComponentSchema {
collectionName: 'components_group_activities'; collectionName: 'components_group_activities';
info: { info: {
@@ -26,8 +59,14 @@ export interface GroupActivity extends Struct.ComponentSchema {
displayName: 'Activity'; displayName: 'Activity';
}; };
attributes: { attributes: {
activityAvatar: Schema.Attribute.Media<
'images' | 'files' | 'videos' | 'audios'
>;
activityDate: Schema.Attribute.DateTime; activityDate: Schema.Attribute.DateTime;
activityMessage: Schema.Attribute.String; activityMessage: Schema.Attribute.String;
activityType: Schema.Attribute.Enumeration<
['user', 'group', 'choral', 'message', 'post', 'contact']
>;
activityUser: Schema.Attribute.String; activityUser: Schema.Attribute.String;
}; };
} }
@@ -43,6 +82,19 @@ export interface SocialTags extends Struct.ComponentSchema {
}; };
} }
export interface UserLanguage extends Struct.ComponentSchema {
collectionName: 'components_user_languages';
info: {
description: '';
displayName: 'language';
icon: 'feather';
};
attributes: {
language: Schema.Attribute.String;
level: Schema.Attribute.Integer;
};
}
export interface UserPermissions extends Struct.ComponentSchema { export interface UserPermissions extends Struct.ComponentSchema {
collectionName: 'components_user_permissions'; collectionName: 'components_user_permissions';
info: { info: {
@@ -63,8 +115,11 @@ declare module '@strapi/strapi' {
export module Public { export module Public {
export interface ComponentSchemas { export interface ComponentSchemas {
'address.full-address': AddressFullAddress; 'address.full-address': AddressFullAddress;
'configuration.parameter': ConfigurationParameter;
'configuration.privacy': ConfigurationPrivacy;
'group.activity': GroupActivity; 'group.activity': GroupActivity;
'social.tags': SocialTags; 'social.tags': SocialTags;
'user.language': UserLanguage;
'user.permissions': UserPermissions; 'user.permissions': UserPermissions;
} }
} }

View File

@@ -584,6 +584,122 @@ export interface ApiChannelChannel extends Struct.CollectionTypeSchema {
}; };
} }
export interface ApiChatConversationMemberChatConversationMember
extends Struct.CollectionTypeSchema {
collectionName: 'chat_conversation_members';
info: {
displayName: 'ChatConversationMember';
pluralName: 'chat-conversation-members';
singularName: 'chat-conversation-member';
};
options: {
draftAndPublish: false;
};
attributes: {
conversation: Schema.Attribute.Relation<
'oneToOne',
'api::chat-conversation.chat-conversation'
>;
createdAt: Schema.Attribute.DateTime;
createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
Schema.Attribute.Private;
joinedAt: Schema.Attribute.DateTime;
lastReadAt: Schema.Attribute.DateTime;
locale: Schema.Attribute.String & Schema.Attribute.Private;
localizations: Schema.Attribute.Relation<
'oneToMany',
'api::chat-conversation-member.chat-conversation-member'
> &
Schema.Attribute.Private;
publishedAt: Schema.Attribute.DateTime;
role: Schema.Attribute.Enumeration<['member', 'admin', 'owner']>;
updatedAt: Schema.Attribute.DateTime;
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
Schema.Attribute.Private;
user: Schema.Attribute.Relation<
'oneToOne',
'plugin::users-permissions.user'
>;
};
}
export interface ApiChatConversationChatConversation
extends Struct.CollectionTypeSchema {
collectionName: 'chat_conversations';
info: {
description: '';
displayName: 'ChatConversation';
pluralName: 'chat-conversations';
singularName: 'chat-conversation';
};
options: {
draftAndPublish: false;
};
attributes: {
createdAt: Schema.Attribute.DateTime;
createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
Schema.Attribute.Private;
creator: Schema.Attribute.Relation<
'oneToOne',
'plugin::users-permissions.user'
>;
isGroup: Schema.Attribute.Boolean;
locale: Schema.Attribute.String & Schema.Attribute.Private;
localizations: Schema.Attribute.Relation<
'oneToMany',
'api::chat-conversation.chat-conversation'
> &
Schema.Attribute.Private;
messages: Schema.Attribute.Relation<
'oneToMany',
'api::chat-message.chat-message'
>;
publishedAt: Schema.Attribute.DateTime;
title: Schema.Attribute.String;
updatedAt: Schema.Attribute.DateTime;
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
Schema.Attribute.Private;
users: Schema.Attribute.Relation<
'oneToMany',
'plugin::users-permissions.user'
>;
};
}
export interface ApiChatMessageChatMessage extends Struct.CollectionTypeSchema {
collectionName: 'chat_messages';
info: {
displayName: 'ChatMessage';
pluralName: 'chat-messages';
singularName: 'chat-message';
};
options: {
draftAndPublish: false;
};
attributes: {
content: Schema.Attribute.String & Schema.Attribute.Required;
createdAt: Schema.Attribute.DateTime;
createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
Schema.Attribute.Private;
deletedAt: Schema.Attribute.DateTime;
isEdited: Schema.Attribute.Boolean & Schema.Attribute.DefaultTo<false>;
locale: Schema.Attribute.String & Schema.Attribute.Private;
localizations: Schema.Attribute.Relation<
'oneToMany',
'api::chat-message.chat-message'
> &
Schema.Attribute.Private;
publishedAt: Schema.Attribute.DateTime;
sender: Schema.Attribute.Relation<
'oneToOne',
'plugin::users-permissions.user'
>;
updatedAt: Schema.Attribute.DateTime;
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
Schema.Attribute.Private;
};
}
export interface ApiChoralMembershipChoralMembership export interface ApiChoralMembershipChoralMembership
extends Struct.CollectionTypeSchema { extends Struct.CollectionTypeSchema {
collectionName: 'choral_memberships'; collectionName: 'choral_memberships';
@@ -1047,7 +1163,7 @@ export interface ApiGroupMembershipGroupMembership
Schema.Attribute.Private; Schema.Attribute.Private;
publishedAt: Schema.Attribute.DateTime; publishedAt: Schema.Attribute.DateTime;
role: Schema.Attribute.Enumeration< role: Schema.Attribute.Enumeration<
['member', 'admin', 'owner', 'follow', 'pending'] ['member', 'admin', 'owner', 'follow', 'pending', 'invited', 'rejected']
>; >;
updatedAt: Schema.Attribute.DateTime; updatedAt: Schema.Attribute.DateTime;
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
@@ -1828,6 +1944,7 @@ export interface PluginUsersPermissionsUser
draftAndPublish: false; draftAndPublish: false;
}; };
attributes: { attributes: {
activities: Schema.Attribute.Component<'group.activity', true>;
address: Schema.Attribute.Text; address: Schema.Attribute.Text;
announcements: Schema.Attribute.Relation< announcements: Schema.Attribute.Relation<
'oneToMany', 'oneToMany',
@@ -1837,6 +1954,7 @@ export interface PluginUsersPermissionsUser
background: Schema.Attribute.Media< background: Schema.Attribute.Media<
'images' | 'files' | 'videos' | 'audios' 'images' | 'files' | 'videos' | 'audios'
>; >;
bio: Schema.Attribute.Text;
blocked: Schema.Attribute.Boolean & Schema.Attribute.DefaultTo<false>; blocked: Schema.Attribute.Boolean & Schema.Attribute.DefaultTo<false>;
board: Schema.Attribute.Relation<'oneToOne', 'api::board.board'>; board: Schema.Attribute.Relation<'oneToOne', 'api::board.board'>;
choral_memberships: Schema.Attribute.Relation< choral_memberships: Schema.Attribute.Relation<
@@ -1861,11 +1979,13 @@ export interface PluginUsersPermissionsUser
createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
Schema.Attribute.Private; Schema.Attribute.Private;
dob: Schema.Attribute.Date; dob: Schema.Attribute.Date;
education: Schema.Attribute.String;
email: Schema.Attribute.Email & email: Schema.Attribute.Email &
Schema.Attribute.Required & Schema.Attribute.Required &
Schema.Attribute.SetMinMaxLength<{ Schema.Attribute.SetMinMaxLength<{
minLength: 6; minLength: 6;
}>; }>;
experience: Schema.Attribute.Integer;
friends: Schema.Attribute.Relation< friends: Schema.Attribute.Relation<
'oneToMany', 'oneToMany',
'plugin::users-permissions.user' 'plugin::users-permissions.user'
@@ -1878,6 +1998,7 @@ export interface PluginUsersPermissionsUser
job: Schema.Attribute.Enumeration< job: Schema.Attribute.Enumeration<
['choir_director', 'choir_addict', 'choir_master', 'choir_singer', 'none'] ['choir_director', 'choir_addict', 'choir_master', 'choir_singer', 'none']
>; >;
languages: Schema.Attribute.JSON;
locale: Schema.Attribute.String & Schema.Attribute.Private; locale: Schema.Attribute.String & Schema.Attribute.Private;
localizations: Schema.Attribute.Relation< localizations: Schema.Attribute.Relation<
'oneToMany', 'oneToMany',
@@ -1885,10 +2006,7 @@ export interface PluginUsersPermissionsUser
> & > &
Schema.Attribute.Private; Schema.Attribute.Private;
name: Schema.Attribute.String; name: Schema.Attribute.String;
nbFollowers: Schema.Attribute.Integer & Schema.Attribute.DefaultTo<0>; parameter: Schema.Attribute.Component<'configuration.parameter', false>;
nbFollowing: Schema.Attribute.Integer & Schema.Attribute.DefaultTo<0>;
nbPosts: Schema.Attribute.Integer & Schema.Attribute.DefaultTo<0>;
nbSaved: Schema.Attribute.Integer & Schema.Attribute.DefaultTo<0>;
password: Schema.Attribute.Password & password: Schema.Attribute.Password &
Schema.Attribute.Private & Schema.Attribute.Private &
Schema.Attribute.SetMinMaxLength<{ Schema.Attribute.SetMinMaxLength<{
@@ -1899,6 +2017,7 @@ export interface PluginUsersPermissionsUser
'oneToMany', 'oneToMany',
'api::post-ownership.post-ownership' 'api::post-ownership.post-ownership'
>; >;
privacy: Schema.Attribute.Component<'configuration.privacy', false>;
provider: Schema.Attribute.String; provider: Schema.Attribute.String;
publishedAt: Schema.Attribute.DateTime; publishedAt: Schema.Attribute.DateTime;
related_contacts: Schema.Attribute.Relation< related_contacts: Schema.Attribute.Relation<
@@ -1912,6 +2031,7 @@ export interface PluginUsersPermissionsUser
>; >;
saved_posts: Schema.Attribute.Relation<'oneToMany', 'api::post.post'>; saved_posts: Schema.Attribute.Relation<'oneToMany', 'api::post.post'>;
surname: Schema.Attribute.String; surname: Schema.Attribute.String;
tags: Schema.Attribute.JSON;
updatedAt: Schema.Attribute.DateTime; updatedAt: Schema.Attribute.DateTime;
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
Schema.Attribute.Private; Schema.Attribute.Private;
@@ -1943,6 +2063,9 @@ declare module '@strapi/strapi' {
'api::board-list.board-list': ApiBoardListBoardList; 'api::board-list.board-list': ApiBoardListBoardList;
'api::board.board': ApiBoardBoard; 'api::board.board': ApiBoardBoard;
'api::channel.channel': ApiChannelChannel; 'api::channel.channel': ApiChannelChannel;
'api::chat-conversation-member.chat-conversation-member': ApiChatConversationMemberChatConversationMember;
'api::chat-conversation.chat-conversation': ApiChatConversationChatConversation;
'api::chat-message.chat-message': ApiChatMessageChatMessage;
'api::choral-membership.choral-membership': ApiChoralMembershipChoralMembership; 'api::choral-membership.choral-membership': ApiChoralMembershipChoralMembership;
'api::choral-permission.choral-permission': ApiChoralPermissionChoralPermission; 'api::choral-permission.choral-permission': ApiChoralPermissionChoralPermission;
'api::choral.choral': ApiChoralChoral; 'api::choral.choral': ApiChoralChoral;