1 /* Copyright (C) 2000-2006 Thomas Bopp, Thorsten Hampel, Ludger Merkens
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 * $Id: Group.pike,v 1.8 2010/01/26 12:09:01 astra Exp $
19 inherit "/classes/Object" : __object;
20 inherit "/base/member" : __member;
28 #include <attributes.h>
30 class Group : public Object,member{
39 private int iGroupRoles; /* special privileges of the group */
40 private RoleList groupRoles; /* all roles for this groups */
42 private string sGroupName; /* the groups name */
43 private string sGroupPW; /* password for the group */
44 array aoGroupMembers; /* members of the group */
45 array aoInvites; /* invited users */
46 array aPending; /* waiting users */
47 array aoExclusiveGroups; /* groups with mutual exclusive members*/
48 object oParent; /* the groups parent */
51 #define GROUP_ADMIN_ACCESS SANCTION_ALL
54 * Initialization of the object.
62 ::init_member(); // groups are also group members !
63 aoGroupMembers = ({ });
66 groupRoles = RoleList();
69 add_data_storage(STORE_GROUP,retrieve_group_data, restore_group_data);
75 * Constructor of the group.
91 * Create a duplicate of this object.
93 * @return the duplicate object
96 mapping do_duplicate(void|mapping vars)
98 mapping dup = ::do_duplicate(vars);
100 foreach( aoGroupMembers, object member ) {
101 dup_obj->add_member(member);
107 * Set the parent group of this group.
109 * @param object grp - the new parent
112 void set_parent(object grp)
114 if ( _Server->is_a_factory(CALLER) ) {
116 require_save(STORE_GROUP);
121 * Get the parent group. The group is identified by
122 * (parent->identifier).(groups name)
124 * @return the parent group or zero
131 object get_workroom() {
132 return do_query_attribute(GROUP_WORKROOM);
136 * Get the sub groups of this group (the groups that are members of this
139 * @see get_sub_groups_recursive
141 * @see get_members_recursive
143 * @return an array containing all groups that are members of this group
145 array get_sub_groups()
147 return get_members( CLASS_GROUP );
151 * Get the sub groups of this group recursively (the groups that are members
152 * of this group or any group that is a member of this group, and so on).
156 * @see get_members_recursive
158 * @return an array containing all groups that are members of this group,
161 array get_sub_groups_recursive() {
162 return get_members_recursive( CLASS_GROUP );
166 * Called when created to register the group in the database.
168 * @param string name - register as name
171 void database_registration(string name)
174 "Registration of group " + name + " failed !");
175 require_save(STORE_GROUP);
181 * Set the group's name.
183 * @param string name - the new name of the group
185 void set_group_name(string name)
187 if ( CALLER != _Server->get_factory(CLASS_GROUP) )
188 THROW("Invalid call to set_group_name !", E_ACCESS);
190 string old_name = do_query_attribute( OBJ_NAME );
191 mixed new_name = name / ".";
192 new_name = new_name[ sizeof(new_name) - 1 ];
193 object workroom = do_query_attribute(GROUP_WORKROOM);
194 if ( objectp(workroom) ) {
195 if ( workroom->query_attribute(OBJ_NAME) == old_name+"'s workarea" )
196 workroom->set_attribute( OBJ_NAME, new_name + "'s workarea" );
198 workroom->update_path();
200 require_save(STORE_GROUP);
205 return get_identifier();
208 string get_steam_email()
210 return get_identifier() + "@" + _Server->get_server_name();
214 * Get the group's name.
216 * @return the name of the group
218 string get_group_name()
224 * The destructor of the group object. Removes all members for instance.
232 // let the factory delete the group, because it can do this with the
233 // permissions of the group creator:
239 * This is an internal function that can only to be called by the group
240 * factory. It recursively deletes the group.
242 void low_delete_object()
244 if ( !_Server->is_a_factory(CALLER) )
245 steam_error("Illegal call to Group.low_delete_object !");
251 foreach ( get_sub_groups(), object grp ) {
252 remove_member( grp );
255 // remove remaining members:
256 foreach ( aoGroupMembers, member ) {
258 // remove from parents:
259 foreach ( get_groups(), object grp ) {
261 // delete workroom and calendar:
262 obj = query_attribute( GROUP_WORKROOM );
263 if ( objectp(obj) && !obj->delete() )
264 werror( "Failed to delete group '%s' workroom (object id %d)\n",
265 get_identifier(), obj->get_object_id() );
266 obj = query_attribute( GROUP_CALENDAR );
267 if ( objectp(obj) && !obj->delete() )
268 werror( "Failed to delete group '%s' calendar (object id %d)\n",
269 get_identifier(), obj->get_object_id() );
271 __object::delete_object();
272 __member::delete_object();
275 bool leave_group(object grp, void|object parent)
277 return ::leave_group(grp);
280 bool join_group(object grp)
282 // if the user is currently member of no group move the group
283 int sz = sizeof(get_groups());
284 bool result = ::join_group(grp);
293 * Checks if the group features some special privileges.
295 * @param permission - does the group feature this permission?
296 * @return true or false
297 * @see add_permission
300 features(int permission, void|mixed ctx)
302 if ( iGroupRoles & permission )
304 return groupRoles->check(permission, ctx);
308 * Returns an integer describing the special privileges of the group.
310 * @return permissions of the group
311 * @see add_permission
320 * Add special privileges to the group.
322 * @param permission - add the permission to roles of group
326 add_permission(int permission)
328 try_event(EVENT_GRP_ADD_PERMISSION, CALLER, permission);
329 iGroupRoles |= permission;
330 require_save(STORE_GROUP);
331 run_event(EVENT_GRP_ADD_PERMISSION, CALLER, permission);
336 * Set new default permissions for the group. These are role permissions
337 * like read-everything,write-everything,etc. which is usually only
338 * valid for the ADMIN gorup.
340 * @param int permission - permission bit array.
341 * @return true or throw and error.
342 * @see get_permission
345 set_permission(int permission)
347 try_event(EVENT_GRP_ADD_PERMISSION, CALLER, permission);
348 Role r = Role("general", permission, 0);
350 iGroupRoles = permission;
351 require_save(STORE_GROUP);
352 run_event(EVENT_GRP_ADD_PERMISSION, CALLER, permission);
357 final void add_role(Role r)
359 try_event(EVENT_GRP_ADD_PERMISSION, CALLER, r);
361 require_save(STORE_GROUP);
362 run_event(EVENT_GRP_ADD_PERMISSION, CALLER, r);
365 final RoleList get_roles()
371 * Check if a given user is member of this group.
373 * @param user - the user to check
374 * @return true of false
379 is_member(object user)
381 for ( int i = sizeof(aoGroupMembers) - 1; i >= 0; i-- ) {
382 if ( aoGroupMembers[i] == user )
389 * Check if a given user is member of this group or a subgroup
391 * @param user - the user to check
392 * @return true of false
397 is_virtual_member(object user)
399 for ( int i = sizeof(aoGroupMembers) - 1; i >= 0; i-- ) {
400 if ( aoGroupMembers[i] == user )
402 else if ( aoGroupMembers[i]->get_object_class() & CLASS_GROUP )
403 if ( aoGroupMembers[i]->is_virtual_member(user) )
410 is_virtual_parent(object group)
412 if (!objectp(oParent))
415 if (oParent == group)
417 return oParent->is_virtual_parent(group);
423 * See if a user is admin of this group. It doesnt require
424 * membership in the group.
426 * @param user - the user to check for admin
427 * @return true of false
431 is_admin(object user)
433 if ( !objectp(user) )
435 ASSERTINFO(IS_PROXY(user), "User is not a proxy !");
436 return (query_sanction(user)&GROUP_ADMIN_ACCESS) == GROUP_ADMIN_ACCESS;
440 * Get all admins of a group. Other groups might be admins of a group
443 * @return array of admin objects (Users)
446 final array get_admins()
448 array admins = ({ });
450 foreach( aoGroupMembers, object member) {
451 if ( is_admin(member) ) {
452 if ( member->get_object_class() & CLASS_GROUP )
453 admins += member->get_admins();
454 admins += ({ member });
462 * Check a group password against a string passed.
463 * @param string pass - the group password
464 * @return 1 - ok, 0 - failed
467 check_group_pw(string pass)
469 return stringp(pass) && stringp(sGroupPW) && strlen(sGroupPW)!=0 && pass==sGroupPW;
474 * Checks whether the group is password protected.
475 * @return 1 if the group has a password, 0 if it has no password
477 final bool has_password () {
478 return stringp(sGroupPW) && sizeof(sGroupPW) > 0;
483 * Add a new member to this group. Optionally a password can be
484 * passed to the function so the user joins with a password directly.
486 * @param user - new member
487 * @param string|void pass - the group password
490 * @return 1 - ok, 0 - failed, -1 pending, -2 pending failed
493 add_member(object user, string|void pass)
497 ASSERTINFO(IS_PROXY(user), "User is not a proxy !");
498 // guest cannot be in any group
499 steam_error("The guest user cannot be part of any group !");
503 object caller = CALLER;
506 * Pass right password to security...
507 * The user may add himself to the group with the appropriate password.
508 * Invited users may also join
510 try_event(EVENT_ADD_MEMBER, caller, user,
511 (user == this_user() || geteuid() == user) &&
512 (search(aoInvites, user) >= 0 ||
513 (stringp(pass) && stringp(sGroupPW) && strlen(sGroupPW) != 0 && pass == sGroupPW)));
515 // make sure there won't be any loops
516 if ( _SECURITY->valid_group(user) ) {
522 while ( i < sizeof(grp) ) {
523 mems = grp[i]->get_members();
524 foreach(mems, object m) {
525 LOG("Member:"+m->get_identifier()+"\n");
526 THROW("add_member() recursion detected !",
528 if ( _SECURITY->valid_group(m) )
533 // is this group virtually a parent - recursion possible
534 if (is_virtual_parent(user)) {
535 steam_error("Cannot add a group that is a virtual parent of this group!");
538 // kick user from all exclusive parent groups sub-groups of this group ;)
539 foreach( get_groups(), object group) {
540 // user joins a subgroup
541 LOG("Group to check:" + group->get_identifier()+"\n");
542 if ( group->query_attribute(GROUP_EXCLUSIVE_SUBGROUPS) == 1 ) {
543 foreach ( group->get_members(), object xgroup )
544 if ( xgroup->get_object_class() & CLASS_GROUP &&
545 xgroup->is_member(user) )
546 xgroup->remove_member(user);
549 int size = do_query_attribute(GROUP_MAXSIZE);
551 ( user->get_object_class() & CLASS_GROUP) ||
552 count_members() < size )
555 run_event(EVENT_ADD_MEMBER, CALLER, user);
559 return add_pending(user, pass);
563 void do_add_member(object user)
565 steam_error("The user cannot join the group !");
566 aoGroupMembers += ({ user });
568 // remove membership request:
569 remove_from_attribute(GROUP_MEMBERSHIP_REQS, user);
571 if ( arrayp(aoInvites) )
572 aoInvites -= ({ user });
574 /* Users must be able to read the group for tell and say events */
575 set_sanction(user, query_sanction(user)|SANCTION_READ);
577 require_save(STORE_ACCESS);
578 require_save(STORE_GROUP);
584 * Get the number of members (users only)
586 * @return the number of member users of this group
591 foreach(aoGroupMembers, object member)
592 if ( member->get_object_class() & CLASS_USER )
600 * Add a request to become member to this group. That is the current
601 * use will become member of the group.
605 add_membership_request(void|object user)
607 if ( !objectp(user) )
609 if ( user == USER("guest") )
610 steam_error("Cannot add guest user ...");
611 do_append_attribute(GROUP_MEMBERSHIP_REQS, user);
615 * Check whether a given user requested membership for this group.
617 * @param object user - the user to check
618 * @return true or false
620 bool requested_membership(object user)
622 return !arrayp(do_query_attribute(GROUP_MEMBERSHIP_REQS)) ||
623 search(do_query_attribute(GROUP_MEMBERSHIP_REQS), user) >= 0;
627 * Remove a request for membership from the list of membership
628 * requests of this group.
630 * @param object user - remove the request of the user.
631 * @see add_membership_request
633 void remove_membership_request(object user)
635 if ( (user==this_user()) || (user==geteuid())
636 || is_admin( this_user() ) || is_admin( geteuid() ) ) {
637 remove_from_attribute(GROUP_MEMBERSHIP_REQS, user);
642 * Get the array (copied) of membership requests for this group.
644 * @return array of user objects requesting membership.
648 return copy_value(do_query_attribute(GROUP_MEMBERSHIP_REQS));
653 * Promote a user to group administration. The user does not need to be member.
655 * @param object user - the new admin user of this group
658 void set_admin(object user)
660 sanction_object(user, GROUP_ADMIN_ACCESS);
664 * Remove an administrator from the groups administration
666 * @param object user - the admin user
669 void remove_admin(object user)
671 sanction_object(user, 0);
675 * allows free entry to this group (everyone can join)
677 * @param int entry - boolean value if users can join for free or not
680 void set_free_entry(int entry)
683 sanction_object(GROUP("everyone"), SANCTION_INSERT);
685 sanction_object(GROUP("everyone"), 0);
691 * Invite a user to join this group. If the current user has the
692 * appropriate permissions the given user will be marked as invited
693 * and may join for free.
695 * @param object user - the user to invite.
698 void invite_user(object user)
700 try_event(EVENT_ADD_MEMBER, CALLER, user, 0);
701 if ( search(aoInvites, user) >= 0 )
702 THROW("Failed to invite user - user already invited !", E_ERROR);
703 aoInvites += ({ user });
704 require_save(STORE_GROUP);
705 run_event(EVENT_ADD_MEMBER, CALLER, user);
708 void remove_invite(object user)
710 try_event(EVENT_REMOVE_MEMBER, CALLER, user);
711 if ( search(aoInvites, user) == -1 )
712 THROW("Failed to remove invitation for user - user not invited !", E_ERROR);
713 aoInvites -= ({ user });
714 require_save(STORE_GROUP);
715 run_event(EVENT_REMOVE_MEMBER, CALLER, user);
719 * Check if a given user is invited to join this group.
721 * @param object user - the user to check.
722 * @return true of false.
725 bool is_invited(object user)
727 if ( !arrayp(aoInvites) )
730 return search(aoInvites, user) >= 0;
735 * Get all invited users of this group.
737 * @return array of invited users
741 return copy_value(aoInvites);
744 public void check_consistency()
746 bool consistent = true;
748 foreach(aoGroupMembers, object o) {
749 if (o->status() < 0 || o->status() == 3) {
755 array fixed_members = ({ });
756 foreach(aoGroupMembers, object o) {
757 if (o->status() >= 0 && o->status() != 3) {
758 fixed_members += ({ o });
761 aoGroupMembers = fixed_members;
762 require_save(STORE_GROUP);
767 * remove a member from the group.
769 * @param user - the member to remove
770 * @return if successfully
774 remove_member(object user)
776 LOG("remove_member");
777 ASSERTINFO(!objectp(user) || IS_PROXY(user), "User is not a proxy !");
780 if ( !is_member(user) && !is_pending(user) )
783 if (is_pending(user))
786 remove_pending(user);
787 require_save(STORE_GROUP);
791 LOG("actual member?");
792 try_event(EVENT_REMOVE_MEMBER, CALLER, user);
793 set_sanction(user, 0);
794 aoGroupMembers -= ({ user });
795 require_save(STORE_USER);
796 require_save(STORE_ACCESS);
797 run_event(EVENT_REMOVE_MEMBER, CALLER, user);
799 // try to fill group with first pending
800 if (arrayp(aPending) && sizeof(aPending) > 0 )
803 add_member(aPending[0][0], aPending[0][1]);
804 string msg = do_query_attribute(GROUP_MSG_ACCEPT);
806 msg = "You have been accepted to group:"+
807 do_query_attribute(OBJ_NAME);
808 aPending[0][0]->message(msg);
809 aPending = aPending[1..];
810 require_save(STORE_USER);
819 * Returns the groups members.
821 * @see get_members_recursive
822 * @see get_sub_groups
823 * @see get_sub_groups_recursive
826 * @param classes (optional) limit the result to the specified class (e.g.
827 * CLASS_USER or CLASS_GROUP)
828 * @return the groups members
832 get_members(int|void classes)
834 if ( classes != 0 ) {
835 array members = ({ });
836 foreach(aoGroupMembers, object o) {
837 if ( o->get_object_class() & classes )
842 return copy_value(aoGroupMembers);
847 * get the class of the object
849 * @return the class of the object
854 return ::get_object_class() | CLASS_GROUP;
858 * Returns the members of the group and all subgroups (recursively).
859 * If no parameter is specified, then this returns only users, not
863 * @see get_sub_groups
864 * @see get_sub_groups_recursive
867 * @param classes (optional) limit the result to the specified class (e.g.
868 * CLASS_USER or CLASS_GROUP), default: CLASS_USER
869 * @return members of this group and all subgroups
872 array get_members_recursive ( void|int classes ) {
873 if ( zero_type(classes) ) classes = CLASS_USER; // backwards compatibility
874 array result = ({ });
875 foreach ( get_members(), object obj ) {
876 if ( !objectp(obj) || obj->status() < 0 ) continue; // only valid objs
877 int obj_class = obj->get_object_class();
878 if ( obj_class & classes )
880 if ( obj_class & CLASS_GROUP)
881 result |= obj->get_members_recursive( classes );
890 * Get the mails of a user.
892 * @return array of objects of mail documents
894 array get_mails(void|int from_obj, void|int to_obj)
896 array mails = get_annotations();
897 if ( sizeof(mails) == 0 )
901 to_obj = sizeof(mails);
902 if ( !intp(from_obj) )
904 return mails[from_obj-1..to_obj-1];
909 * Returns the group's emails, optionally filtered by object class,
910 * attribute values or pagination.
911 * The description of the filters and sort options can be found in the
912 * filter_objects_array() function of the "searching" module.
915 * Return the 10 newest mails whose subjects do not start with "{SPAM}",
917 * get_mails_filtered(
919 * ({ "-", "attribute", "OBJ_DESC", "prefix", "{SPAM}" }),
920 * ({ "+", "class", CLASS_DOCUMENT }),
923 * ({ ">", "attribute", "OBJ_CREATION_TIME" })
926 * @param mail_folder (optional) mail folder from which to return the mails
927 * (if not specified, then the inbox of the group is used)
928 * @param filters (optional) an array of filters (each an array as described
929 * in the "searching" module) that specify which objects to return
930 * @param sort (optional) an array of sort entries (each an array as described
931 * in the "searching" module) that specify the order of the items
932 * @param offset (optional) only return the objects starting at (and including)
934 * @param length (optional) only return a maximum of this many objects
935 * @return a mapping ([ "objects":({...}), "total":nr, "length":nr,
936 * "start":nr, "page":nr ]), where the "objects" value is an array of
937 * objects that match the specified filters, sort order and pagination.
938 * The other indices contain pagination information ("total" is the total
939 * number of objects after filtering but before applying "length", "length"
940 * is the requested number of items to return (as in the parameter list),
941 * "start" is the start index of the result in the total number of objects,
942 * and "page" is the page number (starting with 1) of pages with "length"
943 * objects each, or 0 if invalid).
945 mapping get_mails_paginated ( object|void mail_folder, array|void filters, array|void sort, int|void offset, int|void length )
947 return get_module( "searching" )->paginate_object_array(
948 mail_folder->get_annotations(), filters, sort, offset, length );
952 * Returns the group's emails, optionally filtered, sorted and limited by
953 * offset and length. This returns the same as the "objects" index in the
954 * result of get_mails_paginated() and is here for compatibility reasons and
955 * ease of use (if you don't need pagination information).
957 * @see get_mails_paginated
959 array get_mails_filtered ( object|void mail_folder, array|void filters, array|void sort, int|void offset, int|void length )
961 return get_mails_paginated( mail_folder, filters, sort, offset, length )["objects"];
970 * Send an internal mail to all members of this group.
971 * If the sending user has activated sent mail storage, then a copy of the
972 * mail will be stored in her sent mail folder.
974 * @param msg the message body (can be a plaintext or html string, a document
976 * @param subject an optional subject
977 * @param sender an optional sender mail address
978 * @param mimetype optional mime type of the message body
980 final void mail(string|object|mapping msg, string|mapping|void subject, void|string sender, void|string mimetype)
982 object user = geteuid() || this_user();
983 if ( !objectp(user) ) user = _ROOT;
984 if ( mappingp(subject) )
985 subject = subject[do_query_attribute("language")||"english"];
986 if ( objectp(msg) && !stringp(subject) )
987 subject = msg->query_attribute( OBJ_DESC ) || msg->get_identifier();
988 if ( !stringp(subject) )
989 subject = "Message from " + user->get_identifier();
990 if ( !stringp(mimetype) )
991 mimetype = "text/html";
994 if ( objectp(msg) ) {
996 // OBJ_DESC is subject of messages
997 string desc = msg->query_attribute(OBJ_DESC);
998 if ( !stringp(desc) || desc == "" )
999 msg->set_attribute(OBJ_DESC, msg->get_identifier());
1002 object factory = _Server->get_factory(CLASS_DOCUMENT);
1003 message = factory->execute( ([ "name": replace(subject, "/", "_"),
1004 "mimetype": mimetype,
1006 if ( mappingp(msg) )
1007 msg = msg[do_query_attribute("language")||"english"];
1008 message->set_attribute(OBJ_DESC, subject);
1009 if ( lower_case(mimetype) == "text/html" && stringp(msg) ) {
1010 // check whether <html> and <body> tags are missing:
1011 msg = Messaging.fix_html( msg );
1013 message->set_content(msg);
1016 array targets = get_members_recursive();
1017 string mailsetting = do_query_attribute(GROUP_MAIL_SETTINGS) || "open";
1018 if ( mailsetting == "closed" ) {
1019 if ( !is_member(geteuid() || this_user()) )
1020 steam_user_error("Group accepts only messages from members!");
1022 object tmod = get_module("tasks");
1023 object sending_user = this_user();
1024 if ( !objectp(sending_user) ) sending_user = geteuid();
1025 if ( objectp(tmod) ) {
1026 Task.Task task = Task.Task(send_mail);
1027 task->params = ({ message, subject, sender, mimetype, targets, headers,
1029 tmod->run_task(task);
1032 send_mail( message, subject, sender, mimetype, targets, headers,
1039 send_mail(string|object|mapping msg, string|mapping|void subject, void|string sender, void|string mimetype, array targets, mapping headers, object user)
1041 object mail_obj = do_send_mail( msg, subject, sender, mimetype, targets,
1043 if ( objectp(mail_obj) && objectp(user) && user->is_storing_sent_mail() &&
1044 objectp(user->get_sent_mail_folder()) ) {
1045 object mail_copy = mail_obj->duplicate();
1046 if ( objectp(mail_copy) ) {
1047 mail_copy->sanction_object( user, SANCTION_ALL );
1048 object old_euid = geteuid();
1049 mixed euid_err = catch(seteuid( user ));
1050 get_module( "table:read-documents" )->download_document( 0, mail_copy, UNDEFINED ); // mark as read
1051 foreach ( mail_copy->get_annotations(), object ann )
1052 get_module( "table:read-documents" )->download_document( 0, ann, UNDEFINED ); // mark as read
1053 if ( !euid_err ) seteuid( old_euid );
1054 user->get_sent_mail_folder()->add_annotation( mail_copy );
1062 object do_send_mail(string|object|mapping msg, string|mapping|void subject, void|string sender, void|string mimetype, array targets, mapping headers, object user)
1065 array failed = ({ });
1066 foreach (targets, object member) {
1067 if ( !objectp(member) ) continue;
1070 if ( objectp(msg) ) {
1071 mailmsg = msg->duplicate();
1077 if ( mappingp(headers) ) {
1078 tmp_msg_obj = member->do_mail(mailmsg, subject, sender, mimetype, headers);
1081 tmp_msg_obj = member->do_mail(mailmsg, subject, sender, mimetype);
1083 if ( !objectp(msg_obj) && objectp(tmp_msg_obj) )
1084 msg_obj = tmp_msg_obj;
1087 FATAL("Error while sending group mail to %O: %O\n%O",
1090 failed += ({ member->get_identifier() + "( " + member->get_name()+ " )" });
1093 // also notify the user about failed mailing
1094 if ( sizeof(failed) > 0 ) {
1095 if ( objectp(user) ) {
1097 sprintf("Failed to send message '%O' to the following recipients:" +
1100 "Failed to send message", "postmaster", "text/html");
1104 // store message as annotation
1106 do_add_annotation(msg->duplicate());
1109 FATAL("Failed to store annotation on group: %O\n%O", err[0], err[1]);
1119 * Set a new password for this group. A password is used to
1120 * allow users to join the group without waiting for someone to
1121 * accept their membership request.
1123 * @param string pw - the new group password.
1124 * @return true or false
1126 bool set_group_password(string pw)
1128 THROW("Unauthorized call to set_group_password() !", E_ACCESS);
1129 LOG("set_group_password("+pw+")");
1131 require_save(STORE_GROUP);
1136 * get the data of the group for saving
1138 * @return array of group data
1140 * @see restore_group_data
1144 retrieve_group_data()
1146 ASSERTINFO(CALLER == _Database,
1147 "retrieve_group_data() must be called by database !");
1149 "GroupMembers":aoGroupMembers,
1150 "GroupRoles":iGroupRoles,
1152 "GroupPassword": sGroupPW,
1153 "GroupInvites": aoInvites,
1154 "GroupName": sGroupName,
1155 "GroupPending": aPending,
1157 "ExclusiveGroups": aoExclusiveGroups,
1158 "Roles": groupRoles->save(),
1165 * restore the data of the group: must be called by Database
1167 * @param data - the data to restore
1169 * @see retrieve_group_data
1173 restore_group_data(mixed data)
1175 ASSERTINFO(CALLER == _Database, "Caller must be database !");
1177 aoGroupMembers = data["GroupMembers"];
1178 iGroupRoles = data["GroupRoles"];
1179 aoGroups = data["Groups"];
1180 sGroupPW = data["GroupPassword"];
1181 aoInvites = data["GroupInvites"];
1182 sGroupName = data["GroupName"];
1183 aPending = data["GroupPending"];
1184 oParent = data["Parent"];
1185 aoExclusiveGroups = data["ExclusiveGroups"];
1187 // loading the roles of this group
1188 // Role code is in libraries/Roles.pmod
1189 groupRoles = RoleList();
1190 if ( arrayp(data["Roles"]) )
1191 groupRoles->load(data->Roles);
1193 if ( !stringp(sGroupName) || sGroupName == "undefined" )
1194 sGroupName = get_identifier();
1195 if ( arrayp(aoGroupMembers) )
1196 aoGroupMembers -= ({ 0 });
1202 * send a message to the group - will only call the SAY_EVENT
1204 * @param msg - the message to send
1206 void message(string msg)
1208 try_event(EVENT_SAY, CALLER, msg);
1209 run_event(EVENT_SAY, CALLER, msg);
1213 * add a user to the pending list, the pendnig list is a list of users
1214 * waiting for acceptance due to the groups size exceeding the GROUP_MAXSIZE
1216 * @param user - the user to add
1217 * @param pass - optional password to pass to add_member
1222 add_pending(object user, string|void pass)
1227 if (!iSizePending ||(iSizePending > sizeof(aPending)))
1229 aPending += ({ ({ user, pass }) });
1230 require_save(STORE_USER);
1239 * check if a user is already waiting for acceptance on the pending list
1240 * @param user - the user to check for
1245 is_pending(object user)
1247 if ( arrayp(aPending) ) {
1248 foreach( aPending, mixed pend_arr )
1249 if ( arrayp(pend_arr) && sizeof(pend_arr) >= 2 )
1250 if ( pend_arr[0] == user )
1257 remove_pending(object user)
1259 if (arrayp(aPending))
1262 res = map(aPending, lambda(mixed a)
1263 { return a[0]->get_object_id();} );
1266 int p = search(res, user->get_object_id());
1271 require_save(STORE_USER);
1279 * get the list of users waiting to be accepted to the group, in case the
1280 * maximum group size is limited.
1281 * @return - (array)object (the users)
1284 final array get_pending()
1286 return map(aPending, lambda(mixed a) { return a[0];} );
1291 * add a group to the mutual list, A user may be only member to one
1292 * group of this list. Aquiring membership in one of theese groups will
1293 * automatically remove the user from all other groups of this list.
1294 * @param group - the group to add to the cluster
1296 final bool add_to_mutual_list(object group)
1298 try_event(EVENT_GRP_ADDMUTUAL, CALLER, group);
1300 foreach(aoExclusiveGroups, object g)
1301 g->low_add_to_mutual_list(group);
1303 group->low_add_to_mutual_list( aoExclusiveGroups +({this_object()}));
1304 aoExclusiveGroups |= ({ group });
1306 require_save(STORE_GROUP);
1310 * this function will be called from other groups to indicate, this
1311 * group isn't required to inform other groups about this addition.
1312 * To add a group to the cluster call add_to_mutual_list
1313 * @param group - the group beeing informed
1315 final bool low_add_to_mutual_list(array group)
1317 ASSERTINFO(_SECURITY && _SECURITY->valid_group(CALLER),
1318 "low_add_to_mutal was called from non group object");
1319 // try_event(EVENT_GRP_ADDMUTUAL, CALLER, group);
1320 // this is not necessary since SECURITY knows about clusters
1321 aoExclusiveGroups |= group;
1322 require_save(STORE_GROUP);
1327 * get the list of groups connected in a mutual exclusive list
1328 * @return an array of group objects
1330 final array get_mutual_list()
1332 return copy_value(aoExclusiveGroups);
1335 string get_identifier()
1337 if ( stringp(sGroupName) && strlen(sGroupName) > 0 )
1339 return query_attribute(OBJ_NAME);
1342 string parent_and_group_name()
1344 if ( objectp(get_parent()) )
1345 return get_parent()->query_attribute(OBJ_NAME) + "." +
1346 do_query_attribute(OBJ_NAME);
1347 return do_query_attribute(OBJ_NAME);
1350 bool query_join_everyone()
1352 return ((query_sanction(_WORLDUSER) & (SANCTION_READ|SANCTION_INSERT)) ==
1353 (SANCTION_READ|SANCTION_INSERT));
1356 function get_function(string func)
1358 object caller = CALLER;
1359 THROW( sprintf("Only database is allowed to get function pointer.\nNOT %O\n%O",
1360 caller, backtrace()), E_ACCESS);
1361 if ( func == "do_add_member" )
1362 return do_add_member;
1363 return ::get_function(func);