// @ts-check
/**
* Event Visibility Service
*
* Shared service for managing event visibility across event and eventGroup modules.
* Handles visibility filters and access control for events.
*/
import { transaction, query, UUID2hex, HEX2uuid} from "@commtool/sql-query"
import { visibilityList} from "../../tree/executeFilters/executeFilters.js"
/**
* Add group visibility to an event
* @param {Buffer} UIDevent - Event UID
* @param {Buffer|Buffer[]} groupUID - Group UID or array of group UIDs
* @param {Object} req - Request object with session data
* @param {Object|null} connection - Optional database connection for transaction
*/
export const addGroupVisibility=async (UIDevent,groupUID, req, connection=null) =>
{
// this will add the visisbility filters to the event based on the group or groups supplied
const root=UUID2hex(req.session.root)
const user=UUID2hex(req.session.user)
//console.log('addGroupVisibility', HEX2uuid(UIDevent))
// get the groups this group belongs to
let allGroups=[]
const [eventT]= await query(`SELECT ObjectBase.Data
FROM ObjectBase
INNER JOIN Links ON (Links.UIDTarget=ObjectBase.UID AND Links.Type='event')
WHERE Links.UID=? AND ObjectBase.Type='eventT'`,[UIDevent],{cast:['json']})
const template=eventT.Data
if(Array.isArray(groupUID))
{
allGroups=await query (`
SELECT UID ,hierarchie,Title,UIDchangeable,UIDvisible
FROM
((
SELECT myGroup.UID AS UID ,myGroup.hierarchie,myGroup.Title,UIDV1() AS UIDchangeable, UIDV1() AS UIDvisible
FROM ObjectBase AS myGroup
INNER JOIN Links ON (Links.UIDTarget=myGroup.UID AND Links.Type IN ('member','memberA','memberS'))
WHERE Links.UID IN (?) AND myGroup.Type='group'
GROUP BY myGroup.UID
)
UNION ALL
(
SELECT myGroup.UID AS UID ,myGroup.hierarchie,myGroup.Title,UIDV1() AS UIDchangeable, UIDV1() AS UIDvisible
FROM ObjectBase AS myGroup
WHERE myGroup.UID IN (?) AND myGroup.Type='group'
GROUP BY myGroup.UID
)) AS total
GROUP BY UID;
`,[groupUID,groupUID],{connection})
}
else
{
allGroups=await query (`
SELECT UID ,hierarchie,Title,UIDchangeable,UIDvisible
FROM
((
SELECT myGroup.UID AS UID ,myGroup.hierarchie,myGroup.Title,UIDV1() AS UIDchangeable, UIDV1() AS UIDvisible
FROM ObjectBase AS myGroup
INNER JOIN Links ON (Links.UIDTarget=myGroup.UID AND Links.Type IN ('member','memberA','memberS'))
WHERE Links.UID =? AND myGroup.Type='group'
GROUP BY myGroup.UID
)
UNION ALL
(
SELECT myGroup.UID AS UID ,myGroup.hierarchie,myGroup.Title,UIDV1() AS UIDchangeable, UIDV1() AS UIDvisible
FROM ObjectBase AS myGroup
WHERE myGroup.UID =? AND myGroup.Type='group'
GROUP BY myGroup.UID
)) AS total
GROUP BY UID;
`,[groupUID,groupUID],{connection})
}
// get the already existing filters for this event
const filters=await query(`SELECT ObjectBase.UID, ObjectBase.Type, ObjectBase.UIDBelongsTo
FROM ObjectBase
INNER JOIN Links ON (Links.UID=ObjectBase.UID AND Links.Type ='list')
WHERE ObjectBase.Type IN ('visible','changeable') AND Links.UIDTarget=?`,
[UIDevent],{connection}
)
// for the changeable filter
for(const share of template.shares)
{
const shareGroups=allGroups
.filter(g=>g.hierarchie===share.hierarchie) // filter the groups with the given hierarchie
.filter(g=>!filters.find(f=>(f.UIDBelongsTo.equals(g.UID) && f.Type==='changeable'))) // only groups which do not have a changeable filter
if(shareGroups.length)
{
//console.log('filter',shareGroups)
// add filter to group for extraction of the changeable objects and pointing to the event
await query(`
INSERT INTO ObjectBase(UID,Type,UIDBelongsTo,Title,Display,SortName, FullTextIndex, dindex,Data)
VALUES (?,'changeable',?,?,?,'changeable','',0,?)
`,
shareGroups.map(g=>([g.UIDchangeable, g.UID, template.name,g.Title,JSON.stringify(share.Data)])),
{batch:true,connection}
)
await query(`INSERT IGNORE INTO Links (UID,Type,UIDTarget)
VALUES(?,'list',?)`,shareGroups.map(g=>([g.UIDchangeable,UIDevent])),
{batch:true,connection})
/* // now put the initial execution of the filters into the queue. This will generate as well the filter link
queueAddArray(req,shareGroups.map(g=>({
type:'changeable',
UID: g.UIDchangeable,
UIDBelongsTo: g.UID,
oldTarget: null,
newTarget: UIDevent,
})))*/
// now execute the filters
for( const source of shareGroups )
{
await visibilityList(source.UIDchangeable,UIDevent,connection)
}
}
}
for(const share of template.sharesRead)
{
const shareGroups=allGroups
.filter(g=>g.hierarchie===share.hierarchie) // filter the groups with the given hierarchie
.filter(g=>!filters.find(f=>(f.UIDBelongsTo.equals(g.UID) && f.Type==='visible'))) // only groups which do not have a changeable filter
if(shareGroups.length)
{
// add filter to group for extraction of the changeable objects and pointing to the event
await query(`
INSERT INTO ObjectBase(UID,Type,UIDBelongsTo,Title,Display,SortName, FullTextIndex, dindex,Data)
VALUES (?,'changeable',?,?,?,'changeable','',0,?)
`,
shareGroups.map(g=>([g.UIDvisible, g.UID, template.name,g.Title,JSON.stringify(share.Data)])),
{batch:true,connection}
)
await query(`INSERT IGNORE INTO Links (UID,Type,UIDTarget)
VALUES(?,'list',?)`,shareGroups.map(g=>([g.UIDvisible,UIDevent])),
{batch:true,connection})
// now put the initial execution of the filters into the queue. This will generate as well the filter link
/*queueAddArray(req,shareGroups.map(g=>({
type:'visible',
UID: g.UIDvisible,
UIDBelongsTo: g.UID,
oldTarget: null,
newTarget: UIDevent,
})))*/
for( const source of shareGroups )
{
await visibilityList(source.UIDvisible,UIDevent,connection)
}
}
}
}
/**
* Rebuild event visibility from scratch
* @param {Buffer} UIDevent - Event UID
* @param {Object} req - Request object with session data
* @param {Object|null} connection - Optional database connection for transaction
*/
export const rebuildEventVisibility=async(UIDevent,req, connection=null)=>
{
const visibilityTransaction=async (con)=>
{
// remove all visibility
await con.query(`DELETE FROM Visible WHERE UID=?`,[UIDevent])
// delete all visiblity filter
await con.query(`
DELETE filter,Links
FROM Links
LEFT JOIN ObjectBase AS filter ON (Links.UID=filter.UID AND filter.Type IN ('changeable','visible'))
WHERE Links.UIDTarget=? AND Links.Type='list' `,[UIDevent])
// rebuild group visibility
const [rgroup]=await con.query('SELECT UIDBelongsTo FROM ObjectBase WHERE UID=?',[UIDevent]) // get the responsible group
const groups= await con.query(`SELECT ObjectBase.UIDBelongsTo AS UID FROM ObjectBase
INNER JOIN Links ON (Links.UID=ObjectBase.UID AND Links.Type='memberA' )
WHERE ObjectBase.Type='gentry' AND Links.UIDTarget=? `,[UIDevent])
await addGroupVisibility(UIDevent,[rgroup.UIDBelongsTo,...groups.map(g=>g.UID)],req,con)
// add visibility to all jobs
await con.query(`INSERT INTO Visible (UID,Type,UIDUser)
SELECT ? ,IF(Links.Type='memberA','admin','changeable'),job.UIDBelongsTo
FROM ObjectBase AS job
INNER JOIN Links ON (Links.UID=job.UID AND Links.Type IN ('member','memberA'))
WHERE Links.UIDTarget=? AND job.Type='eventJob'
GROUP BY job.UIDBelongsTo`,[UIDevent,UIDevent])
}
if(connection)
{
// use existing transaction
await visibilityTransaction(connection)
}
else
{
await transaction(async (con)=>
{
await visibilityTransaction(con)
},{beforeTransaction:async (connection)=> await connection.query(`SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;`)})
}
}
/**
* Rebuild event access completely
* Deletes all visibility filters and visibilities, then rebuilds them
* @param {Buffer} UIDevent - Event UID
* @param {Object} req - Request object with session data
*/
export const rebuildEventAccess=async (UIDevent,req)=>
{
// we do a complete veisibility rebuild for this list
// this means: we delete all visibiliy filter and all visibilities for this event
// we then add the visibility to the person who entered the event
// we then a add the filters, like it would be a new event
// of course we do all taht in a transactionso that only the final reuslt gets committed
await transaction(async (connection)=>{
// delete all filters and there links
await query(`DELETE ObjectBase,Links FROM Links
INNER JOIN ObjectBase ON (ObjectBase.UID=Links.UID AND ObjectBase.Type IN ('visible','changeable'))
WHERE Links.Type = 'list' AND Links.UIDTarget=? `,
[UIDevent],{connection})
// delete all visibility for this event
await query(`DELETE FROM Visible WHERE UID=?`,[UIDevent],{connection})
// add the visibility for all having a job in this event, theones with mamberA loink have admin rights
await query(`INSERT INTO Visible (UID,Type,UIDUser)
SELECT ?, IF(Links.Type='memberA','admin','changeable'),ObjectBase.UIDBelongsTo
FROM ObjectBase
INNER JOIN Links ON (Links.UID=ObjectBase.UID AND Links.Type IN ('member','memberA'))
WHERE ObjectBase.Type='eventJob' AND Links.UIDTarget=?
`,
[UIDevent,UIDevent],
{connection, log:false}
)
// add the group vsibility according to the filters from the template for the participating groups (type gentry) or administratrive group (type group)
// get all participating groups
const eventGroups=await query(`SELECT ObjectBase.UIDBelongsTo AS UID FROM
ObjectBase
INNER JOIN Links ON(Links.UID=ObjectBase.UID AND Links.Type='memberA')
WHERE ObjectBase.Type ='gentry' AND Links.UIDTarget=?`,
[UIDevent], {log:false})
// and the administrative group
const eventGroups2=await query(`SELECT ObjectBase.UID FROM
ObjectBase
INNER JOIN Links ON(Links.UIDTarget=ObjectBase.UID AND Links.Type='memberA')
WHERE ObjectBase.Type ='group' AND Links.UID=?`,
[UIDevent], {log:false})
if(eventGroups.length>0 || eventGroups2.length>0 )
await addGroupVisibility(UIDevent,[...eventGroups.map(g=>g.UID),...eventGroups2.map(g=>g.UID)],req,connection)
// now it should be done...
// when the transaction closes, the new visibility will become active, until than still the old one was active
},
{beforeTransaction:async (connection)=> await connection.query(`SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;`)})
}