mibew/src/mibew/libs/groups.php

681 lines
20 KiB
PHP

<?php
/*
* This file is a part of Mibew Messenger.
*
* Copyright 2005-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Import namespaces and classes of the core
use Mibew\EventDispatcher\EventDispatcher;
use Mibew\EventDispatcher\Events;
use Mibew\Database;
use Mibew\Settings;
/**
* Get chatgroup by id
*
* @param integer $id ID for the chat group
*
* @return null|array It is chatgroup structure. contains (groupId integer,
* parent integer, vcemail string, vclocalname string, vccommonname string,
* vclocaldescription string, vccommondescription string, iweight integer,
* vctitle string, vcchattitle string, vclogo string, vchosturl string)
*/
function group_by_id($id)
{
$db = Database::getInstance();
$group = $db->query(
"SELECT * FROM {opgroup} WHERE groupid = ?",
array($id),
array('return_rows' => Database::RETURN_ONE_ROW)
);
return $group;
}
/**
* Get chatgroup by name
*
* @param string $name Name of the chat group
*
* @return null|array It is chatgroup structure. contains (groupId integer,
* parent integer, vcemail string, vclocalname string, vccommonname string,
* vclocaldescription string, vccommondescription string, iweight integer,
* vctitle string, vcchattitle string, vclogo string, vchosturl string)
*/
function group_by_name($name)
{
$db = Database::getInstance();
$group = $db->query(
"SELECT * FROM {opgroup} WHERE vclocalname = ?",
array($name),
array('return_rows' => Database::RETURN_ONE_ROW)
);
return $group;
}
/**
* Get chatgroup name
*
* @param array $group chat group object
*
* @return string return chat group name
*/
function get_group_name($group)
{
if (!is_array($group)) {
return '';
}
$use_local_name = (get_home_locale() == get_current_locale())
|| !isset($group['vccommonname'])
|| !$group['vccommonname'];
if ($use_local_name) {
return $group['vclocalname'];
} else {
return $group['vccommonname'];
}
}
/**
* Builds list of group ids for specific operator
*
* @param int $operator_id ID of the specific operator.
*
* @return string Comma separated list of operator groups ids
*/
function get_operator_groups_list($operator_id)
{
$db = Database::getInstance();
if (Settings::get('enablegroups') == '1') {
$group_ids = array(0);
$all_groups = $db->query(
"SELECT groupid FROM {operatortoopgroup} WHERE operatorid = ? ORDER BY groupid",
array($operator_id),
array('return_rows' => Database::RETURN_ALL_ROWS)
);
foreach ($all_groups as $g) {
$group_ids[] = $g['groupid'];
}
return implode(",", $group_ids);
} else {
return "";
}
}
/**
* List of available groups
*
* @param array $skip_group ID of groups which most be skipped.
*
* @return array list of all available groups in chatgroup structure. contains
* (groupId integer, parent integer, vcemail string, vclocalname string,
* vccommonname string, vclocaldescription string, vccommondescription string,
* iweight integer, vctitle string, vcchattitle string, vclogo string,
* vchosturl string)
*/
function get_available_parent_groups($skip_group)
{
$result = array();
$result[] = array(
'groupid' => '',
'level' => '',
'vclocalname' => getlocal("-none-"),
);
$db = Database::getInstance();
$groups_list = $db->query(
("SELECT {opgroup}.groupid AS groupid, parent, vclocalname "
. "FROM {opgroup} ORDER BY vclocalname"),
null,
array('return_rows' => Database::RETURN_ALL_ROWS)
);
if ($skip_group) {
$skip_group = (array) $skip_group;
} else {
$skip_group = array();
}
$result = array_merge($result, get_sorted_child_groups_($groups_list, $skip_group, 0));
return $result;
}
/**
* Check if group has any child
*
* @param int $group_id ID of the specific chat group.
*
* @return boolean True if specified group has any child
*/
function group_has_children($group_id)
{
$db = Database::getInstance();
$children = $db->query(
"SELECT COUNT(*) AS count FROM {opgroup} WHERE parent = ?",
array($group_id),
array('return_rows' => Database::RETURN_ONE_ROW)
);
return ($children['count'] > 0);
}
/**
* Get parent of chatgroup
*
* @param array $group specific chatgroup
*
* @return array parent of given group. It is chatgroup structure. contains
* (groupId integer, parent integer, vcemail string, vclocalname string,
* vccommonname string, vclocaldescription string, vccommondescription string,
* iweight integer, vctitle string, vcchattitle string, vclogo string,
* vchosturl string)
*/
function get_top_level_group($group)
{
return is_null($group['parent']) ? $group : group_by_id($group['parent']);
}
/**
* Try to load email for specified group or for its parent.
*
* @param int $group_id Group id
* @return string|boolean Email address or false if there is no email
*/
function get_group_email($group_id)
{
// Try to get group email
$group = group_by_id($group_id);
if (empty($group)) {
return false;
}
if ($group && !empty($group['vcemail'])) {
return $group['vcemail'];
}
// Try to get parent group email
if (!is_null($group['parent'])) {
$group = group_by_id($group['parent']);
if ($group && !empty($group['vcemail'])) {
return $group['vcemail'];
}
}
// There is no email
return false;
}
/**
* Check if group online
*
* @param array $group Associative group array. Should contain 'ilastseen' key.
* @return bool
*/
function group_is_online($group)
{
return $group['ilastseen'] !== null
&& $group['ilastseen'] < Settings::get('online_timeout');
}
/**
* Check if group is away
*
* @param array $group Associative group array. Should contain 'ilastseenaway'
* key.
* @return bool
*/
function group_is_away($group)
{
return $group['ilastseenaway'] !== null
&& $group['ilastseenaway'] < Settings::get('online_timeout');
}
/**
* Return local or common group description depending on current locale.
*
* @param array $group Associative group array. Should contain following keys:
* - 'vccommondescription': string, contain common description of the group;
* - 'vclocaldescription': string, contain local description of the group.
* @return string Group description
*/
function get_group_description($group)
{
$use_local_description = (get_home_locale() == get_current_locale())
|| !isset($group['vccommondescription'])
|| !$group['vccommondescription'];
if ($use_local_description) {
return $group['vclocaldescription'];
} else {
return $group['vccommondescription'];
}
}
/**
* Checks that group's array has all necessary fields.
*
* @param array $group Associative group's array.
* @return bool Boolean true if all the fields are in place and false otherwise.
*/
function check_group_fields($group)
{
$obligatory_fields = array(
'groupid',
'vclocalname',
'vclocaldescription',
'vccommonname',
'vccommondescription',
'vcemail',
'iweight',
'parent',
'vctitle',
'vcchattitle',
'vchosturl',
'vclogo',
);
return (count(array_diff($obligatory_fields, array_keys($group))) == 0);
}
/**
* Creates group
*
* Triggers {@link \Mibew\EventDispatcher\Events::GROUP_CREATE} event.
*
* @param array $group Operators' group. The $group array must contains the
* following keys:
* - vclocalname,
* - vclocaldescription,
* - vccommonname,
* - vccommondescription,
* - vcemail,
* - iweight,
* - parent,
* - vctitle,
* - vcchattitle,
* - vchosturl,
* - vclogo,
* @return array Created group
* @throws \InvalidArgumentException if not all of the group fields are
* specified.
*/
function create_group($group)
{
$group += array('groupid' => false);
if (!check_group_fields($group)) {
throw new \InvalidArgumentException('Not all group fields are specified');
}
$db = Database::getInstance();
$db->query(
("INSERT INTO {opgroup} ("
. "parent, vclocalname, vclocaldescription, vccommonname, "
. "vccommondescription, vcemail, vctitle, vcchattitle, vchosturl, "
. "vclogo, iweight"
. ") values ("
. ":parent, :name, :desc, :common_name, "
. ":common_desc, :email, :title, :chat_title, :url, "
. ":logo, :weight)"),
array(
':parent' => ($group['parent'] ? (int) $group['parent'] : null),
':name' => $group['vclocalname'],
':desc' => $group['vclocaldescription'],
':common_name' => $group['vccommonname'],
':common_desc' => $group['vccommondescription'],
':email' => $group['vcemail'],
':title' => $group['vctitle'],
':chat_title' => $group['vcchattitle'],
':url' => $group['vchosturl'],
':logo' => $group['vclogo'],
':weight' => $group['iweight'],
)
);
$id = $db->insertedId();
$new_group = $db->query(
"SELECT * FROM {opgroup} WHERE groupid = ?",
array($id),
array('return_rows' => Database::RETURN_ONE_ROW)
);
$args = array('group' => $new_group);
EventDispatcher::getInstance()->triggerEvent(Events::GROUP_CREATE, $args);
return $new_group;
}
/**
* Updates group info
*
* Triggers {@link \Mibew\EventDispatcher\Events::GROUP_UPDATE} event.
*
* @param array $group Operators' group. The $group array must contains the
* following keys:
* - groupid,
* - vclocalname,
* - vclocaldescription,
* - vccommonname,
* - vccommondescription,
* - vcemail,
* - iweight,
* - parent,
* - vctitle,
* - vcchattitle,
* - vchosturl,
* - vclogo,
* @throws \InvalidArgumentException if not all of the group fields are
* specified.
*/
function update_group($group)
{
if (!check_group_fields($group)) {
throw new \InvalidArgumentException('Not all group fields are specified');
}
// Get the original state of the group to trigger the "update" event later.
$original_group = group_by_id($group['groupid']);
$db = Database::getInstance();
$db->query(
("UPDATE {opgroup} SET "
. "parent = :parent, vclocalname = :name, vclocaldescription = :desc, "
. "vccommonname = :common_name, vccommondescription = :common_desc, "
. "vcemail = :email, vctitle = :title, vcchattitle = :chat_title, "
. "vchosturl = :host_url, vclogo = :logo, iweight = :weight "
. "where groupid = :id"),
array(
':parent' => ($group['parent'] ? (int) $group['parent'] : null),
':name' => $group['vclocalname'],
':desc' => $group['vclocaldescription'],
':common_name' => $group['vccommonname'],
':common_desc' => $group['vccommondescription'],
':email' => $group['vcemail'],
':title' => $group['vctitle'],
':chat_title' => $group['vcchattitle'],
':host_url' => $group['vchosturl'],
':logo' => $group['vclogo'],
':weight' => $group['iweight'],
':id' => $group['groupid'],
)
);
if ($group['parent']) {
$db->query(
"UPDATE {opgroup} SET parent = NULL WHERE parent = ?",
array($group['groupid'])
);
}
$args = array(
'group' => $group,
'original_group' => $original_group,
);
EventDispatcher::getInstance()->triggerEvent(Events::GROUP_UPDATE, $args);
}
/**
* Builds list of chatgroup operators ids.
*
* @param int $group_id ID of the chatgroup.
*
* @return array ID of all operators in specified group.
*/
function get_group_members($group_id)
{
$rows = Database::getInstance()->query(
"SELECT operatorid FROM {operatortoopgroup} WHERE groupid = ?",
array($group_id),
array('return_rows' => Database::RETURN_ALL_ROWS)
);
$operators = array();
foreach ($rows as $row) {
$operators[] = $row['operatorid'];
}
return $operators;
}
/**
* Update operators of specific group
*
* Triggers {@link \Mibew\EventDispatcher\Events::GROUP_UPDATE_OPERATORS} event.
*
* @param int $group_id ID of the group.
* @param array $new_value list of all operators of specified group.
*/
function update_group_members($group_id, $new_value)
{
// Get the unchanged set of operators related with the group to trigger
// "update" event later
$original_operators = get_group_members($group_id);
$db = Database::getInstance();
$db->query(
"DELETE FROM {operatortoopgroup} WHERE groupid = ?",
array($group_id)
);
foreach ($new_value as $operator_id) {
$db->query(
"INSERT INTO {operatortoopgroup} (groupid, operatorid) VALUES (?, ?)",
array($group_id, $operator_id)
);
}
if ($original_operators != $new_value) {
// Trigger the "update" event only if operators set is changed.
$args = array(
'group' => group_by_id($group_id),
'original_operators' => $original_operators,
'operators' => $new_value,
);
EventDispatcher::getInstance()->triggerEvent(Events::GROUP_UPDATE_OPERATORS, $args);
}
}
/**
* Deletes a group with specified ID.
*
* Triggers {@link \Mibew\EventDispatcher\Events::GROUP_DELETE} event.
*
* @param int $group_id ID of the group that should be deleted.
*/
function delete_group($group_id)
{
$db = Database::getInstance();
$db->query("DELETE FROM {opgroup} WHERE groupid = ?", array($group_id));
$db->query("DELETE FROM {operatortoopgroup} WHERE groupid = ?", array($group_id));
$db->query("UPDATE {thread} SET groupid = 0 WHERE groupid = ?", array($group_id));
$args = array('id' => $group_id);
EventDispatcher::getInstance()->triggerEvent(Events::GROUP_DELETE, $args);
}
function get_all_groups()
{
$db = Database::getInstance();
$groups = $db->query(
("SELECT {opgroup}.groupid AS groupid, parent, "
. "vclocalname, vclocaldescription "
. "FROM {opgroup} ORDER BY vclocalname"),
null,
array('return_rows' => Database::RETURN_ALL_ROWS)
);
return get_sorted_child_groups_($groups);
}
/**
* Retrieves list of groups the operator can see.
*
* This function keeps in mind groups isolation.
*
* @deprecated since 2.1.1. Use {@link get_groups_for_operator()} function
* instead.
* @param array $operator List of operator's fields. See
* {@link operator_by_id()} for details of its keys.
* @return array List of group arrays. See {@link group_by_id()} for details
* about their structure.
*/
function get_all_groups_for_operator($operator)
{
return get_groups_for_operator($operator, false);
}
function get_sorted_child_groups_(
$groups_list,
$skip_groups = array(),
$max_level = -1,
$group_id = null,
$level = 0
) {
$child_groups = array();
foreach ($groups_list as $index => $group) {
if ($group['parent'] == $group_id && !in_array($group['groupid'], $skip_groups)) {
$group['level'] = $level;
$child_groups[] = $group;
if ($max_level == -1 || $level < $max_level) {
$child_groups = array_merge(
$child_groups,
get_sorted_child_groups_(
$groups_list,
$skip_groups,
$max_level,
$group['groupid'],
$level + 1
)
);
}
}
}
return $child_groups;
}
function get_groups_($check_away, $operator, $order = null)
{
$db = Database::getInstance();
if ($order) {
switch ($order['by']) {
case 'weight':
$orderby = "iweight";
break;
case 'lastseen':
$orderby = "ilastseen";
break;
default:
$orderby = "{opgroup}.vclocalname";
}
$orderby = sprintf(
" IF(ISNULL({opgroup}.parent),CONCAT('_',%s),'') %s, {opgroup}.iweight ",
$orderby,
($order['desc'] ? 'DESC' : 'ASC')
);
} else {
$orderby = "iweight, vclocalname";
}
$values = array(
':now' => time(),
);
$query = "SELECT DISTINCT {opgroup}.groupid AS groupid, "
. "{opgroup}.parent AS parent, "
. "vclocalname, vclocaldescription, iweight, "
. "(SELECT count(*) "
. "FROM {operatortoopgroup} "
. "WHERE {opgroup}.groupid = {operatortoopgroup}.groupid"
. ") AS inumofagents, "
. "(SELECT MIN(:now - dtmlastvisited) AS time "
. "FROM {operatortoopgroup}, {operator} "
. "WHERE istatus = 0 "
. "AND {opgroup}.groupid = {operatortoopgroup}.groupid "
. "AND {operatortoopgroup}.operatorid = {operator}.operatorid" .
") AS ilastseen"
. ($check_away
? ", (SELECT MIN(:now - dtmlastvisited) AS time "
. "FROM {operatortoopgroup}, {operator} "
. "WHERE istatus <> 0 "
. "AND {opgroup}.groupid = {operatortoopgroup}.groupid "
. "AND {operatortoopgroup}.operatorid = {operator}.operatorid"
. ") AS ilastseenaway"
: "")
. " FROM {opgroup} ";
if ($operator) {
$query .= ", (SELECT {opgroup}.groupid, {opgroup}.parent "
. "FROM {opgroup}, {operatortoopgroup} "
. "WHERE {opgroup}.groupid = {operatortoopgroup}.groupid "
. "AND {operatortoopgroup}.operatorid = :operatorid) i "
. "WHERE {opgroup}.groupid = i.parent "
. "OR {opgroup}.parent = i.groupid "
. "OR ({opgroup}.parent = i.parent AND {opgroup}.parent IS NOT NULL) "
. "OR {opgroup}.groupid = i.groupid ";
$values[':operatorid'] = $operator['operatorid'];
}
$query .= " ORDER BY " . $orderby;
$groups = $db->query(
$query,
$values,
array('return_rows' => Database::RETURN_ALL_ROWS)
);
return get_sorted_child_groups_($groups);
}
function get_groups($check_away)
{
return get_groups_($check_away, null);
}
function get_groups_for_operator($operator, $check_away = false)
{
return get_groups_($check_away, $operator);
}
function get_sorted_groups($order)
{
return get_groups_(true, null, $order);
}
function get_operator_group_ids($operator_id)
{
$rows = Database::getInstance()->query(
"SELECT groupid FROM {operatortoopgroup} WHERE operatorid = ?",
array($operator_id),
array('return_rows' => Database::RETURN_ALL_ROWS)
);
$groups = array();
foreach ($rows as $row) {
$groups[] = $row['groupid'];
}
return $groups;
}
function get_operators_from_adjacent_groups($operator)
{
return get_operators_list(array(
'isolated_operator_id' => $operator['operatorid'],
));
}