Index: branches/RC/core/kernel/db/dbitem.php =================================================================== diff -u -N -r9973 -r10005 --- branches/RC/core/kernel/db/dbitem.php (.../dbitem.php) (revision 9973) +++ branches/RC/core/kernel/db/dbitem.php (.../dbitem.php) (revision 10005) @@ -833,21 +833,25 @@ * * @param array $master Table data from TempHandler * @param int $foreign_key ForeignKey value to filter name check query by + * @param string $title_field FieldName to alter, by default - TitleField of the prefix + * @param string $format sprintf-style format of renaming pattern, by default Copy %1$s of %2$s which makes it Copy [Number] of Original Name * @access private */ - function NameCopy($master=null, $foreign_key=null) + function NameCopy($master=null, $foreign_key=null, $title_field=null, $format='Copy %1$s of %2$s') { + if (!isset($title_field)) { $title_field = $this->Application->getUnitOption($this->Prefix, 'TitleField'); if (!$title_field || isset($this->CalculatedFields[$title_field]) ) return; + } $new_name = $this->GetDBField($title_field); $original_checked = false; do { - if ( preg_match('/Copy ([0-9]*) *of (.*)/', $new_name, $regs) ) { - $new_name = 'Copy '.($regs[1]+1).' of '.$regs[2]; + if ( preg_match('/'.sprintf($format, '([0-9]*) *', '(.*)').'/', $new_name, $regs) ) { + $new_name = sprintf($format, ($regs[1]+1), $regs[2]); } elseif ($original_checked) { - $new_name = 'Copy of '.$new_name; + $new_name = sprintf($format, '', $new_name); } // if we are cloning in temp table this will look for names in temp table, Index: branches/RC/core/units/user_groups/user_groups_config.php =================================================================== diff -u -N -r9241 -r10005 --- branches/RC/core/units/user_groups/user_groups_config.php (.../user_groups_config.php) (revision 9241) +++ branches/RC/core/units/user_groups/user_groups_config.php (.../user_groups_config.php) (revision 10005) @@ -7,7 +7,42 @@ 'ParentPrefix' => 'g', 'ForeignKey' => 'GroupId', 'ParentTableKey' => 'GroupId', + + 'IDField' => 'PortalUserId', + + 'ListSQLs' => Array( + '' => ' SELECT %1$s.* %2$s FROM %1$s + LEFT JOIN '.TABLE_PREFIX.'PortalGroup g ON %1$s.GroupId = g.GroupId + LEFT JOIN '.TABLE_PREFIX.'PortalUser u ON %1$s.PortalUserId = u.PortalUserId' ), + 'ItemSQLs' => Array( + '' => ' SELECT %1$s.* %2$s FROM %1$s + LEFT JOIN '.TABLE_PREFIX.'PortalGroup g ON %1$s.GroupId = g.GroupId + LEFT JOIN '.TABLE_PREFIX.'PortalUser u ON %1$s.PortalUserId = u.PortalUserId' + ), + 'CalculatedFields' => Array ( + '' => Array( + 'UserName' => 'CONCAT(u.LastName, \' \', u.FirstName)', + 'UserLogin' => 'u.Login', + ), + ), + 'VirtualFields' => Array ( + 'UserName' => Array('type' => 'string'), + 'UserLogin' => Array('type' => 'string'), + ), + 'Grids' => Array( + 'GroupUsers' => Array( + 'Icons' => Array ('default' => 'icon16_group.gif'), + 'Fields' => Array( + 'PortalUserId' => Array ('title' => 'la_col_Id', 'data_block' => 'grid_checkbox_td', 'filter_block' => 'grid_range_filter'), + 'UserName' => Array ('title'=>'la_col_UserFirstLastName'), + 'UserLogin' => Array ('title'=>'la_col_Login'), + 'PrimaryGroup' => Array( 'title'=>'la_col_PrimaryGroup', 'filter_block' => 'grid_options_filter'), + 'MembershipExpires' => Array ('title' => 'la_col_MembershipExpires', 'data_block' => 'grid_membership_td', 'filter_block' => 'grid_date_range_filter'), + ), + ), + ), + ), 'u-ug' => Array( 'ParentPrefix' => 'u', @@ -39,11 +74,12 @@ LEFT JOIN '.TABLE_PREFIX.'PortalGroup g ON %1$s.GroupId = g.GroupId'), 'AutoDelete' => true, - 'AutoClone' => true, + 'AutoClone' => false, 'CalculatedFields' => Array ( '' => Array( 'GroupName' => 'g.Name', + 'GroupDescription' => 'g.Description', ), ), @@ -57,6 +93,7 @@ 'VirtualFields' => Array ( 'GroupName' => Array('type' => 'string'), + 'GroupDescription' => Array('type' => 'string'), ), @@ -66,6 +103,7 @@ 'Fields' => Array( 'GroupId' => Array ('title' => 'la_col_Id', 'data_block' => 'grid_checkbox_td', 'filter_block' => 'grid_range_filter'), 'GroupName' => Array ('title'=>'la_col_GroupName'), + 'GroupDescription' => Array ('title'=>'la_col_Description'), 'PrimaryGroup' => Array( 'title'=>'la_col_PrimaryGroup', 'filter_block' => 'grid_options_filter'), 'MembershipExpires' => Array ('title' => 'la_col_MembershipExpires', 'data_block' => 'grid_membership_td', 'filter_block' => 'grid_date_range_filter'), ), Index: branches/RC/core/admin_templates/users/user_edit_password.tpl =================================================================== diff -u -N -r8929 -r10005 --- branches/RC/core/admin_templates/users/user_edit_password.tpl (.../user_edit_password.tpl) (revision 8929) +++ branches/RC/core/admin_templates/users/user_edit_password.tpl (.../user_edit_password.tpl) (revision 10005) @@ -10,7 +10,7 @@ \ No newline at end of file Index: branches/RC/core/units/general/helpers/search_helper.php =================================================================== diff -u -N -r9643 -r10005 --- branches/RC/core/units/general/helpers/search_helper.php (.../search_helper.php) (revision 9643) +++ branches/RC/core/units/general/helpers/search_helper.php (.../search_helper.php) (revision 10005) @@ -443,6 +443,13 @@ } break; + case 'picker': + $field_value = strlen($field_options['submit_value']) ? $field_options['submit_value'] : false; + if ($field_value) { + $filter_value = $table_name.'`'.$field_name.'` LIKE "%|'.$field_value.'|%"'; + } + break; + case 'like': $filter_value = $this->buildWhereClause($field_options['submit_value'], Array($table_name.'`'.$field_name.'`')); break; Index: branches/RC/core/admin_templates/users/root_edit_password.tpl =================================================================== diff -u -N -r8929 -r10005 --- branches/RC/core/admin_templates/users/root_edit_password.tpl (.../root_edit_password.tpl) (revision 8929) +++ branches/RC/core/admin_templates/users/root_edit_password.tpl (.../root_edit_password.tpl) (revision 10005) @@ -27,6 +27,7 @@ + Index: branches/RC/core/units/users/users_event_handler.php =================================================================== diff -u -N -r9976 -r10005 --- branches/RC/core/units/users/users_event_handler.php (.../users_event_handler.php) (revision 9976) +++ branches/RC/core/units/users/users_event_handler.php (.../users_event_handler.php) (revision 10005) @@ -12,7 +12,8 @@ $permissions = Array ( // admin 'OnSetPersistantVariable' => Array('self' => 'view'), // because setting to logged in user only - 'OnUpdateRootPassword' => Array('self' => true), // because setting to logged in user only + 'OnUpdateRootPassword' => Array('self' => true), + 'OnUpdatePassword' => Array('self' => true), // front 'OnRefreshForm' => Array('self' => true), @@ -28,6 +29,7 @@ 'OnItemBuild' => Array('self' => true), 'OnMassResetSettings' => Array('self' => 'edit'), + 'OnMassCloneUsers' => Array('self' => 'add'), ); $this->permMapping = array_merge($this->permMapping, $permissions); @@ -54,6 +56,22 @@ if (!$this->Application->IsAdmin()) { $object->addFilter('status_filter', '%1$s.Status = '.STATUS_ACTIVE); } + + if ($event->Special == 'group') { + $group_id = $this->Application->GetVar('g_id'); + if ($group_id !== false) { + // show only users, that user doesn't belong to current group + $table_name = $this->Application->GetTempName(TABLE_PREFIX.'UserGroup', 'prefix:g'); + $sql = 'SELECT PortalUserId + FROM '.$table_name.' + WHERE GroupId = '.$group_id; + $user_ids = $this->Conn->GetCol($sql); + array_push($user_ids); // Guest & Everyone groups are set dynamically + if ($user_ids) { + $object->addFilter('already_member_filter', '%1$s.PortalUserId NOT IN ('.implode(',', $user_ids).')'); + } + } + } } @@ -406,6 +424,7 @@ */ function OnAfterItemCreate(&$event) { + if ($this->Application->GetVar('skip_set_primary')) return; $is_subscriber = $this->Application->GetVar('IsSubscriber'); if(!$is_subscriber) { @@ -1134,26 +1153,54 @@ */ function OnUpdateRootPassword(&$event) { + return $this->OnUpdatePassword($event); + } + + /** + * Allows to change root password + * + * @param kEvent $event + */ + function OnUpdatePassword(&$event) + { + $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); + if (!$items_info) return ; + list ($id, $field_values) = each($items_info); $user_id = $this->Application->RecallVar('user_id'); - if ($user_id != -1) { - // not "root" can't change root's password via this event + if ($id == $user_id && ($user_id > 0 || $user_id == -1)) { + $user_dummy =& $this->Application->recallObject($event->Prefix.'.-item', null, Array('skip_autoload' => true)); + /* @var $user_dummy kDBItem */ + + $user_dummy->Load($id); + $status_field = array_shift($this->Application->getUnitOption($event->Prefix, 'StatusField')); + + if ($user_dummy->GetDBField($status_field) != STATUS_ACTIVE) { + // not active user is not allowed to update his record (he could not activate himself manually) return false; } + } + if ($user_id == -1) { + $object =& $event->getObject( Array('skip_autoload' => true) ); + /* @var $object UsersItem */ + // put salt to user's config - $field_options = $this->Application->getUnitOption($event->Prefix.'.RootPassword', 'Fields'); + $field_options = $object->GetFieldOptions('RootPassword'); $field_options['salt'] = 'b38'; - $this->Application->setUnitOption($event->Prefix.'.RootPassword', 'Fields', $field_options); - - $object =& $event->getObject( Array('skip_autoload' => true) ); - /* @var $object UsersItem */ - - $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); - if ($items_info) { - list ($id, $field_values) = each($items_info); + $object->SetFieldOptions('RootPassword', $field_options); + $verify_options = $object->GetFieldOptions('VerifyRootPassword'); + $verify_options['salt'] = 'b38'; + $object->SetFieldOptions('VerifyRootPassword', $verify_options); + + // this is internal hack to allow root/root passwords for dev + if ($this->Application->isDebugMode() && $field_values['RootPassword'] == 'root') { + $this->Application->ConfigHash['Min_Password'] = 4; + } + $this->RemoveRequiredFields($object); $object->SetDBField('RootPassword', $this->Application->ConfigValue('RootPass')); $object->SetFieldsFromHash($field_values); + $object->setID(-1); $status = $object->Validate(); if ($status) { // validation on, password match too @@ -1167,8 +1214,19 @@ else { $event->status = erFAIL; $event->redirect = false; + return; } } + else { + $object =& $event->getObject(); + $object->SetFieldsFromHash($field_values); + if (!$object->Update()) { + $event->status = erFAIL; + $event->redirect = false; + } + } + $event->SetRedirectParam('opener', 'u'); + $event->redirect == true; } /** @@ -1372,6 +1430,63 @@ $this->Application->setUnitOption($event->Prefix, 'Fields', $fields); } } + + /** + * OnMassCloneUsers + * + * @param kEvent $event + */ + function OnMassCloneUsers(&$event) + { + if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) { + return; + } + + $event->status=erSUCCESS; + $ids = $this->StoreSelectedIDs($event); + + $this->Application->SetVar('skip_set_primary', 1); // otherwise it will default primary group, search for skip_set_primary above + $temp_handler =& $this->Application->recallObject($event->Prefix.'_TempHandler', 'kTempTablesHandler'); + /* @var $temp_handler kTempTablesHandler */ + $cloned_users = $temp_handler->CloneItems($event->Prefix, '', $ids); + $this->clearSelectedIDs($event); + } + + /** + * When cloning users, reset password (set random) + * + * @param kEvent $event + */ + function OnBeforeClone(&$event) + { + $object =& $event->getObject(); + /* @var $object kDBItem */ + $object->setRequired('Password', 0); + $object->setRequired('VerifyPassword', 0); + $object->SetDBField('Password', rand(100000000, 999999999)); + $object->SetDBField('CreatedOn', adodb_mktime()); + $object->SetDBField('ResourceId', false); // this will reset it + + // change email cause it should be unique + $object->NameCopy(array(), $object->GetID(), 'Email', 'copy%1$s.%2$s'); + + $object->UpdateFormattersSubFields(); + } + + /** + * Copy user groups after copying user + * + * @param kEvent $event + */ + function OnAfterClone(&$event) + { + $id = $event->getEventParam('id'); + $original_id = $event->getEventParam('original_id'); + + $sql = 'INSERT '.TABLE_PREFIX."UserGroup SELECT $id, GroupId, MembershipExpires, PrimaryGroup, 0 FROM ".TABLE_PREFIX."UserGroup WHERE PortalUserId = $original_id"; + $this->Conn->Query($sql); + } + } ?> \ No newline at end of file Index: branches/RC/core/kernel/session/session.php =================================================================== diff -u -N -r9643 -r10005 --- branches/RC/core/kernel/session/session.php (.../session.php) (revision 9643) +++ branches/RC/core/kernel/session/session.php (.../session.php) (revision 10005) @@ -707,7 +707,20 @@ */ function GetSessionCookie() { - return isset($this->Application->HttpQuery->Cookie[$this->CookieName]) ? $this->Application->HttpQuery->Cookie[$this->CookieName] : false; + $keep_session_on_browser_close = $this->Application->ConfigValue('KeepSessionOnBrowserClose'); + if (isset($this->Application->HttpQuery->Cookie[$this->CookieName]) && + ( $keep_session_on_browser_close || + ( + !$keep_session_on_browser_close && + isset($this->Application->HttpQuery->Cookie[$this->CookieName.'_live']) + && + $this->Application->HttpQuery->Cookie[$this->CookieName] == $this->Application->HttpQuery->Cookie[$this->CookieName.'_live'] + ) + ) + ) { + return $this->Application->HttpQuery->Cookie[$this->CookieName]; + } + return false; } /** @@ -717,6 +730,7 @@ function SetSessionCookie() { $this->SetCookie($this->CookieName, $this->SID, $this->Expiration); + $this->SetCookie($this->CookieName.'_live', $this->SID); $_COOKIE[$this->CookieName] = $this->SID; // for compatibility with in-portal } Index: branches/RC/core/admin_templates/incs/header.tpl =================================================================== diff -u -N -r9643 -r10005 --- branches/RC/core/admin_templates/incs/header.tpl (.../header.tpl) (revision 9643) +++ branches/RC/core/admin_templates/incs/header.tpl (.../header.tpl) (revision 10005) @@ -25,6 +25,7 @@ +