Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
tibetsprague committed Jul 24, 2024
2 parents 1fc4fb5 + 739cc1a commit ceece7d
Show file tree
Hide file tree
Showing 62 changed files with 5,683 additions and 5,163 deletions.
6 changes: 3 additions & 3 deletions APIs.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ NOTE: you will want to pass _either_ a slug _or_ an id to query by. If you pass
}
```

Example GraphQL mutation: __Updating a Group__ (only will succeed on groups that the user is a moderator of)
Example GraphQL mutation: __Updating a Group__ (only will succeed on groups that the user is an administrator of)
```
{
"query": "mutation ($id: ID, $changes: GroupInput) { updateGroup(id: $id, changes: $changes) { id name slug } }",
Expand Down Expand Up @@ -204,8 +204,8 @@ Example GraphQL mutation:
}
}
],
"moderatorDescriptor": "Steward", // Default is Moderator
"moderatorDescriptorPlural": "Stewards", // Default is Moderators
"stewardDescriptor": "Steward", // Default is Steward
"stewardDescriptorPlural": "Stewards", // Default is Stewards
"settings": {
locationDisplayPrecision: precise, // precise => precise location displayed, near => location text shows nearby town/city and coordinate shifted, region => location not shown on map at all and location text shows nearby city/region
publicMemberDirectory: false, // Boolean
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

## [5.9.0] - 2024-07-23

### Added
- __More powerful Roles & Responsibilities__: Groups can now have roles that have specific responsibilities, and members can have multiple roles in a group. Roles can be assigned to members by group Coordinators (described below). There are 4 built in System responsibilities: Administration (super admin that can do everything and change all group settings), Manage Content (can remove posts from the group, and edit the Explore page), Remove Members (boot members from the group), and Add Members (full access to the invite and join request functionality for the group). There are also 3 built in Common Roles that all groups have: Coordinators have full Administration powers, Moderators can Manage Content and Remove Members, and Hosts can Add Members. Groups can also add custom roles with custom responsibilities defined by the group, or custom roles that include the system responsibilities.

## [5.8.0] - 2024-07-03

### Added
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Please check out the [contrbution guide in the EVO](https://github.com/Hylozoic/hylo-evo/blob/dev/CONTRIBUTING.md) repo for more details
2 changes: 1 addition & 1 deletion api/controllers/ExportController.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ module.exports = {
// auth check
let ok = false
try {
ok = await GroupMembership.hasModeratorRole(req.session.userId, p.groupId)
ok = await GroupMembership.hasResponsibility(req.session.userId, p.groupId, Responsibility.constants.RESP_ADMINISTRATION)
} catch (err) {
return res.status(422).send({ error: err.message ? err.message : err })
}
Expand Down
8 changes: 4 additions & 4 deletions api/controllers/UserController.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ module.exports = {
let message = locales[locale].apiInviteMessageContent(req.api_client)
let subject = locales[locale].apiInviteMessageSubject(group.get('name'))
if (req.api_client) {
const client = await (new OIDCAdapter("Client")).find(req.api_client.id)
const client = await (new OIDCAdapter('Client')).find(req.api_client.id)
if (!client) {
return res.status(403).json({ error: 'Unauthorized' })
}
subject = client.invite_subject || locales[locale].clientInviteSubjectDefault(group.get('name'))
message = client.invite_message || locales[locale].clientInviteMessageDefault({userName: user.get('name'), groupName: group.get('name')})
message = client.invite_message || locales[locale].clientInviteMessageDefault({ userName: user.get('name'), groupName: group.get('name') })
}
const inviteBy = await group.moderators().fetchOne()
const inviteBy = await group.stewards().fetchOne()

await InvitationService.create({
groupId: group.id,
Expand All @@ -50,7 +50,7 @@ module.exports = {

const attrs = { name, email: email ? email.toLowerCase() : null, email_validated: false, active: false, group }
if (isModeratorVal) {
attrs.role = GroupMembership.Role.MODERATOR
attrs.role = GroupMembership.Role.MODERATOR // This is ultimately fed to Group.addMembers, which handles mod -> Coordinator. TODO: RESP, fix this
}

return User.create(attrs)
Expand Down
11 changes: 6 additions & 5 deletions api/graphql/filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,21 @@ export const groupFilter = userId => relation => {
const selectIdsForMember = Group.selectIdsForMember(userId)
const parentGroupIds = GroupRelationship.parentIdsFor(selectIdsForMember)
const childGroupIds = GroupRelationship.childIdsFor(selectIdsForMember)
const selectModeratedGroupIds = Group.selectIdsForMember(userId, { 'group_memberships.role': GroupMembership.Role.MODERATOR })
const childrenOfModeratedGroupIds = GroupRelationship.childIdsFor(selectModeratedGroupIds)
// You can see all related groups, even hidden ones, if you are a group Administrator
const selectStewardedGroupIds = Group.selectIdsByResponsibilities(userId, [Responsibility.Common.RESP_ADMINISTRATION])
const childrenOfStewardedGroupIds = GroupRelationship.childIdsFor(selectStewardedGroupIds)

// Can see groups you are a member of...
q2.whereIn('groups.id', selectIdsForMember)
// + their parent groups
q2.orWhereIn('groups.id', parentGroupIds)
// + child groups that are not hidden, except moderators of a group can see its hidden children
// + child groups that are not hidden, except Admininstrators of a group can see its hidden children
q2.orWhere(q3 => {
q3.where(q4 => {
q4.whereIn('groups.id', childGroupIds)
q4.andWhere('groups.visibility', '!=', Group.Visibility.HIDDEN)
})
q3.orWhereIn('groups.id', childrenOfModeratedGroupIds)
q3.orWhereIn('groups.id', childrenOfStewardedGroupIds)
})
// + all public groups
q2.orWhere(q5 => {
Expand Down Expand Up @@ -125,7 +126,7 @@ export const personFilter = userId => relation => relation.query(q => {
q3.select('group_memberships.user_id')
q3.whereIn('group_memberships.group_id', Group.selectIdsForMember(userId))
})
const sharedConnections = UserConnection.query(ucq =>{
const sharedConnections = UserConnection.query(ucq => {
ucq.select('other_user_id')
ucq.where('user_connections.user_id', userId)
})
Expand Down
30 changes: 25 additions & 5 deletions api/graphql/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import mixpanel from '../../lib/mixpanel'
import {
acceptGroupRelationshipInvite,
acceptJoinRequest,
addGroupResponsibility,
addGroupRole,
addMember,
addModerator,
addPeopleToProjectRole,
addPostToCollection,
addResponsibilityToRole,
addProposalVote,
addRoleToMember,
addSkill,
Expand Down Expand Up @@ -42,6 +44,7 @@ import {
deleteComment,
deleteGroup,
deleteGroupRelationship,
deleteGroupResponsibility,
deleteGroupTopic,
deletePost,
deleteProjectRole,
Expand All @@ -63,7 +66,7 @@ import {
logout,
markActivityRead,
markAllActivitiesRead,
messageGroupModerators,
messageGroupStewards,
pinPost,
processStripeToken,
reactOn,
Expand All @@ -79,6 +82,7 @@ import {
removeModerator,
removePost,
removePostFromCollection,
removeResponsibilityFromRole,
removeRoleFromMember,
removeProposalVote,
removeSkill,
Expand All @@ -96,6 +100,7 @@ import {
unlinkAccount,
updateComment,
updateGroup,
updateGroupResponsibility,
updateGroupRole,
updateGroupTopic,
updateGroupTopicFollow,
Expand Down Expand Up @@ -215,6 +220,7 @@ export function makeAuthenticatedQueries (userId, fetchOne, fetchMany) {
InvitationService.check(invitationToken, accessCode),
collection: (root, { id }) => fetchOne('Collection', id),
comment: (root, { id }) => fetchOne('Comment', id),
commonRoles: (root, args) => CommonRole.fetchAll(args),
connections: (root, args) => fetchMany('PersonConnection', args),
group: async (root, { id, slug, updateLastViewed }) => {
// you can specify id or slug, but not both
Expand Down Expand Up @@ -254,6 +260,7 @@ export function makeAuthenticatedQueries (userId, fetchOne, fetchMany) {
person: (root, { id, email }) => fetchOne('Person', id || email, id ? 'id' : 'email'),
post: (root, { id }) => fetchOne('Post', id),
posts: (root, args) => fetchMany('Post', args),
responsibilities: (root, args) => Responsibility.fetchAll(args),
savedSearches: (root, args) => fetchMany('SavedSearch', args),
search: (root, args) => {
if (!args.first) args.first = 20
Expand Down Expand Up @@ -296,6 +303,8 @@ export function makeMutations (expressContext, userId, isAdmin, fetchOne) {

acceptJoinRequest: (root, { joinRequestId }) => acceptJoinRequest(userId, joinRequestId),

addGroupResponsibility: (root, { groupId, title, description }) => addGroupResponsibility({ userId, groupId, title, description }),

addGroupRole: (root, { groupId, color, name, description, emoji }) => addGroupRole({ userId, groupId, color, name, description, emoji }),

addModerator: (root, { personId, groupId }) =>
Expand All @@ -309,7 +318,10 @@ export function makeMutations (expressContext, userId, isAdmin, fetchOne) {

addProposalVote: (root, { postId, optionId }) => addProposalVote({ userId, postId, optionId }),

addRoleToMember: (root, { personId, groupRoleId, groupId }) => addRoleToMember({ userId, personId, groupRoleId, groupId }),
addResponsibilityToRole: (root, { responsibilityId, roleId, groupId }) =>
addResponsibilityToRole({ userId, responsibilityId, roleId, groupId }),

addRoleToMember: (root, { personId, roleId, groupId, isCommonRole = false }) => addRoleToMember({ userId, personId, roleId, groupId, isCommonRole }),

addSkill: (root, { name }) => addSkill(userId, name),
addSkillToLearn: (root, { name }) => addSkillToLearn(userId, name),
Expand Down Expand Up @@ -366,6 +378,9 @@ export function makeMutations (expressContext, userId, isAdmin, fetchOne) {

deleteGroupRelationship: (root, { parentId, childId }) => deleteGroupRelationship(userId, parentId, childId),

deleteGroupResponsibility: (root, { responsibilityId, groupId }) =>
deleteGroupResponsibility({ userId, responsibilityId, groupId }),

deleteGroupTopic: (root, { id }) => deleteGroupTopic(userId, id),

deleteMe: (root) => deleteUser({ sessionId, userId }),
Expand Down Expand Up @@ -408,7 +423,7 @@ export function makeMutations (expressContext, userId, isAdmin, fetchOne) {

markAllActivitiesRead: (root) => markAllActivitiesRead(userId),

messageGroupModerators: (root, { groupId }) => messageGroupModerators(userId, groupId),
messageGroupStewards: (root, { groupId }) => messageGroupStewards(userId, groupId),

pinPost: (root, { postId, groupId }) =>
pinPost(userId, postId, groupId),
Expand Down Expand Up @@ -445,9 +460,11 @@ export function makeMutations (expressContext, userId, isAdmin, fetchOne) {
removePostFromCollection: (root, { collectionId, postId }) =>
removePostFromCollection(userId, collectionId, postId),

removeProposalVote: (root, { postId, optionId }) => removeProposalVote({ userId, postId, optionId }),
removeResponsibilityFromRole: (root, { roleResponsibilityId, groupId }) =>
removeResponsibilityFromRole({ userId, roleResponsibilityId, groupId }),

removeRoleFromMember: (root, { groupRoleId, personId, groupId }) => removeRoleFromMember({ groupRoleId, personId, userId, groupId }),
removeRoleFromMember: (root, { roleId, personId, groupId, isCommonRole }) => removeRoleFromMember({ roleId, personId, userId, groupId, isCommonRole }),
removeProposalVote: (root, { postId, optionId }) => removeProposalVote({ userId, postId, optionId }),

removeSkill: (root, { id, name }) => removeSkill(userId, id || name),
removeSkillToLearn: (root, { id, name }) => removeSkillToLearn(userId, id || name),
Expand Down Expand Up @@ -479,6 +496,9 @@ export function makeMutations (expressContext, userId, isAdmin, fetchOne) {
unlinkAccount: (root, { provider }) =>
unlinkAccount(userId, provider),

updateGroupResponsibility: (root, { groupId, responsibilityId, title, description }) =>
updateGroupResponsibility({ userId, groupId, responsibilityId, title, description }),

updateGroupRole: (root, { groupRoleId, color, name, description, emoji, active, groupId }) =>
updateGroupRole({ userId, groupRoleId, color, name, description, emoji, active, groupId }),

Expand Down
26 changes: 13 additions & 13 deletions api/graphql/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { createRequestHandler, makeMutations, makeAuthenticatedQueries } from '.
import '../../test/setup'
import factories from '../../test/setup/factories'
import { mockify, spyify, unspyify } from '../../test/setup/helpers'
import { some, sortBy } from 'lodash/fp'
import { some } from 'lodash/fp'
import { updateFollowers } from '../models/post/util'

describe('graphql request handler', () => {
var handler,
let handler,
req, res,
user, user2,
group,
Expand All @@ -20,7 +20,7 @@ describe('graphql request handler', () => {
user = factories.user()
user2 = factories.user()
group = factories.group()
post = factories.post({ type: Post.Type.DISCUSSION})
post = factories.post({ type: Post.Type.DISCUSSION })
post2 = factories.post({ type: Post.Type.REQUEST })
comment = factories.comment()
media = factories.media()
Expand All @@ -43,16 +43,16 @@ describe('graphql request handler', () => {
group.posts().attach(post2),
group.addMembers([user.id, user2.id]).then((memberships) => {
const earlier = new Date(new Date().getTime() - 86400000)
return memberships[0].save({ created_at: earlier }, {patch: true})
return memberships[0].save({ created_at: earlier }, { patch: true })
})
])
.then(() => Promise.all([
updateFollowers(post),
updateFollowers(post2)
]))
.then(() => Promise.all([
updateFollowers(post),
updateFollowers(post2)
]))
})

after(async function() {
after(async function () {
await groupExtension.destroy()
await extension.destroy()
})
Expand All @@ -62,7 +62,7 @@ describe('graphql request handler', () => {
req.url = '/noo/graphql'
req.method = 'POST'
req.headers = {
'Content-Type': 'application/json',
'Content-Type': 'application/json'
},
req.session = {
userId: user.id,
Expand Down Expand Up @@ -557,7 +557,7 @@ describe('graphql request handler', () => {
`,
serverContext: { req, res }
})

return expect(executionResult).to.deep.nested.include({
data: {
sendEmailVerification: {
Expand All @@ -579,7 +579,7 @@ describe('graphql request handler', () => {
`,
serverContext: { req, res }
})

return expect(executionResult).to.deep.nested.include({
data: {
sendEmailVerification: {
Expand All @@ -604,7 +604,7 @@ describe('graphql request handler', () => {
`,
serverContext: { req, res }
})

return expect(executionResult).to.deep.nested.include({
data: {
sendEmailVerification: {
Expand Down
Loading

0 comments on commit ceece7d

Please sign in to comment.