targets)
'pass' => 'm', // don't pass any other (except "m") prefixes to admin session expiration template
'expired' => 1, // expiration mark to show special error on login screen
'no_pass_through' => 1, // this way kApplication::HREF won't add them again
);
if ($this->Application->isAdmin) {
$this->Application->Redirect('index', $admin_url_params, '', 'index.php');
}
if ($this->Application->GetVar('admin') == 1) {
// Front-End showed in admin's right frame
$session_admin = $this->Application->recallObject('Session.admin');
/* @var $session_admin Session */
if (!$session_admin->LoggedIn()) {
// front-end session created from admin session & both expired
$this->Application->DeleteVar('admin');
$this->Application->Redirect('index', $admin_url_params, '', 'admin/index.php');
}
}
// Front-End session expiration
$get = $this->Application->HttpQuery->getRedirectParams();
$t = $this->Application->GetVar('t');
$get['js_redirect'] = $this->Application->ConfigValue('UseJSRedirect');
$this->Application->Redirect($t ? $t : 'index', $get);
}
/**
* [SCHEDULED TASK] Deletes expired sessions
*
* @param kEvent $event
*/
function OnDeleteExpiredSessions($event)
{
if (defined('IS_INSTALL') && IS_INSTALL) {
return ;
}
/** @var SessionStorage $session_storage */
$session_storage = $this->Application->recallObject('SessionStorage');
$session_storage->DeleteExpired();
}
/**
* Checks user data and logs it in if allowed
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnLogin($event)
{
$object = $event->getObject( Array ('form_name' => 'login') );
/* @var $object kDBItem */
$object->SetFieldsFromHash($this->getSubmittedFields($event));
$username = $object->GetDBField('UserLogin');
$password = $object->GetDBField('UserPassword');
$remember_login = $object->GetDBField('UserRememberLogin') == 1;
/* @var $user_helper UserHelper */
$user_helper = $this->Application->recallObject('UserHelper');
$user_helper->event =& $event;
$result = $user_helper->loginUser($username, $password, false, $remember_login);
if ($result != LoginResult::OK) {
$event->status = kEvent::erFAIL;
$object->SetError('UserLogin', $result == LoginResult::NO_PERMISSION ? 'no_permission' : 'invalid_password');
}
if ( is_object($event->MasterEvent) && ($event->MasterEvent->Name == 'OnLoginAjax') ) {
// used to insert just logged-in user e-mail on "One Step Checkout" form in "Modern Store" theme
$user =& $user_helper->getUserObject();
$event->SetRedirectParam('user_email', $user->GetDBField('Email'));
}
}
/**
* Performs user login from ajax request
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnLoginAjax($event)
{
$ajax_form_helper = $this->Application->recallObject('AjaxFormHelper');
/* @var $ajax_form_helper AjaxFormHelper */
$ajax_form_helper->transitEvent($event, 'OnLogin');
}
/**
* [HOOK] Auto-Logins Front-End user when "Remember Login" cookie is found
*
* @param kEvent $event
*/
function OnAutoLoginUser($event)
{
$remember_login_cookie = $this->Application->GetVar('remember_login');
if (!$remember_login_cookie || $this->Application->isAdmin || $this->Application->LoggedIn()) {
return ;
}
/* @var $user_helper UserHelper */
$user_helper = $this->Application->recallObject('UserHelper');
$user_helper->loginUser('', '', false, false, $remember_login_cookie);
}
/**
* Called when user logs in using old in-portal
*
* @param kEvent $event
*/
function OnInpLogin($event)
{
$sync_manager = $this->Application->recallObject('UsersSyncronizeManager', null, Array(), Array ('InPortalSyncronize'));
/* @var $sync_manager UsersSyncronizeManager */
$sync_manager->performAction('LoginUser', $event->getEventParam('user'), $event->getEventParam('pass') );
if ($event->redirect && is_string($event->redirect)) {
// some real template specified instead of true
$this->Application->Redirect($event->redirect, $event->getRedirectParams());
}
}
/**
* Called when user logs in using old in-portal
*
* @param kEvent $event
*/
function OnInpLogout($event)
{
$sync_manager = $this->Application->recallObject('UsersSyncronizeManager', null, Array(), Array ('InPortalSyncronize'));
/* @var $sync_manager UsersSyncronizeManager */
$sync_manager->performAction('LogoutUser');
}
/**
* Performs user logout
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnLogout($event)
{
/* @var $user_helper UserHelper */
$user_helper = $this->Application->recallObject('UserHelper');
$user_helper->event =& $event;
$user_helper->logoutUser();
}
/**
* Redirects user after successful registration to confirmation template (on Front only)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemCreate(kEvent $event)
{
parent::OnAfterItemCreate($event);
$this->afterItemChanged($event);
$this->assignToPrimaryGroup($event);
}
/**
* Performs user registration
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnCreate(kEvent $event)
{
if ( $this->Application->isAdmin ) {
parent::OnCreate($event);
return ;
}
$object = $event->getObject( Array('form_name' => 'registration') );
/* @var $object UsersItem */
$field_values = $this->getSubmittedFields($event);
$user_email = getArrayValue($field_values, 'Email');
$subscriber_id = $user_email ? $this->getSubscriberByEmail($user_email) : false;
if ( $subscriber_id ) {
// update existing subscriber
$object->Load($subscriber_id);
$object->SetDBField('PrimaryGroupId', $this->Application->ConfigValue('User_NewGroup'));
$this->Application->SetVar($event->getPrefixSpecial(true), Array ($object->GetID() => $field_values));
}
$object->SetFieldsFromHash($field_values);
$event->setEventParam('form_data', $field_values);
$status = $object->isLoaded() ? $object->Update() : $object->Create();
if ( !$status ) {
$event->status = kEvent::erFAIL;
$event->redirect = false;
$object->setID( (int)$object->GetID() );
}
$this->setNextTemplate($event, true);
if ( ($event->status == kEvent::erSUCCESS) && $event->redirect ) {
$this->assignToPrimaryGroup($event);
$object->sendEmails();
$this->autoLoginUser($event);
}
}
/**
* Processes user registration from ajax request
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnRegisterAjax(kEvent $event)
{
$ajax_form_helper = $this->Application->recallObject('AjaxFormHelper');
/* @var $ajax_form_helper AjaxFormHelper */
$ajax_form_helper->transitEvent($event, 'OnCreate', Array ('do_refresh' => 1));
}
/**
* Returns subscribed user ID by given e-mail address
*
* @param string $email
* @return int|bool
* @access protected
*/
protected function getSubscriberByEmail($email)
{
$verify_user = $this->Application->recallObject('u.verify', null, Array ('skip_autoload' => true));
/* @var $verify_user UsersItem */
$verify_user->Load($email, 'Email');
return $verify_user->isLoaded() && $verify_user->isSubscriberOnly() ? $verify_user->GetID() : false;
}
/**
* Login user if possible, if not then redirect to corresponding template
*
* @param kEvent $event
*/
function autoLoginUser($event)
{
$object = $event->getObject();
/* @var $object UsersItem */
if ( $object->GetDBField('Status') == STATUS_ACTIVE ) {
/* @var $user_helper UserHelper */
$user_helper = $this->Application->recallObject('UserHelper');
$user =& $user_helper->getUserObject();
$user->Load($object->GetID());
if ( $user_helper->checkLoginPermission() ) {
$user_helper->loginUserById( $user->GetID() );
}
}
}
/**
* Set's new unique resource id to user
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemCreate(kEvent $event)
{
parent::OnBeforeItemCreate($event);
$this->beforeItemChanged($event);
$cs_helper = $this->Application->recallObject('CountryStatesHelper');
/* @var $cs_helper kCountryStatesHelper */
$object = $event->getObject();
/* @var $object UsersItem */
if ( !$object->isSubscriberOnly() ) {
// don't check state-to-country relations for subscribers
$cs_helper->CheckStateField($event, 'State', 'Country');
}
if ( $object->getFormName() != 'login' ) {
$this->_makePasswordRequired($event);
}
$cs_helper->PopulateStates($event, 'State', 'Country');
$this->setUserGroup($object);
/* @var $user_helper UserHelper */
$user_helper = $this->Application->recallObject('UserHelper');
if ( !$user_helper->checkBanRules($object) ) {
$object->SetError('Username', 'banned');
}
$object->SetDBField('IPAddress', $this->Application->getClientIp());
if ( !$this->Application->isAdmin ) {
$object->SetDBField('FrontLanguage', $this->Application->GetVar('m_lang'));
}
}
/**
* Sets primary group of the user
*
* @param kDBItem $object
*/
protected function setUserGroup(&$object)
{
if ($object->Special == 'subscriber') {
$object->SetDBField('PrimaryGroupId', $this->Application->ConfigValue('User_SubscriberGroup'));
return ;
}
// set primary group to user
if ( !$this->Application->isAdminUser ) {
$group_id = $object->GetDBField('PrimaryGroupId');
if ($group_id) {
// check, that group is allowed for Front-End
$sql = 'SELECT GroupId
FROM ' . TABLE_PREFIX . 'UserGroups
WHERE GroupId = ' . (int)$group_id . ' AND FrontRegistration = 1';
$group_id = $this->Conn->GetOne($sql);
}
if (!$group_id) {
// when group not selected OR not allowed -> use default group
$object->SetDBField('PrimaryGroupId', $this->Application->ConfigValue('User_NewGroup'));
}
}
}
/**
* Assigns a user to it's primary group
*
* @param kEvent $event
*/
protected function assignToPrimaryGroup($event)
{
$object = $event->getObject();
/* @var $object kDBItem */
$primary_group_id = $object->GetDBField('PrimaryGroupId');
if ($primary_group_id) {
$ug_table = TABLE_PREFIX . 'UserGroupRelations';
if ( $object->IsTempTable() ) {
$ug_table = $this->Application->GetTempName($ug_table, 'prefix:' . $event->Prefix);
}
$sql = 'SELECT COUNT(*)
FROM ' . $ug_table . '
WHERE PortalUserId = ' . $object->GetID() . ' AND GroupId = ' . $primary_group_id;
if ( $this->Conn->GetOne($sql) ) {
return;
}
$fields_hash = Array (
'PortalUserId' => $object->GetID(),
'GroupId' => $primary_group_id,
);
if ( $object->IsTempTable() ) {
$new_id = (int)$this->Conn->GetOne('SELECT MIN(Id) FROM ' . $ug_table .' WHERE Id < 0' );
$fields_hash['Id'] = $new_id - 1;
}
$this->Conn->doInsert($fields_hash, $ug_table);
}
}
/**
* Set's new unique resource id to user
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemValidate(kEvent $event)
{
$object = $event->getObject();
/* @var $object kDBItem */
$resource_id = $object->GetDBField('ResourceId');
if ( !$resource_id ) {
$object->SetDBField('ResourceId', $this->Application->NextResourceId());
}
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnRecommend($event)
{
$object = $event->getObject( Array ('form_name' => 'recommend') );
/* @var $object kDBItem */
$object->SetFieldsFromHash($this->getSubmittedFields($event));
if ( !$object->ValidateField('RecommendEmail') ) {
$event->status = kEvent::erFAIL;
return ;
}
$send_params = Array (
'to_email' => $object->GetDBField('RecommendEmail'),
'to_name' => $object->GetDBField('RecommendEmail'),
);
$user_id = $this->Application->RecallVar('user_id');
$email_sent = $this->Application->emailUser('USER.SUGGEST', $user_id, $object->getEmailParams($send_params));
$this->Application->emailAdmin('USER.SUGGEST', null, $object->getEmailParams());
if ( $email_sent ) {
$event->SetRedirectParam('pass', 'all');
$event->redirect = $this->Application->GetVar('template_success');
}
else {
$event->status = kEvent::erFAIL;
$object->SetError('RecommendEmail', 'send_error');
}
}
/**
* Saves address changes and mades no redirect
*
* @param kEvent $event
*/
function OnUpdateAddress($event)
{
$object = $event->getObject(Array ('skip_autoload' => true));
/* @var $object kDBItem */
$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
if ( $items_info ) {
list ($id, $field_values) = each($items_info);
if ( $id > 0 ) {
$object->Load($id);
}
$object->setID($id);
$object->SetFieldsFromHash($field_values);
$event->setEventParam('form_data', $field_values);
$object->Validate();
}
$cs_helper = $this->Application->recallObject('CountryStatesHelper');
/* @var $cs_helper kCountryStatesHelper */
$cs_helper->PopulateStates($event, 'State', 'Country');
$event->redirect = false;
}
/**
* Validate subscriber's email & store it to session -> redirect to confirmation template
*
* @param kEvent $event
*/
function OnSubscribeQuery($event)
{
$object = $event->getObject( Array ('form_name' => 'subscription') );
/* @var $object UsersItem */
$object->SetFieldsFromHash($this->getSubmittedFields($event));
if ( !$object->ValidateField('SubscriberEmail') ) {
$event->status = kEvent::erFAIL;
return ;
}
$user_email = $object->GetDBField('SubscriberEmail');
$object->Load($user_email, 'Email');
$event->SetRedirectParam('subscriber_email', $user_email);
if ( $object->isLoaded() && $object->isSubscribed() ) {
$event->redirect = $this->Application->GetVar('unsubscribe_template');
}
else {
$event->redirect = $this->Application->GetVar('subscribe_template');
}
$event->SetRedirectParam('pass', 'm');
}
/**
* Subscribe/Unsubscribe user based on email stored in previous step
*
* @param kEvent $event
*/
function OnSubscribeUser($event)
{
$object = $event->getObject( Array ('form_name' => 'subscription') );
/* @var $object UsersItem */
$user_email = $this->Application->GetVar('subscriber_email');
$object->SetDBField('SubscriberEmail', $user_email);
if ( !$object->ValidateField('SubscriberEmail') ) {
$event->status = kEvent::erFAIL;
return ;
}
$username_required = $object->isRequired('Username');
$this->RemoveRequiredFields($object);
$object->Load($user_email, 'Email');
if ( $object->isLoaded() ) {
if ( $object->isSubscribed() ) {
if ( $event->getEventParam('no_unsubscribe') ) {
// for customization code from FormsEventHandler
return ;
}
if ( $object->isSubscriberOnly() ) {
$temp_handler = $this->Application->recallObject($event->Prefix . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event));
/* @var $temp_handler kTempTablesHandler */
$temp_handler->DeleteItems($event->Prefix, '', Array($object->GetID()));
}
else {
$this->RemoveSubscriberGroup( $object->GetID() );
}
$event->redirect = $this->Application->GetVar('unsubscribe_ok_template');
}
else {
$this->AddSubscriberGroup($object);
$event->redirect = $this->Application->GetVar('subscribe_ok_template');
}
}
else {
$object->generatePassword();
$object->SetDBField('Email', $user_email);
if ( $username_required ) {
$object->SetDBField('Username', str_replace('@', '_at_', $user_email));
}
$object->SetDBField('Status', STATUS_ACTIVE); // make user subscriber Active by default
if ( $object->Create() ) {
$this->AddSubscriberGroup($object);
$event->redirect = $this->Application->GetVar('subscribe_ok_template');
}
}
}
/**
* Adding user to subscribers group
*
* @param UsersItem $object
*/
function AddSubscriberGroup(&$object)
{
if ( !$object->isSubscriberOnly() ) {
$fields_hash = Array (
'PortalUserId' => $object->GetID(),
'GroupId' => $this->Application->ConfigValue('User_SubscriberGroup'),
);
$this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'UserGroupRelations');
}
$send_params = $object->getEmailParams();
$this->Application->emailUser('USER.SUBSCRIBE', $object->GetID(), $send_params);
$this->Application->emailAdmin('USER.SUBSCRIBE', null, $send_params);
}
/**
* Removing user from subscribers group
*
* @param int $user_id
*/
function RemoveSubscriberGroup($user_id)
{
$group_id = $this->Application->ConfigValue('User_SubscriberGroup');
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'UserGroupRelations
WHERE PortalUserId = ' . $user_id . ' AND GroupId = ' . $group_id;
$this->Conn->Query($sql);
$send_params = Array (
'PrefixSpecial' => 'u',
'item_id' => $user_id
);
$this->Application->emailUser('USER.UNSUBSCRIBE', $user_id, $send_params);
$this->Application->emailAdmin('USER.UNSUBSCRIBE', null, $send_params);
}
/**
* Validates forgot password form and sends password reset confirmation e-mail
*
* @param kEvent $event
* @return void
*/
function OnForgotPassword($event)
{
$object = $event->getObject( Array ('form_name' => 'forgot_password') );
/* @var $object kDBItem */
$object->SetFieldsFromHash($this->getSubmittedFields($event));
$user = $this->Application->recallObject('u.tmp', null, Array ('skip_autoload' => true));
/* @var $user UsersItem */
$found = $allow_reset = false;
$email_or_username = $object->GetDBField('ForgotLogin');
$is_email = strpos($email_or_username, '@') !== false;
if ( strlen($email_or_username) ) {
$user->Load($email_or_username, $is_email ? 'Email' : 'Username');
}
if ( $user->isLoaded() ) {
$min_pwd_reset_delay = $this->Application->ConfigValue('Users_AllowReset');
$found = ($user->GetDBField('Status') == STATUS_ACTIVE) && strlen($user->GetDBField('Password'));
if ( !$user->GetDBField('PwResetConfirm') ) {
// no reset made -> allow
$allow_reset = true;
}
else {
// reset made -> wait N minutes, then allow
$allow_reset = TIMENOW > $user->GetDBField('PwRequestTime') + $min_pwd_reset_delay;
}
}
if ($found && $allow_reset) {
$this->Application->emailUser('USER.PSWDC', $user->GetID(), $user->getEmailParams());
$event->redirect = $this->Application->GetVar('template_success');
return ;
}
if ( strlen($email_or_username) ) {
$object->SetError('ForgotLogin', $found ? 'reset_denied' : ($is_email ? 'unknown_email' : 'unknown_username'));
}
if ( !$object->ValidateField('ForgotLogin') ) {
$event->status = kEvent::erFAIL;
}
}
/**
* Updates kDBItem
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnUpdate(kEvent $event)
{
parent::OnUpdate($event);
if ( !$this->Application->isAdmin ) {
$this->setNextTemplate($event);
}
}
/**
+ * Updates kDBItem via AJAX.
+ *
+ * @param kEvent $event Event.
+ *
+ * @return void
+ */
+ protected function OnUpdateAjax(kEvent $event)
+ {
+ /** @var AjaxFormHelper $ajax_form_helper */
+ $ajax_form_helper = $this->Application->recallObject('AjaxFormHelper');
+ $ajax_form_helper->transitEvent($event, 'OnUpdate');
+ }
+
+ /**
* Checks state against country
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemUpdate(kEvent $event)
{
parent::OnBeforeItemUpdate($event);
$this->beforeItemChanged($event);
$cs_helper = $this->Application->recallObject('CountryStatesHelper');
/* @var $cs_helper kCountryStatesHelper */
$cs_helper->CheckStateField($event, 'State', 'Country');
$cs_helper->PopulateStates($event, 'State', 'Country');
$object = $event->getObject();
/* @var $object kDBItem */
if ( $event->Special == 'forgot' ) {
$object->SetDBField('PwResetConfirm', '');
$object->SetDBField('PwRequestTime_date', NULL);
$object->SetDBField('PwRequestTime_time', NULL);
}
$changed_fields = array_keys($object->GetChangedFields());
if ( $changed_fields && !in_array('Modified', $changed_fields) ) {
$object->SetDBField('Modified_date', time());
$object->SetDBField('Modified_time', time());
}
if ( !$this->Application->isAdmin && in_array('Email', $changed_fields) && ($event->Special != 'email-restore') ) {
$object->SetDBField('EmailVerified', 0);
}
}
/**
* Occurs before item is changed
*
* @param kEvent $event
*/
function beforeItemChanged($event)
{
$object = $event->getObject();
/* @var $object UsersItem */
if ( !$this->Application->isAdmin && $object->getFormName() == 'registration' ) {
// sets new user's status based on config options
$status_map = Array (1 => STATUS_ACTIVE, 2 => STATUS_DISABLED, 3 => STATUS_PENDING, 4 => STATUS_PENDING);
$object->SetDBField('Status', $status_map[ $this->Application->ConfigValue('User_Allow_New') ]);
if ( $this->Application->ConfigValue('User_Password_Auto') ) {
$object->generatePassword( rand(5, 8) );
}
if ( $this->Application->ConfigValue('RegistrationCaptcha') ) {
$captcha_helper = $this->Application->recallObject('CaptchaHelper');
/* @var $captcha_helper kCaptchaHelper */
$captcha_helper->validateCode($event, false);
}
if ( $event->Name == 'OnBeforeItemUpdate' ) {
// when a subscriber-only users performs normal registration, then assign him to Member group
$this->setUserGroup($object);
}
}
}
/**
* Sets redirect template based on user status & user request contents
*
* @param kEvent $event
* @param bool $for_registration
*/
function setNextTemplate($event, $for_registration = false)
{
$event->SetRedirectParam('opener', 's');
$object = $event->getObject();
/* @var $object UsersItem */
$next_template = false;
if ( $object->GetDBField('Status') == STATUS_ACTIVE && $this->Application->GetVar('next_template') ) {
$next_template = $this->Application->GetVar('next_template');
}
elseif ( $for_registration ) {
switch ( $this->Application->ConfigValue('User_Allow_New') ) {
case 1: // Immediate
$next_template = $this->Application->GetVar('registration_confirm_template');
break;
case 3: // Upon Approval
case 4: // Email Activation
$next_template = $this->Application->GetVar('registration_confirm_pending_template');
break;
}
}
if ($next_template) {
$event->redirect = $next_template;
}
}
/**
* Delete users from groups if their membership is expired
*
* @param kEvent $event
*/
function OnCheckExpiredMembership($event)
{
// send pre-expiration reminders: begin
$pre_expiration = time() + $this->Application->ConfigValue('User_MembershipExpirationReminder') * 3600 * 24;
$sql = 'SELECT PortalUserId, GroupId
FROM '.TABLE_PREFIX.'UserGroupRelations
WHERE (MembershipExpires IS NOT NULL) AND (ExpirationReminderSent = 0) AND (MembershipExpires < '.$pre_expiration.')';
$skip_clause = $event->getEventParam('skip_clause');
if ($skip_clause) {
$sql .= ' AND !('.implode(') AND !(', $skip_clause).')';
}
$records = $this->Conn->Query($sql);
if ($records) {
$conditions = Array();
/** @var UsersItem $user */
$user = $this->Application->recallObject('u', null, array('skip_autoload' => true));
foreach ($records as $record) {
$user->Load($record['PortalUserId']);
$this->Application->emailUser(
'USER.MEMBERSHIP.EXPIRATION.NOTICE',
$record['PortalUserId'],
$user->getEmailParams()
);
$this->Application->emailAdmin('USER.MEMBERSHIP.EXPIRATION.NOTICE', null, $user->getEmailParams());
$conditions[] = '(PortalUserId = '.$record['PortalUserId'].' AND GroupId = '.$record['GroupId'].')';
}
$sql = 'UPDATE '.TABLE_PREFIX.'UserGroupRelations
SET ExpirationReminderSent = 1
WHERE '.implode(' OR ', $conditions);
$this->Conn->Query($sql);
}
// send pre-expiration reminders: end
// remove users from groups with expired membership: begin
$sql = 'SELECT PortalUserId
FROM '.TABLE_PREFIX.'UserGroupRelations
WHERE (MembershipExpires IS NOT NULL) AND (MembershipExpires < '.time().')';
$user_ids = $this->Conn->GetCol($sql);
if ($user_ids) {
/** @var UsersItem $user */
$user = $this->Application->recallObject('u', null, array('skip_autoload' => true));
foreach ($user_ids as $id) {
$user->Load($id);
$this->Application->emailUser('USER.MEMBERSHIP.EXPIRED', $id, $user->getEmailParams());
$this->Application->emailAdmin('USER.MEMBERSHIP.EXPIRED', null, $user->getEmailParams());
}
}
$sql = 'DELETE FROM '.TABLE_PREFIX.'UserGroupRelations
WHERE (MembershipExpires IS NOT NULL) AND (MembershipExpires < '.time().')';
$this->Conn->Query($sql);
// remove users from groups with expired membership: end
}
/**
* Used to keep user registration form data, while showing affiliate registration form fields
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnRefreshForm($event)
{
$event->redirect = false;
$item_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
list($id, $field_values) = each($item_info);
$object = $event->getObject( Array ('skip_autoload' => true) );
/* @var $object kDBItem */
$object->IgnoreValidation = true;
$object->setID($id);
$object->SetFieldsFromHash($field_values);
$event->setEventParam('form_data', $field_values);
}
/**
* Sets persistant variable
*
* @param kEvent $event
*/
function OnSetPersistantVariable($event)
{
$field = $this->Application->GetVar('field');
$value = $this->Application->GetVar('value');
$this->Application->StorePersistentVar($field, $value);
$force_tab = $this->Application->GetVar('SetTab');
if ($force_tab) {
$this->Application->StoreVar('force_tab', $force_tab);
}
}
/**
* Return user from order by special .ord
*
* @param kEvent $event
* @return int
* @access public
*/
public function getPassedID(kEvent $event)
{
switch ($event->Special) {
case 'ord':
$order = $this->Application->recallObject('ord');
/* @var $order OrdersItem */
return $order->GetDBField('PortalUserId');
break;
case 'profile':
$id = $this->Application->GetVar('user_id');
if ( $id ) {
$event->setEventParam(kEvent::FLAG_ID_FROM_REQUEST, true);
return $id;
}
// If none user_id given use current user id.
return $this->Application->RecallVar('user_id');
break;
case 'forgot':
/* @var $user_helper UserHelper */
$user_helper = $this->Application->recallObject('UserHelper');
$id = $user_helper->validateUserCode($this->Application->GetVar('user_key'), 'forgot_password');
if ( is_numeric($id) ) {
return $id;
}
break;
}
if ( preg_match('/^(login|register|recommend|subscribe|forgot)/', $event->Special) ) {
// this way we can have 2+ objects stating with same special, e.g. "u.login-sidebox" and "u.login-main"
return USER_GUEST;
}
elseif ( preg_match('/^(update|delete)/', $event->Special) ) {
// This way we can have 2+ objects stating with same special, e.g. "u.update-sidebox" and "u.update-profile".
return $this->Application->RecallVar('user_id');
}
return parent::getPassedID($event);
}
/**
* Allows to change root password
*
* @param kEvent $event
* @return void
* @access protected
*/
protected 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 ( $id == $user_id && ($user_id > 0 || $user_id == USER_ROOT) ) {
$user_dummy = $this->Application->recallObject($event->Prefix . '.-item', null, Array ('skip_autoload' => true));
/* @var $user_dummy kDBItem */
$user_dummy->Load($id);
$status_field = $event->getUnitConfig()->getStatusField(true);
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 ;
}
}
if ( $user_id == USER_ROOT ) {
$object = $event->getObject(Array ('skip_autoload' => true));
/* @var $object UsersItem */
// this is internal hack to allow root/root passwords for dev
if ( $this->Application->isDebugMode() && $field_values['RootPassword'] == 'root' ) {
$object->SetFieldOption('RootPassword', 'min_length', 4);
}
$this->RemoveRequiredFields($object);
$object->SetDBField('RootPassword', $this->Application->ConfigValue('RootPass'));
$object->setID(-1);
$object->SetFieldsFromHash($field_values);
$event->setEventParam('form_data', $field_values);
if ( $object->Validate() ) {
// validation on, password match too
$fields_hash = Array ('VariableValue' => $object->GetDBField('RootPassword'));
$conf_table = $this->Application->getUnitConfig('conf')->getTableName();
$this->Conn->doUpdate($fields_hash, $conf_table, 'VariableName = "RootPass"');
$event->SetRedirectParam('opener', 'u');
}
else {
$event->status = kEvent::erFAIL;
$event->redirect = false;
return ;
}
}
else {
/** @var kDBItem $object */
$object = $event->getObject();
$object->SetFieldsFromHash($field_values);
$event->setEventParam('form_data', $field_values);
if ( !$object->Update() ) {
$event->status = kEvent::erFAIL;
$event->redirect = false;
}
}
$event->SetRedirectParam('opener', 'u');
}
/**
* Resets grid settings, remembered in each user record
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnMassResetSettings($event)
{
if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
$event->status = kEvent::erFAIL;
return;
}
$ids = $this->StoreSelectedIDs($event);
$default_user_id = $this->Application->ConfigValue('DefaultSettingsUserId');
if ( in_array($default_user_id, $ids) ) {
array_splice($ids, array_search($default_user_id, $ids), 1);
}
if ( $ids ) {
$q = 'DELETE FROM ' . TABLE_PREFIX . 'UserPersistentSessionData WHERE PortalUserId IN (' . join(',', $ids) . ') AND
(VariableName LIKE "%_columns_%"
OR
VariableName LIKE "%_filter%"
OR
VariableName LIKE "%_PerPage%")';
$this->Conn->Query($q);
}
$this->clearSelectedIDs($event);
}
/**
* Checks, that currently loaded item is allowed for viewing (non permission-based)
*
* @param kEvent $event
* @return bool
* @access protected
*/
protected function checkItemStatus(kEvent $event)
{
$object = $event->getObject();
/* @var $object kDBItem */
if ( !$object->isLoaded() ) {
return true;
}
$virtual_users = Array (USER_ROOT, USER_GUEST);
return ($object->GetDBField('Status') == STATUS_ACTIVE) || in_array($object->GetID(), $virtual_users);
}
/**
* Sends approved/declined email event on user status change
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemUpdate(kEvent $event)
{
parent::OnAfterItemUpdate($event);
$this->afterItemChanged($event);
$object = $event->getObject();
/* @var $object UsersItem */
if ( !$this->Application->isAdmin && ($event->Special != 'email-restore') ) {
$this->sendEmailChangeEvent($event);
}
if ( !$this->Application->isAdmin || $object->IsTempTable() ) {
return;
}
$this->sendStatusChangeEvent($object->GetID(), $object->GetOriginalField('Status'), $object->GetDBField('Status'));
}
/**
* Occurs, after item is changed
*
* @param kEvent $event
*/
protected function afterItemChanged($event)
{
$this->saveUserImages($event);
$object = $event->getObject();
/* @var $object UsersItem */
if ( $object->GetDBField('EmailPassword') && $object->GetDBField('Password_plain') ) {
$email_passwords = $this->Application->RecallVar('email_passwords');
$email_passwords = $email_passwords ? unserialize($email_passwords) : Array ();
$email_passwords[ $object->GetID() ] = $object->GetDBField('Password_plain');
$this->Application->StoreVar('email_passwords', serialize($email_passwords));
}
// update user subscription status (via my profile or new user registration)
if ( !$this->Application->isAdmin && !$object->isSubscriberOnly() ) {
if ( $object->GetDBField('SubscribeToMailing') && !$object->isSubscribed() ) {
$this->AddSubscriberGroup($object);
}
elseif ( !$object->GetDBField('SubscribeToMailing') && $object->isSubscribed() ) {
$this->RemoveSubscriberGroup( $object->GetID() );
}
}
}
/**
* Stores user's original Status before overwriting with data from temp table
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeDeleteFromLive(kEvent $event)
{
parent::OnBeforeDeleteFromLive($event);
$user_id = $event->getEventParam('id');
$user_status = $this->Application->GetVar('user_status', Array ());
if ( $user_id > 0 ) {
$user_status[$user_id] = $this->getUserStatus($user_id);
$this->Application->SetVar('user_status', $user_status);
}
}
/**
* Sends approved/declined email event on user status change (in temp tables during editing)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterCopyToLive(kEvent $event)
{
parent::OnAfterCopyToLive($event);
$temp_id = $event->getEventParam('temp_id');
$email_passwords = $this->Application->RecallVar('email_passwords');
if ( $email_passwords ) {
$email_passwords = unserialize($email_passwords);
if ( isset($email_passwords[$temp_id]) ) {
$object = $event->getObject();
/* @var $object kDBItem */
$object->SwitchToLive();
$object->Load( $event->getEventParam('id') );
$object->SetField('Password', $email_passwords[$temp_id]);
$object->SetField('VerifyPassword', $email_passwords[$temp_id]);
$this->Application->emailUser($temp_id > 0 ? 'USER.NEW.PASSWORD': 'USER.ADD.BYADMIN', $object->GetID(), $object->getEmailParams());
unset($email_passwords[$temp_id]);
$this->Application->StoreVar('email_passwords', serialize($email_passwords));
}
}
if ( $temp_id > 0 ) {
// only send status change e-mail on user update
$new_status = $this->getUserStatus($temp_id);
$user_status = $this->Application->GetVar('user_status');
$this->sendStatusChangeEvent($temp_id, $user_status[$temp_id], $new_status);
}
}
/**
* Returns user status (active, pending, disabled) based on ID and temp mode setting
*
* @param int $user_id
* @return int
*/
function getUserStatus($user_id)
{
$config = $this->getUnitConfig();
$sql = 'SELECT Status
FROM '. $config->getTableName() .'
WHERE '. $config->getIDField() .' = '.$user_id;
return $this->Conn->GetOne($sql);
}
/**
* Sends approved/declined email event on user status change
*
* @param int $user_id
* @param int $prev_status
* @param int $new_status
*/
function sendStatusChangeEvent($user_id, $prev_status, $new_status)
{
$status_events = Array (
STATUS_ACTIVE => 'USER.APPROVE',
STATUS_DISABLED => 'USER.DENY',
);
$email_event = isset($status_events[$new_status]) ? $status_events[$new_status] : false;
if (($prev_status != $new_status) && $email_event) {
$send_params = Array (
'PrefixSpecial' => 'u',
'item_id' => $user_id,
);
$this->Application->emailUser($email_event, $user_id, $send_params);
$this->Application->emailAdmin($email_event, null, $send_params);
}
// deletes sessions from users, that are no longer active
if (($prev_status != $new_status) && ($new_status != STATUS_ACTIVE)) {
$sql = 'SELECT SessionKey
FROM ' . TABLE_PREFIX . 'UserSessions
WHERE PortalUserId = ' . $user_id;
$session_ids = $this->Conn->GetCol($sql);
$this->Application->Session->DeleteSessions($session_ids);
}
}
/**
* Sends restore/validation email event on user email change
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function sendEmailChangeEvent(kEvent $event)
{
$object = $event->getObject();
/* @var $object UsersItem */
$new_email = $object->GetDBField('Email');
$prev_email = $object->GetOriginalField('Email');
if ( !$new_email || ($prev_email == $new_email) ) {
return;
}
$prev_emails = $object->GetDBField('PrevEmails');
$prev_emails = $prev_emails ? unserialize($prev_emails) : Array ();
$fields_hash = Array (
'PrevEmails' => serialize($prev_emails),
'EmailVerified' => 0,
);
$user_id = $object->GetID();
if ( $prev_email ) {
$hash = md5(TIMENOW + $user_id);
$prev_emails[$hash] = $prev_email;
$fields_hash['PrevEmails'] = serialize($prev_emails);
$send_params = Array (
'hash' => $hash,
'to_email' => $prev_email,
'to_name' => trim($object->GetDBField('FirstName') . ' ' . $object->GetDBField('LastName')),
);
$this->Application->emailUser('USER.EMAIL.CHANGE.UNDO', null, $object->getEmailParams($send_params));
}
if ( $new_email ) {
$this->Application->emailUser('USER.EMAIL.CHANGE.VERIFY', $user_id, $object->getEmailParams());
}
// direct DB update, since USER.EMAIL.CHANGE.VERIFY puts verification code in user record, that we don't want to loose
$this->Conn->doUpdate($fields_hash, $object->TableName, 'PortalUserId = ' . $user_id);
}
/**
* OnAfterConfigRead for users
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterConfigRead(kEvent $event)
{
parent::OnAfterConfigRead($event);
$config = $event->getUnitConfig();
$default_form = $config->getFormByName('default');
$form_fields =& $default_form['Fields'];
// 1. arrange user registration countries
$site_helper = $this->Application->recallObject('SiteHelper');
/* @var $site_helper SiteHelper */
$first_country = $site_helper->getDefaultCountry('', false);
if ($first_country === false) {
$first_country = $this->Application->ConfigValue('User_Default_Registration_Country');
}
if ($first_country) {
// update user country dropdown sql
$form_fields['Country']['options_sql'] = preg_replace('/ORDER BY (.*)/', 'ORDER BY IF (CountryStateId = '.$first_country.', 1, 0) DESC, \\1', $form_fields['Country']['options_sql']);
}
// 2. set default user registration group
$form_fields['PrimaryGroupId']['default'] = $this->Application->ConfigValue('User_NewGroup');
// 3. allow avatar upload on Front-End
$file_helper = $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
$file_helper->createItemFiles($event->Prefix, true); // create image fields
if ($this->Application->isAdminUser) {
// 4. when in administrative console, then create all users with Active status
$form_fields['Status']['default'] = STATUS_ACTIVE;
// 5. remove groups tab on editing forms when AdvancedUserManagement config variable not set
if (!$this->Application->ConfigValue('AdvancedUserManagement')) {
$edit_tab_presets = $config->getEditTabPresets();
foreach ($edit_tab_presets as $preset_name => $preset_tabs) {
if (array_key_exists('groups', $preset_tabs)) {
unset($edit_tab_presets[$preset_name]['groups']);
if (count($edit_tab_presets[$preset_name]) == 1) {
// only 1 tab left -> remove it too
$edit_tab_presets[$preset_name] = Array ();
}
}
}
$config->setEditTabPresets($edit_tab_presets);
}
}
if ( $this->Application->ConfigValue('RegistrationUsernameRequired') ) {
// Username becomes required only, when it's used in registration process
$max_username = $this->Application->ConfigValue('MaxUserName');
$form_fields['Username']['required'] = 1;
$form_fields['Username']['min_len'] = $this->Application->ConfigValue('Min_UserName');
$form_fields['Username']['max_len'] = $max_username ? $max_username : 255;
}
$config->addForms($default_form, 'default');
}
/**
* OnMassCloneUsers
*
* @param kEvent $event
*/
function OnMassCloneUsers($event)
{
if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
$event->status = kEvent::erFAIL;
return;
}
$temp_handler = $this->Application->recallObject($event->Prefix.'_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event));
/* @var $temp_handler kTempTablesHandler */
$ids = $this->StoreSelectedIDs($event);
$temp_handler->CloneItems($event->Prefix, '', $ids);
$this->clearSelectedIDs($event);
}
/**
* When cloning users, reset password (set random)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeClone(kEvent $event)
{
parent::OnBeforeClone($event);
$object = $event->getObject();
/* @var $object UsersItem */
$object->generatePassword();
$object->SetDBField('ResourceId', 0); // this will reset it
// change email because it should be unique
$object->NameCopy(Array (), $object->GetID(), 'Email', 'copy%1$s.%2$s');
}
/**
* Saves selected ids to session
*
* @param kEvent $event
*/
function OnSaveSelected($event)
{
$this->StoreSelectedIDs($event);
// remove current ID, otherwise group selector will use it in filters
$this->Application->DeleteVar($event->getPrefixSpecial(true) . '_id');
}
/**
* Sets primary group of selected users
*
* @param kEvent $event
*/
function OnProcessSelected($event)
{
$event->SetRedirectParam('opener', 'u');
$user_ids = $this->getSelectedIDs($event, true);
$this->clearSelectedIDs($event);
$dst_field = $this->Application->RecallVar('dst_field');
if ($dst_field != 'PrimaryGroupId') {
return ;
}
$group_ids = array_keys($this->Application->GetVar('g'));
$primary_group_id = $group_ids ? array_shift($group_ids) : false;
if (!$user_ids || !$primary_group_id) {
return ;
}
$table_name = $this->Application->getUnitConfig('ug')->getTableName();
// 1. mark group as primary
$sql = 'UPDATE ' . TABLE_PREFIX . 'Users
SET PrimaryGroupId = ' . $primary_group_id . '
WHERE PortalUserId IN (' . implode(',', $user_ids) . ')';
$this->Conn->Query($sql);
$sql = 'SELECT PortalUserId
FROM ' . $table_name . '
WHERE (GroupId = ' . $primary_group_id . ') AND (PortalUserId IN (' . implode(',', $user_ids) . '))';
$existing_members = $this->Conn->GetCol($sql);
// 2. add new members to a group
$new_members = array_diff($user_ids, $existing_members);
foreach ($new_members as $user_id) {
$fields_hash = Array (
'GroupId' => $primary_group_id,
'PortalUserId' => $user_id,
);
$this->Conn->doInsert($fields_hash, $table_name);
}
}
/**
* Loads user images
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemLoad(kEvent $event)
{
parent::OnAfterItemLoad($event);
// linking existing images for item with virtual fields
$image_helper = $this->Application->recallObject('ImageHelper');
/* @var $image_helper ImageHelper */
$object = $event->getObject();
/* @var $object UsersItem */
$image_helper->LoadItemImages($object);
$cs_helper = $this->Application->recallObject('CountryStatesHelper');
/* @var $cs_helper kCountryStatesHelper */
$cs_helper->PopulateStates($event, 'State', 'Country');
// get user subscription status
$object->SetDBField('SubscribeToMailing', $object->isSubscribed() ? 1 : 0);
if ( !$this->Application->isAdmin ) {
$object->SetFieldOption('FrontLanguage', 'options', $this->getEnabledLanguages());
}
}
/**
* Returns list of enabled languages with their names
*
* @return Array
* @access protected
*/
protected function getEnabledLanguages()
{
$cache_key = 'user_languages[%LangSerial%]';
$ret = $this->Application->getCache($cache_key);
if ( $ret === false ) {
$languages = $this->Application->recallObject('lang.enabled', 'lang_List');
/* @var $languages kDBList */
$ret = Array ();
foreach ($languages as $language_info) {
$ret[$languages->GetID()] = $language_info['LocalName'];
}
$this->Application->setCache($cache_key, $ret);
}
return $ret;
}
/**
* Save user images
*
* @param kEvent $event
*/
function saveUserImages($event)
{
if (!$this->Application->isAdmin) {
$image_helper = $this->Application->recallObject('ImageHelper');
/* @var $image_helper ImageHelper */
$object = $event->getObject();
/* @var $object kDBItem */
// process image upload in virtual fields
$image_helper->SaveItemImages($object);
}
}
/**
* Makes password required for new users
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreCreate(kEvent $event)
{
parent::OnPreCreate($event);
if ( $event->status != kEvent::erSUCCESS ) {
return;
}
$object = $event->getObject();
/* @var $object kDBItem */
$user_type = $this->Application->GetVar('user_type');
if ( $user_type ) {
$object->SetDBField('UserType', $user_type);
if ( $user_type == UserType::ADMIN ) {
$object->SetDBField('PrimaryGroupId', $this->Application->ConfigValue('User_AdminGroup'));
}
}
if ( $this->Application->ConfigValue('User_Password_Auto') ) {
$object->SetDBField('EmailPassword', 1);
}
$this->_makePasswordRequired($event);
}
/**
* Makes password required for new users
*
* @param kEvent $event
*/
function _makePasswordRequired($event)
{
$object = $event->getObject();
/* @var $object kDBItem */
$required_fields = Array ('Password', 'Password_plain', 'VerifyPassword', 'VerifyPassword_plain');
$object->setRequired($required_fields);
}
/**
* Load item if id is available
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function LoadItem(kEvent $event)
{
$id = $this->getPassedID($event);
if ( $id < 0 ) {
// when root, guest and so on
$object = $event->getObject();
/* @var $object kDBItem */
$object->Clear($id);
return;
}
parent::LoadItem($event);
}
/**
* Occurs just after login (for hooking)
*
* @param kEvent $event
*/
function OnAfterLogin($event)
{
if ( is_object($event->MasterEvent) && !$this->Application->isAdmin ) {
$event->MasterEvent->SetRedirectParam('login', 1);
}
}
/**
* Occurs just before logout (for hooking)
*
* @param kEvent $event
*/
function OnBeforeLogout($event)
{
if ( is_object($event->MasterEvent) && !$this->Application->isAdmin ) {
$event->MasterEvent->SetRedirectParam('logout', 1);
}
}
/**
* Generates password
*
* @param kEvent $event
*/
function OnGeneratePassword($event)
{
$event->status = kEvent::erSTOP;
if ( $this->Application->isAdminUser ) {
echo kUtil::generatePassword();
}
}
/**
* Changes user's password and logges him in
*
* @param kEvent $event
*/
function OnResetLostPassword($event)
{
$object = $event->getObject();
/* @var $object kDBItem */
$event->CallSubEvent('OnUpdate');
if ( $event->status == kEvent::erSUCCESS ) {
/* @var $user_helper UserHelper */
$user_helper = $this->Application->recallObject('UserHelper');
$user =& $user_helper->getUserObject();
$user->Load( $object->GetID() );
if ( $user_helper->checkLoginPermission() ) {
$user_helper->loginUserById( $user->GetID() );
}
}
}
/**
* Generates new Root password and email it
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnResetRootPassword($event)
{
$password_formatter = $this->Application->recallObject('kPasswordFormatter');
/* @var $password_formatter kPasswordFormatter */
$new_root_password = kUtil::generatePassword();
$this->Application->SetConfigValue('RootPass', $password_formatter->hashPassword($new_root_password));
$this->Application->emailAdmin('ROOT.RESET.PASSWORD', null, Array ('password' => $new_root_password));
$event->SetRedirectParam('reset', 1);
$event->SetRedirectParam('pass', 'm');
}
/**
* Perform login of user, selected in Admin Console, on Front-End in a separate window
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnLoginAs(kEvent $event)
{
/* @var $user_helper UserHelper */
$user_helper = $this->Application->recallObject('UserHelper');
$user =& $user_helper->getUserObject();
$user->Load( $this->Application->GetVar('user_id') );
if ( !$user->isLoaded() ) {
return ;
}
if ( $user_helper->checkLoginPermission() ) {
$user_helper->loginUserById( $user->GetID() );
}
}
}
Index: branches/5.3.x/core/units/helpers/fck_helper.php
===================================================================
--- branches/5.3.x/core/units/helpers/fck_helper.php (revision 16502)
+++ branches/5.3.x/core/units/helpers/fck_helper.php (revision 16503)
@@ -1,635 +1,641 @@
folder = $this->Application->GetVar('folder');
$this->sortField = $this->Application->GetVar('sort_by');
$this->sortDirection = $this->Application->GetVar('order_by');
$this->Config['AllowedExtensions']['Files'] = Array('gif','jpeg','png','swf','fla','jpg','gif','jpeg','png','avi','mpg','mpeg','zip','rar','arj','gz','tar','doc','pdf','ppt','rdp','swf','swt','txt','vsd','xls','csv','odt');
$this->Config['DeniedExtensions']['Files'] = Array('php','asp','aspx','ascx','jsp','cfm','cfc','pl','bat','exe','dll','reg');
$this->Config['AllowedExtensions']['Images'] = Array('jpg','gif','jpeg','png', 'bmp');
$this->Config['DeniedExtensions']['Images'] = Array('php','asp','aspx','ascx','jsp','cfm','cfc','pl','bat','exe','dll','reg');
$this->Config['AllowedExtensions']['Flash'] = Array('swf','fla');
$this->Config['DeniedExtensions']['Flash'] = Array('php','asp','aspx','ascx','jsp','cfm','cfc','pl','bat','exe','dll','reg');
$this->Config['AllowedExtensions']['Media'] = Array('asf','asx','avi','wav','wax','wma','wm','wmv','m3u','mp2v','mpg','mpeg','m1v','mp2','mp3','mpa','mpe','mpv2','mp4','mid','midi','rmi','qt','aif','aifc','aiff','mov','flv','rm','svcd','swf','vcd');
$this->Config['DeniedExtensions']['Media'] = Array('php','asp','aspx','ascx','jsp','cfm','cfc','pl','bat','exe','dll','reg');
$this->Config['AllowedExtensions']['Documents'] = Array('doc','pdf','ppt','rdp','swf','swt','txt','vsd','xls','csv','zip','odt');
$this->Config['DeniedExtensions']['Documents'] = Array('php','asp','aspx','ascx','jsp','cfm','cfc','pl','bat','exe','dll','reg');
$this->Config['ExtensionIcons'] = Array('ai','avi','bmp','cs','dll','doc','exe','fla','gif','htm','html','jpg','js','mdb','mp3','pdf','png','ppt','rdp','swf','swt','txt','vsd','xls','xml','zip');
}
function CreateFolder($folder = '')
{
if ( !$folder ) {
return false;
}
$folderPath = WRITEABLE . '/user_files/' . $folder;
if ( file_exists($folderPath) && is_dir($folderPath) ) {
return true;
}
/*$permissions = defined('FCK_FOLDERS_PERMISSIONS') ? FCK_FOLDERS_PERMISSIONS : '0777';
return mkdir($folderPath, $permissions);*/
return mkdir($folderPath);
}
function IsAllowedExtension($folder, $file_name)
{
$ext = strtolower( pathinfo($file_name, PATHINFO_EXTENSION) );
if ( isset($this->Config['DeniedExtensions'][$folder]) ) {
if ( in_array($ext, $this->Config['DeniedExtensions'][$folder]) ) {
return false;
}
}
if ( isset($this->Config['AllowedExtensions'][$folder]) ) {
if ( !in_array($ext, $this->Config['AllowedExtensions'][$folder]) ) {
return false;
}
}
return true;
}
/**
* Returns list of sub-folders from given folder (automatically excludes system folders)
*
* @param string $files_dir
* @return Array
* @access public
*/
public function ReadFolders($files_dir)
{
$ret = Array ();
$system_folders = defined('KERNEL_SYSTEM_FOLDERS') ? KERNEL_SYSTEM_FOLDERS : Array ('icons', 'CVS', '.svn');
try {
$iterator = new DirectoryIterator($files_dir);
/* @var $file_info DirectoryIterator */
}
catch (UnexpectedValueException $e) {
return $ret;
}
foreach ($iterator as $file_info) {
$filename = $file_info->getFilename();
if ( $file_info->isDir() && !$file_info->isDot() ) {
$ret[] = $filename;
}
}
return array_diff($ret, $system_folders);
}
/**
* Returns list of files in given folder
*
* @param string $files_dir
* @return Array
* @access public
*/
public function ReadFiles($files_dir)
{
$ret = Array ();
try {
$iterator = new DirectoryIterator($files_dir);
/* @var $file_info DirectoryIterator */
}
catch (UnexpectedValueException $e) {
return $ret;
}
foreach ($iterator as $file_info) {
if ( !$file_info->isDir() ) {
$ret[] = $file_info->getFilename();
}
}
return $ret;
}
/**
* Returns xml containing list of folders in current folder
*
* @return string
* @access public
*/
public function PrintFolders()
{
$files_dir = WRITEABLE . '/user_files/' . $this->folder . '/';
$sub_folders = $this->ReadFolders($files_dir);
natcasesort($sub_folders);
$ret = $this->_buildFoldersXML($sub_folders, 'folder2');
if ( $this->sortField == 'name' && $this->sortDirection == '_desc' ) {
$sub_folders = array_reverse($sub_folders);
}
$ret .= $this->_buildFoldersXML($sub_folders, 'folder');
return $ret;
}
/**
* Build XML, that will output folders for FCKEditor
*
* @param Array $sub_folders
* @param string $xml_node
* @return string
*/
protected function _buildFoldersXML($sub_folders, $xml_node)
{
$ret = '';
foreach ($sub_folders as $sub_folder) {
$ret .= '<' . $xml_node . ' path="' . $this->folder . "/" . $sub_folder . '">' . $sub_folder . '' . $xml_node . '>' . "\n";
}
return $ret;
}
/**
* Transforms filesize in bytes into kilobytes
*
* @param int $size
* @return int
* @access protected
*/
protected function CalculateFileSize($size)
{
if ( $size > 0 ) {
$size = round($size / 1024);
$size = ($size < 1) ? 1 : $size;
}
return $size;
}
/**
* Detects icon for given file extension
*
* @param string $file
* @return string
* @access protected
*/
protected function CheckIconType($file)
{
$ext = strtolower( pathinfo($file, PATHINFO_EXTENSION) );
return $ext && in_array($ext, $this->Config['ExtensionIcons']) ? $ext : 'default.icon';
}
/**
* Build one file xml node
*
* @param int $size
* @param string $url
* @param string $icon
* @param string $date
* @param string $file_name
* @return string
*/
protected function _buildFileXml($size,$url,$icon,$date,$file_name)
{
return '' . $file_name . '' . "\n";
}
/**
* Returns xml containing list of files in current folder
*
* @return string
* @access public
*/
public function PrintFiles()
{
$files_dir = WRITEABLE . '/user_files/' . $this->folder . '/';
$files_url = BASE_PATH . str_replace(DIRECTORY_SEPARATOR, '/', WRITEBALE_BASE) . '/user_files/' . $this->folder . '/';
$aFiles = $this->ReadFiles($files_dir);
$ret = '';
$date_format = "m/d/Y h:i A";
natcasesort($aFiles);
if ( $this->sortField == 'name' && $this->sortDirection == '_desc' ) {
$aFiles = array_reverse($aFiles, TRUE);
}
$aFilesSize = $aFilesDate = Array ();
foreach ($aFiles as $k => $v) {
$aFilesSize[$k] = filesize($files_dir . $v);
$aFilesDate[$k] = filectime($files_dir . $v);
}
if ( $this->sortField == 'name' ) {
foreach ($aFiles as $k => $file) {
$size = $this->CalculateFileSize($aFilesSize[$k]);
$date = date($date_format, $aFilesDate[$k]);
$icon = $this->CheckIconType($file);
$ret .= $this->_buildFileXml($size, $files_url . $file, $icon, $date, $file);
}
}
if ( $this->sortField == 'date' ) {
asort($aFilesDate);
if ( $this->sortDirection == '_desc' ) {
$aFilesDate = array_reverse($aFilesDate, TRUE);
}
foreach ($aFilesDate as $k => $date) {
$size = $this->CalculateFileSize($aFilesSize[$k]);
$file = $aFiles[$k];
$date = date($date_format, $date);
$icon = $this->CheckIconType($file);
$ret .= $this->_buildFileXml($size, $files_url . $file, $icon, $date, $file);
}
}
if ( $this->sortField == 'size' ) {
asort($aFilesSize);
if ( $this->sortDirection == '_desc' ) {
$aFilesSize = array_reverse($aFilesSize, TRUE);
}
foreach ($aFilesSize as $k => $size) {
$size = $this->CalculateFileSize($size);
$file = $aFiles[$k];
$date = date($date_format, $aFilesDate[$k]);
$icon = $this->CheckIconType($file);
$ret .= $this->_buildFileXml($size, $files_url . $file, $icon, $date, $file);
}
}
return $ret;
}
function UploadFile()
{
$upload_dir = $this->Application->GetVar('upload_dir');
$type = explode('/', $upload_dir);
$type = $type[0];
$sServerDir = WRITEABLE . '/user_files/' . $upload_dir . '/';
$aUpFile = $_FILES['NewFile'];
$sFileName = $aUpFile['name'];
$sOriginalFileName = $aUpFile['name'];
$sExtension = strtolower(substr( $sFileName, ( strrpos($sFileName, '.') + 1 ) ) );
$sErrorNumber = 0;
if ( isset( $_FILES['NewFile'] ) && !is_null( $_FILES['NewFile']['tmp_name'] ) )
{
if (in_array($sExtension, $this->Config['AllowedExtensions'][$type]))
{
if (!$aUpFile['error']) {
$iCounter = 0 ;
while ( true )
{
$sFilePath = $sServerDir . $sFileName;
if ( is_file( $sFilePath ) )
{
$iCounter++ ;
$sFileName = $this->RemoveExtension( $sOriginalFileName ) . '(' . $iCounter . ').' . $sExtension;
$sErrorNumber = '201';
}
else
{
// Turn off all error reporting.
error_reporting( 0 ) ;
// Enable error tracking to catch the error.
ini_set( 'track_errors', '1' );
move_uploaded_file( $aUpFile['tmp_name'], $sFilePath );
$sErrorMsg = $php_errormsg;
// Restore the configurations.
ini_restore( 'track_errors' );
ini_restore( 'error_reporting' );
if ( is_file( $sFilePath ) ) {
$oldumask = umask(0);
chmod( $sFilePath, 0666 );
umask( $oldumask );
}
break ;
}
}
}
}
else {
$sErrorNumber = '203';
}
}
else {
$sErrorNumber = '202' ;
}
echo '' ;
}
function RemoveExtension( $fileName )
{
return substr( $fileName, 0, strrpos( $fileName, '.' ) ) ;
}
public function CKEditorTag($editor_name, $editor_value, $params)
{
$editor = $this->prepareConfig($this->getEditor(), $params);
$width = $this->normalizeDimension($params['width']);
$height = $this->normalizeDimension($params['height']);
$editor->textareaAttributes = Array (
'style' => 'width: ' . $width . '; height: ' . $height . ';'
);
$editor->config['height'] = $height; // editor area height
$events = Array (
'configLoaded' => 'function(ev) { CKEDITOR.addCss(ev.editor.config.extraCss); }',
);
return $editor->editor($editor_name, $editor_value, Array (), $events);
}
public function CKEditorInlineTag($editor_name, $params)
{
$editor = $this->prepareConfig($this->getEditor(), $params);
$events = Array (
'configLoaded' => 'function(ev) { CKEDITOR.addCss(ev.editor.config.extraCss); }',
'focus' => 'function(ev) { $("body").trigger("InlineEditor.Focus", [ev]); }',
'blur' => 'function(ev) { $("body").trigger("InlineEditor.Blur", [ev]); }',
);
- return $editor->inline($editor_name, Array (), $events);
+ return $editor->inline($editor_name, array('removePlugins' => 'codemirror'), $events);
}
/**
* Adds measurement units to editor dimensions.
*
* @param string $dimension Dimension.
*
* @return string
*/
protected function normalizeDimension($dimension)
{
if ( preg_match('/^[\d]+$/', $dimension) ) {
$dimension .= 'px';
}
return $dimension;
}
/**
* Returns editor instance.
*
* @return CKEditor
*/
protected function getEditor()
{
include_once(FULL_PATH . EDITOR_PATH . 'ckeditor.php');
$editor = new CKeditor(BASE_PATH . EDITOR_PATH);
$editor->returnOutput = true;
return $editor;
}
/**
* Prepares editor config.
*
* @param CKEditor $editor Editor.
* @param array $tag_params Tag params.
*
* @return CKEditor
*/
protected function prepareConfig(CKEditor $editor, array $tag_params)
{
$editor->lateLoad = array_key_exists('late_load', $tag_params) && $tag_params['late_load'];
list($styles_css, $styles_js) = $this->getStyles();
if ( isset($tag_params['toolbar']) ) {
$toolbar = $tag_params['toolbar'];
}
elseif ( isset($tag_params['mode']) && $tag_params['mode'] == 'inline' ) {
$toolbar = 'Inline';
}
else {
$toolbar = $this->Application->isDebugMode() ? 'DebugMode' : 'Default';
}
$editor->config = Array (
'toolbar' => $toolbar,
'baseHref' => $this->Application->BaseURL() . trim(EDITOR_PATH, '/') . '/',
'customConfig' => $this->getJavaScriptConfig(),
'stylesSet' => 'portal:' . $styles_js,
'contentsCss' => $styles_css,
'Admin' => 1, // for custom file browser to work
'K4' => 1, // for custom file browser to work
'language' => $this->getLanguage(),
);
$this->injectTransitParams($editor, $this->getTransitParams($tag_params));
return $editor;
}
/**
* Transforms transit params into editor config.
*
* @param CKEditor $editor Editor.
* @param array $transit_params Transit params.
*
* @return void
*/
protected function injectTransitParams(CKEditor $editor, array $transit_params)
{
if ( isset($transit_params['bgcolor']) && $transit_params['bgcolor'] ) {
$editor->config['extraCss'] = 'body { background-color: ' . $transit_params['bgcolor'] . '; }';
}
foreach ($transit_params as $param_name => $param_value) {
if ( !$param_value ) {
continue;
}
$param_key = str_replace(' ', '', ucwords(str_replace('_', ' ', $param_name)));
$param_key[0] = strtolower($param_key[0]);
$editor->config[$param_key] = $param_value;
}
if ( isset($editor->config['styleSetName']) ) {
$style_set_parts = explode(':', $editor->config['stylesSet']);
$style_set_parts[0] = $editor->config['styleSetName'];
$editor->config['stylesSet'] = implode(':', $style_set_parts);
unset($editor->config['styleSetName']);
}
}
/**
* Returns url to CSS and JS style configuration.
*
* @return array
*/
protected function getStyles()
{
/** @var ThemeItem $theme */
$theme = $this->Application->recallObject('theme.current');
$stylesheet_file = $theme->getStylesheetFile(true);
if ( $stylesheet_file ) {
$stylesheet_folder_url = dirname($stylesheet_file) . '/';
$url_params = Array ('events[fck]' => 'OnGetsEditorStyles', 'no_pass_through' => 1, 'pass' => 'm');
$prefix = $this->Application->isAdmin ? '_FRONT_END_' : '';
$styles_css = $this->Application->HREF('index', $prefix, $url_params, 'index.php');
+
+ /** @var kCurlHelper $curl_helper */
+ $curl_helper = $this->Application->recallObject('CurlHelper');
+ $curl_helper->Send($styles_css, false);
+ $styles_css = $curl_helper->getInfo(CURLINFO_REDIRECT_URL);
+ $curl_helper->CloseConnection();
}
else {
$stylesheet_folder_url = $this->Application->BaseURL() . trim(EDITOR_PATH, '/') . '/';
$styles_css = $stylesheet_folder_url . 'style.css';
}
$styles_js = $stylesheet_folder_url . 'styles.js';
return array($styles_css, $styles_js);
}
/**
* Returns url to JavaScript configuration file.
*
* @return string
*/
protected function getJavaScriptConfig()
{
if ( file_exists(SYSTEM_PRESET_PATH . DIRECTORY_SEPARATOR . 'inp_ckconfig.js') ) {
$file_helper = $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
return $file_helper->pathToUrl(SYSTEM_PRESET_PATH . DIRECTORY_SEPARATOR . 'inp_ckconfig.js');
}
return $this->Application->BaseURL() . 'core/admin_templates/js/inp_ckconfig.js';
}
/**
* Returns CKEditor locale, that matches default site language.
*
* @return string
*/
protected function getLanguage()
{
static $language_code = null;
if ( !isset($language_code) ) {
$language_code = 'en'; // default value
if ( $this->Application->isAdmin ) {
$language_id = $this->Application->Phrases->LanguageId;
}
else {
$language_id = $this->Application->GetDefaultLanguageId(); // $this->Application->GetVar('m_lang');
}
$sql = 'SELECT Locale
FROM ' . $this->Application->getUnitConfig('lang')->getTableName() . '
WHERE LanguageId = ' . $language_id;
$locale = strtolower($this->Conn->GetOne($sql));
if ( file_exists(FULL_PATH . EDITOR_PATH . 'editor/lang/' . $locale . '.js') ) {
// found language file, that exactly matches locale name (e.g. "en")
$language_code = $locale;
}
else {
$locale = explode('-', $locale);
if ( file_exists(FULL_PATH . EDITOR_PATH . 'editor/lang/' . $locale[0] . '.js') ) {
// language file matches first part of locale (e.g. "ru-RU")
$language_code = $locale[0];
}
}
}
return $language_code;
}
/**
* Returns transit parameters, that should be passed to every used CKEditor instance.
*
* @param array $tag_params Tag params.
*
* @return array
*/
public function getTransitParams(array $tag_params = array())
{
$ret = array();
$transit_params = array('bgcolor' => '', 'body_class' => '', 'body_id' => '', 'style_set_name' => 'portal');
foreach ( $transit_params as $param_name => $default_value ) {
$param_value = isset($tag_params[$param_name]) ? $tag_params[$param_name] : $this->Application->GetVar($param_name);
if ( $param_value || $default_value ) {
$ret[$param_name] = $param_value ? $param_value : $default_value;
}
}
return $ret;
}
}
Index: branches/5.3.x/core/units/helpers/col_picker_helper.php
===================================================================
--- branches/5.3.x/core/units/helpers/col_picker_helper.php (revision 16502)
+++ branches/5.3.x/core/units/helpers/col_picker_helper.php (revision 16503)
@@ -1,580 +1,580 @@
Application->processPrefix($prefix);
$this->Init($splitted['prefix'], $splitted['special']);
$this->useFreezer = $this->Application->ConfigValue('UseColumnFreezer');
$this->gridName = $grid_name;
$this->pickerData = $this->loadColumns();
}
/**
* Loads picker data.
*
* @return ColumnSet
*/
protected function loadColumns()
{
$default_value = $this->Application->isAdmin ? ALLOW_DEFAULT_SETTINGS : false;
$value = $this->Application->RecallPersistentVar($this->getVarName('get'), $default_value);
if ( !$value ) {
$columns = $this->rebuildColumns();
}
else {
$default_columns = $this->getDefaultColumns();
$columns = new ColumnSet(unserialize($value));
if ( !$columns->same($default_columns) ) {
$columns = $this->rebuildColumns($columns);
}
}
return $columns;
}
/**
* Merges default column set with given one.
*
* @param ColumnSet $current_columns Currently used column set.
*
* @return ColumnSet
*/
protected function rebuildColumns(ColumnSet $current_columns = null)
{
if ( isset($current_columns) ) {
$columns = $current_columns->merge($this->getDefaultColumns(), self::DEFAULT_COLUMN_WIDTH);
}
else {
$columns = $this->getDefaultColumns();
}
$this->storeCols($columns);
return $columns;
}
/**
* Returns column set built purely from grid definition in unit config.
*
* @return ColumnSet
*/
protected function getDefaultColumns()
{
$grid_columns = $this->getColumnsFromUnitConfig();
// we NEED to recall dummy here to apply field changes imposed by formatters,
// such as replacing multilingual field titles etc.
$this->Application->recallObject($this->getPrefixSpecial(), null, array('skip_autoload' => 1));
$counter = 0;
$fields = $titles = $widths = $hidden = array();
foreach ( $grid_columns as $name => $options ) {
if ( array_key_exists('formatter_renamed', $options) && $options['formatter_renamed'] ) {
// remove language prefix from field, because formatter renamed column
$this->formatterRenamed[] = $name;
$name = preg_replace('/^l[\d]+_/', '', $name);
}
$fields[$counter] = $name;
$titles[$name] = isset($options['title']) ? $options['title'] : 'column:la_fld_' . $name;
$widths[$name] = isset($options['width']) ? $options['width'] : self::DEFAULT_COLUMN_WIDTH;
if ( isset($options['hidden']) && $options['hidden'] ) {
$hidden[$counter] = $name;
}
$counter++;
}
$cols = array(
'order' => $fields,
'titles' => $titles,
'hidden_fields' => $hidden,
'widths' => $widths,
);
return new ColumnSet($cols);
}
/**
* Returns columns as-is from unit config.
*
* @return array
*/
protected function getColumnsFromUnitConfig()
{
$grid = $this->getUnitConfig()->getGridByName($this->gridName);
if ( $this->useFreezer ) {
$freezer_column = array('__FREEZER__' => array('title' => '__FREEZER__'));
return array_merge_recursive($freezer_column, $grid['Fields']);
}
return $grid['Fields'];
}
/**
* Gets variable name in persistent session to store column positions in
*
* @param string $mode
* @return string
*/
protected function getVarName($mode = 'get')
{
$view_name = $this->Application->RecallVar($this->Prefix . '_current_view');
$ret = $this->Prefix . '[' . $this->gridName . ']columns_.' . $view_name;
if ( $mode == 'get' ) {
// fallback to old storage system, that remember only 1 grid configuration per-unit
if ( $this->Application->RecallPersistentVar($ret) === false ) {
$ret = $this->Prefix . '_columns_.' . $view_name;
}
}
return $ret;
}
/**
* Returns picker data.
*
* @return ColumnSet
*/
public function getData()
{
return $this->pickerData;
}
protected function storeCols(ColumnSet $cols)
{
$this->Application->StorePersistentVar($this->getVarName('set'), serialize($cols->toArray()));
}
/**
* Reorders given grid configuration based on picker data and removes hidden columns.
*
* @param array $grid_columns
*
* @return array
*/
public function apply(array $grid_columns)
{
uksort($grid_columns, array($this, 'compareColumns'));
return $this->removeHidden($grid_columns);
}
/**
* Removes columns, that are hidden in picker configuration.
*
* @param array $grid_columns Grid columns.
*
* @return array
*/
protected function removeHidden(array $grid_columns)
{
$to_remove = array();
foreach ( $grid_columns as $name => $options ) {
if ( array_key_exists('formatter_renamed', $options) && $options['formatter_renamed'] ) {
// remove language prefix from field, because formatter renamed column
$name_renamed = preg_replace('/^l[\d]+_/', '', $name);
}
else {
$name_renamed = $name;
}
if ( $this->pickerData->isHidden($name_renamed) ) {
$to_remove[] = $name;
}
}
foreach ( $to_remove as $name ) {
unset($grid_columns[$name]);
}
return $grid_columns;
}
/**
* Helper function for reordering grid columns.
*
* @param string $first_column
* @param string $second_column
*
* @return integer
*/
protected function compareColumns($first_column, $second_column)
{
$first_column_order = $this->pickerData->getOrder($this->fixColumnName($first_column));
$second_column_order = $this->pickerData->getOrder($this->fixColumnName($second_column));
if ( $first_column_order == $second_column_order ) {
return 0;
}
return ($first_column_order < $second_column_order) ? -1 : 1;
}
/**
* Saves changes to column widths.
*
* @param string $picked Visible columns.
* @param string $hidden Hidden columns.
*
* @return void
*/
public function saveColumns($picked, $hidden)
{
$order = $picked ? explode('|', $picked) : array();
$hidden = $hidden ? explode('|', $hidden) : array();
$order = array_merge($order, $hidden);
$this->pickerData->allFields = $order;
$this->pickerData->hiddenFields = $hidden;
$this->storeCols($this->pickerData);
}
/**
* Saves changes to column widths.
*
* @param array|string $widths Column width info.
*
* @return void
*/
public function saveWidths($widths)
{
if ( !is_array($widths) ) {
$widths = explode(':', $widths);
}
$i = 0;
array_shift($widths); // removing first col (checkbox col) width
foreach ( $this->pickerData->allFields as $field ) {
if ( $field == '__FREEZER__' ) {
continue;
}
$this->pickerData->widths[$field] = isset($widths[$i]) ? $widths[$i] : self::DEFAULT_COLUMN_WIDTH;
$i++;
}
$this->storeCols($this->pickerData);
}
/**
* Returns width of a given column.
*
* @param string $column_name Column name.
*
* @return string
*/
public function getWidth($column_name)
{
return $this->pickerData->getWidth($this->fixColumnName($column_name));
}
/**
* Removes language prefix from formatter renamed column.
*
* @param string $name Column name.
*
* @return string
*/
protected function fixColumnName($name)
{
if ( in_array($name, $this->formatterRenamed) ) {
- // remove language prefix from field, because formatter renamed column
- $column_name = preg_replace('/^l[\d]+_/', '', $name);
+ // Remove language prefix from field, because formatter renamed column.
+ $name = preg_replace('/^l[\d]+_/', '', $name);
}
return $name;
}
}
class ColumnSet extends kBase
{
/**
* List of all fields (key - order, value - field name).
*
* @var array
*/
public $allFields;
/**
* List of hidden fields (key - order, value - field name).
*
* @var array
*/
public $hiddenFields;
/**
* List of field titles (key - field name, value - label).
*
* @var array
*/
public $titles;
/**
* List of field widths (key - field name, value - width).
*
* @var array
*/
public $widths;
/**
* Creates column set.
*
* @param array $data Data.
*/
public function __construct(array $data)
{
$this->allFields = $data['order'];
$this->hiddenFields = $data['hidden_fields'];
$this->titles = $data['titles'];
$this->widths = $data['widths'];
}
/**
* Returns array representation of an object.
*
* @return array
*/
public function toArray()
{
$ret = array(
'order' => $this->allFields,
'hidden_fields' => $this->hiddenFields,
'titles' => $this->titles,
'widths' => $this->widths,
);
return $ret;
}
/**
* Returns title for a column.
*
* @param string $column_name Column name.
*
* @return string
*/
public function getTitle($column_name)
{
return $this->titles[$column_name];
}
/**
* Returns width of a column.
*
* @param string $column_name Column name.
* @param mixed $default Default value.
*
* @return string
*/
public function getWidth($column_name, $default = false)
{
return isset($this->widths[$column_name]) ? $this->widths[$column_name] : $default;
}
/**
* Returns order for a column.
*
* @param string $column_name Column name.
*
* @return integer|boolean
*/
public function getOrder($column_name)
{
return array_search($column_name, $this->allFields);
}
/**
* Determines if a column is hidden.
*
* @param string $column_name Column name.
*
* @return boolean
*/
public function isHidden($column_name)
{
return array_search($column_name, $this->hiddenFields) !== false;
}
/**
* Returns checksum for current column set.
*
* @return integer
*/
public function getChecksum()
{
$sorted_fields = $this->allFields;
$sorted_titles = $this->titles;
asort($sorted_fields);
asort($sorted_titles);
return crc32(implode(',', $sorted_fields) . implode(',', $sorted_titles));
}
/**
* Compares 2 column sets.
*
* @param ColumnSet $columns Column set.
*
* @return boolean
*/
public function same(ColumnSet $columns)
{
return $this->getChecksum() == $columns->getChecksum();
}
/**
* Merges current column set with given one.
*
* @param ColumnSet $default_columns Column set to merge with.
* @param integer $default_width Default column width.
*
* @return self
*/
public function merge(ColumnSet $default_columns, $default_width)
{
// keep user column order (common columns between user's and default grid)
$common = array_intersect($this->allFields, $default_columns->allFields);
// get new columns (found in default grid, but not found in user's grid)
$added = array_diff($default_columns->allFields, $this->allFields);
// in case if freezer was added, then make it first column
if ( in_array('__FREEZER__', $added) ) {
array_unshift($common, '__FREEZER__');
unset($added[array_search('__FREEZER__', $added)]);
}
// keep added column position
$this->allFields = $common;
foreach ( $added as $added_column ) {
$this->insertAfter($added_column, $default_columns->getPrecedingColumn($added_column));
}
$this->titles = $default_columns->titles;
$this->hiddenFields = array_intersect($this->allFields, $this->hiddenFields);
// update width & hidden status for added columns
foreach ( $added as $added_column ) {
$this->widths[$added_column] = $default_columns->getWidth($added_column, $default_width);
if ( $default_columns->isHidden($added_column) ) {
$this->hiddenFields[$default_columns->getOrder($added_column)] = $added_column;
}
}
return $this;
}
/**
* Inserts one column after another.
*
* @param string $new_column Name of column to insert.
* @param string $after_column Name of column to insert after.
*
* @return self
*/
public function insertAfter($new_column, $after_column)
{
$addition = array($after_column, $new_column);
array_splice($this->allFields, $this->getOrder($after_column), 1, $addition);
return $this;
}
/**
* Returns preceding column.
*
* @param string $name Column name.
*
* @return string
*/
public function getPrecedingColumn($name)
{
$prev_column = reset($this->allFields);
foreach ( $this->allFields as $column ) {
if ( $column == $name ) {
return $prev_column;
}
$prev_column = $column;
}
return '';
}
}
\ No newline at end of file
Index: branches/5.3.x/core/units/helpers/deployment_helper.php
===================================================================
--- branches/5.3.x/core/units/helpers/deployment_helper.php (revision 16502)
+++ branches/5.3.x/core/units/helpers/deployment_helper.php (revision 16503)
@@ -1,761 +1,764 @@
_event = new kEvent('adm:OnDummy');
$this->isCommandLine = isset($GLOBALS['argv']) && count($GLOBALS['argv']);
if ( !$this->isCommandLine ) {
$this->ip = $this->Application->getClientIp();
}
else {
if ( isset($GLOBALS['argv'][3]) ) {
$this->ip = $GLOBALS['argv'][3];
}
if ( isset($GLOBALS['argv'][4]) ) {
$new_stages = explode(',', $GLOBALS['argv'][4]);
$unknown_stages = array_diff($new_stages, $this->stages);
if ( $unknown_stages ) {
throw new InvalidArgumentException('Unknown deployment stages: ' . implode(', ', $unknown_stages));
}
$this->stages = $new_stages;
}
}
}
/**
* Sets event, associated with deployment.
*
* @param kEvent $event Event.
*
* @return void
*/
public function setEvent(kEvent $event)
{
$this->_event = $event;
}
/**
* Adds message to script execution log.
*
* @param string $message Message.
* @param boolean $new_line Jump to next line.
*
* @return string
*/
private function toLog($message, $new_line = true)
{
if ( $new_line ) {
$message .= PHP_EOL;
}
$this->logData['Output'] .= $message;
return $message;
}
/**
* Loads already applied revisions list of current module.
*
* @return self
*/
private function loadAppliedRevisions()
{
$sql = 'SELECT RevisionNumber
FROM ' . TABLE_PREFIX . 'ModuleDeploymentLog
WHERE Module = ' . $this->Conn->qstr($this->moduleName);
$this->appliedRevisions = array_flip($this->Conn->GetCol($sql));
return $this;
}
/**
* Deploys changes from all installed modules.
*
* @param boolean $dry_run Use dry run mode?
*
* @return boolean
*/
public function deployAll($dry_run = false)
{
if ( !$this->isCommandLine ) {
echo '' . PHP_EOL;
}
$ret = true;
$this->dryRun = $dry_run;
if ( in_array(self::STAGE_DB_MIGRATE, $this->stages) ) {
foreach ( $this->Application->ModuleInfo as $module_name => $module_info ) {
$this->moduleName = $module_name;
if ( !file_exists($this->getModuleFile('project_upgrades.sql')) ) {
continue;
}
$ret = $ret && $this->deploy($module_name);
}
}
if ( in_array(self::STAGE_CACHE_RESET, $this->stages) ) {
if ( $ret && !$this->dryRun ) {
$this->resetCaches();
$this->refreshThemes();
}
}
if ( !$this->isCommandLine ) {
echo kUtil::escape($this->_runShellScript());
echo '
' . PHP_EOL;
}
return $ret;
}
/**
* Runs user-specific shell script when deployment happens from Web.
*
* @return string
*/
protected function _runShellScript()
{
if ( !$this->Application->isDebugMode(false) ) {
return '';
}
$wrapper_script = '/usr/local/bin/guest2host_server.sh';
$script_name = FULL_PATH . '/tools/' . ($this->dryRun ? 'synchronize.sh' : 'deploy.sh');
if ( file_exists($wrapper_script) && file_exists($script_name) ) {
$script_name = preg_replace('/^.*\/web/', constant('DBG_LOCAL_BASE_PATH'), $script_name);
return shell_exec($wrapper_script . ' ' . $script_name . ' 2>&1');
}
return '';
}
/**
* Deploys pending changes to a site.
*
* @param string $module_name Module name.
*
* @return boolean
*/
private function deploy($module_name)
{
echo $this->colorText('Deploying Module "' . $module_name . '":', 'cyan', true) . PHP_EOL;
if ( !$this->upgradeDatabase() ) {
return false;
}
try {
if ( $this->dryRun ) {
$this->exportLanguagePack();
}
else {
$this->importLanguagePack();
}
}
catch ( Exception $e ) {
echo $this->colorText('Failed with Module "' . $module_name . '".', 'red', true) . PHP_EOL . PHP_EOL;
return false;
}
echo $this->colorText('Done with Module "' . $module_name . '".', 'green', true) . PHP_EOL . PHP_EOL;
return true;
}
/**
* Import latest language pack (without overwrite).
*
* @return self
*/
private function importLanguagePack()
{
$language_import_helper = $this->Application->recallObject('LanguageImportHelper');
/* @var $language_import_helper LanguageImportHelper */
$this->out('Importing LanguagePack ... ');
$filename = $this->getModuleFile('english.lang');
$language_import_helper->performImport($filename, '|0|1|2|', $this->moduleName);
$this->displayStatus('OK');
return $this;
}
/**
* Exports latest language pack.
*
* @return self
*/
private function exportLanguagePack()
{
static $languages = null;
if ( !isset($languages) ) {
$sql = 'SELECT LanguageId
FROM ' . $this->Application->getUnitConfig('lang')->getTableName() . '
WHERE Enabled = 1';
$languages = $this->Conn->GetCol($sql);
}
$language_import_helper = $this->Application->recallObject('LanguageImportHelper');
/* @var $language_import_helper LanguageImportHelper */
$language_import_helper->performExport(EXPORT_PATH . '/' . $this->moduleName . '.lang', '|0|1|2|', $languages, '|' . $this->moduleName . '|');
return $this;
}
/**
* Resets unit and section cache.
*
* @return self
*/
private function resetCaches()
{
// 2. reset unit config cache (so new classes get auto-registered)
$this->out('Resetting Configs Files Cache and Parsed System Data ... ');
$this->_event->CallSubEvent('OnResetConfigsCache');
$this->displayStatus('OK');
// 3. reset sections cache
$this->out('Resetting Admin Console Sections ... ');
$this->_event->CallSubEvent('OnResetSections');
$this->displayStatus('OK');
// 4. reset mod-rewrite cache
$this->out('Resetting ModRewrite Cache ... ');
$this->_event->CallSubEvent('OnResetModRwCache');
$this->displayStatus('OK');
return $this;
}
/**
* Rebuild theme files.
*
* @return self
*/
private function refreshThemes()
{
$this->out('Refreshing Theme Files ... ');
$this->_event->CallSubEvent('OnRebuildThemes');
$this->displayStatus('OK');
return $this;
}
/**
* Runs database upgrade script.
*
* @return boolean
*/
private function upgradeDatabase()
{
$this->loadAppliedRevisions();
$this->Conn->setErrorHandler(array(&$this, 'handleSqlError'));
$this->out('Verifying Database Revisions ... ');
if ( !$this->collectDatabaseRevisions() || !$this->checkRevisionDependencies() ) {
return false;
}
$this->displayStatus('OK');
return $this->applyRevisions();
}
/**
* Collects database revisions from "project_upgrades.sql" file.
*
* @return boolean
*/
private function collectDatabaseRevisions()
{
$filename = $this->getModuleFile('project_upgrades.sql');
if ( !file_exists($filename) ) {
return true;
}
$sqls = file_get_contents($filename);
preg_match_all("/# r([\d]+)([^\:]*):(.*?)(\n|$)/s", $sqls, $matches, PREG_SET_ORDER + PREG_OFFSET_CAPTURE);
if ( !$matches ) {
$this->displayStatus('FAILED' . PHP_EOL . 'No Database Revisions Found');
return false;
}
+ $revision_numbers = array();
+
foreach ( $matches as $index => $match ) {
$revision = $match[1][0];
- if ( $this->revisionApplied($revision) ) {
- // skip applied revisions
- continue;
- }
-
- if ( isset($this->revisionSqls[$revision]) ) {
- // duplicate revision among non-applied ones
+ if ( in_array($revision, $revision_numbers) ) {
$this->displayStatus('FAILED' . PHP_EOL . 'Duplicate revision #' . $revision . ' found');
return false;
}
- // get revision sqls
+ $revision_numbers[] = $revision;
+
+ if ( $this->revisionApplied($revision) ) {
+ // Skip applied revisions.
+ continue;
+ }
+
+ // Get revision sqls.
$start_pos = $match[0][1] + strlen($match[0][0]);
$end_pos = isset($matches[$index + 1]) ? $matches[$index + 1][0][1] : strlen($sqls);
$revision_sqls = substr($sqls, $start_pos, $end_pos - $start_pos);
if ( !$revision_sqls ) {
// revision without sqls
continue;
}
$this->revisionTitles[$revision] = trim($match[3][0]);
$this->revisionSqls[$revision] = $revision_sqls;
$revision_dependencies = $this->parseRevisionDependencies($match[2][0]);
if ( $revision_dependencies ) {
$this->revisionDependencies[$revision] = $revision_dependencies;
}
}
ksort($this->revisionSqls);
ksort($this->revisionDependencies);
return true;
}
/**
* Checks that all dependent revisions are either present now OR were applied before.
*
* @return boolean
*/
private function checkRevisionDependencies()
{
foreach ( $this->revisionDependencies as $revision => $revision_dependencies ) {
foreach ( $revision_dependencies as $revision_dependency ) {
if ( $this->revisionApplied($revision_dependency) ) {
// revision dependent upon already applied -> dependency fulfilled
continue;
}
if ( $revision_dependency >= $revision ) {
$this->displayStatus('FAILED' . PHP_EOL . 'Revision #' . $revision . ' has incorrect dependency to revision #' . $revision_dependency . '. Only dependencies to older revisions are allowed!');
return false;
}
if ( !isset($this->revisionSqls[$revision_dependency]) ) {
$this->displayStatus('FAILED' . PHP_EOL . 'Revision #' . $revision . ' depends on missing revision #' . $revision_dependency . '!');
return false;
}
}
}
return true;
}
/**
* Runs all pending sqls.
*
* @return boolean
*/
private function applyRevisions()
{
if ( !$this->revisionSqls ) {
return true;
}
if ( $this->dryRun ) {
foreach ( $this->revisionSqls as $revision => $sqls ) {
$this->initLog($revision, ModuleDeploymentLog::MODE_MANUAL);
echo PHP_EOL . $this->colorText($this->revisionTitles[$revision], 'gray', true) . PHP_EOL; // 'Processing DB Revision: #' . $revision . ' ... ';
echo $this->toLog($this->colorText('SKIPPING', 'purple'));
$this->saveLog(ModuleDeploymentLog::STATUS_SKIPPED);
}
return true;
}
$this->out('Upgrading Database ... ', true);
foreach ( $this->revisionSqls as $revision => $sqls ) {
echo PHP_EOL . $this->colorText($this->revisionTitles[$revision], 'gray', true) . PHP_EOL; // 'Processing DB Revision: #' . $revision . ' ... ';
$sqls = str_replace("\r\n", "\n", $sqls); // convert to linux line endings
$no_comment_sqls = preg_replace("/#\s([^;]*?)\n/is", "# \\1;\n", $sqls); // add ";" to each comment end to ensure correct split
$sqls = explode(";\n", $no_comment_sqls . "\n"); // ensures that last sql won't have ";" in it
$sqls = array_map('trim', $sqls);
$this->initLog($revision);
try {
foreach ( $sqls as $sql ) {
if ( substr($sql, 0, 1) == '#' ) {
// output comment as is
echo $this->toLog($this->colorText($sql, 'purple'));
continue;
}
elseif ( $sql ) {
echo $this->toLog($this->shortenQuery($sql), false);
$this->Conn->Query($sql);
$this->displayStatus('OK (' . $this->Conn->getAffectedRows() . ')', true, true);
}
}
}
catch ( Exception $e ) {
// consider revisions with errors applied
$this->saveLog(ModuleDeploymentLog::STATUS_ERROR);
return false;
}
$this->saveLog(ModuleDeploymentLog::STATUS_SUCCESS);
}
echo PHP_EOL;
return true;
}
/**
* Returns shortened version of SQL query.
*
* @param string $sql SQL query.
*
* @return string
*/
protected function shortenQuery($sql)
{
$escaped_sql = $this->isCommandLine ? $sql : kUtil::escape($sql);
$single_line_sql = preg_replace('/(\n|\t| )+/is', ' ', $escaped_sql);
return mb_substr(trim($single_line_sql), 0, self::SQL_TRIM_LENGTH) . ' ... ';
}
/**
* Initializes log record for a revision.
*
* @param integer $revision Revision.
* @param integer $mode Mode.
*
* @return self
*/
protected function initLog($revision, $mode = ModuleDeploymentLog::MODE_AUTOMATIC)
{
$this->logData = array(
'Module' => $this->moduleName,
'RevisionNumber' => $revision,
'RevisionTitle' => $this->revisionTitles[$revision],
'IPAddress' => $this->ip,
'Output' => '',
'Mode' => $mode,
'Status' => ModuleDeploymentLog::STATUS_SUCCESS,
);
return $this;
}
/**
* Creates log record.
*
* @param integer $status Status.
*
* @return self
*/
private function saveLog($status)
{
$this->logData['Status'] = $status;
$log = $this->Application->recallObject('module-deployment-log', null, array('skip_autoload' => true));
/* @var $log kDBItem */
$log->Clear();
$log->SetFieldsFromHash($this->logData);
$log->Create();
return $this;
}
/**
* Error handler for sql errors.
*
* @param int $code Error code.
* @param string $msg Error message.
* @param string $sql SQL query, that raised an error.
*
* @return void
* @throws Exception When SQL error happens.
*/
public function handleSqlError($code, $msg, $sql)
{
$this->displayStatus('FAILED', true, true);
$error_msg = 'SQL Error #' . $code . ': ' . $msg;
$this->logData['ErrorMessage'] = $error_msg;
$this->displayStatus($error_msg);
$this->out('Please execute rest of SQLs in this Revision by hand and run deployment script again.', true);
throw new Exception($msg, $code);
}
/**
* Checks if given revision was already applied.
*
* @param int $revision Revision.
*
* @return boolean
*/
private function revisionApplied($revision)
{
return isset($this->appliedRevisions[$revision]);
}
/**
* Returns path to given file in current module install folder.
*
* @param string $filename Filename.
*
* @return string
*/
private function getModuleFile($filename)
{
$module_folder = $this->Application->findModule('Name', $this->moduleName, 'Path');
return FULL_PATH . DIRECTORY_SEPARATOR . $module_folder . 'install/' . $filename;
}
/**
* Extracts revisions from string in format "(1,3,5464,23342,3243)".
*
* @param string $string Comma-separated revision list.
*
* @return array
*/
private function parseRevisionDependencies($string)
{
if ( !$string ) {
return array();
}
$string = explode(',', substr($string, 1, -1));
return array_map('trim', $string);
}
/**
* Applies requested color and bold attributes to given text string.
*
* @param string $text Text.
* @param string $color Color.
* @param boolean $bold Bold flag.
*
* @return string
*/
private function colorText($text, $color, $bold = false)
{
if ( $this->isCommandLine ) {
$color_map = array(
'black' => 30, // dark gray (in bold)
'blue' => 34, // light blue (in bold)
'green' => 32, // light green (in bold)
'cyan' => 36, // light cyan (in bold)
'red' => 31, // light red (in bold)
'purple' => 35, // light purple (in bold)
'brown' => 33, // yellow (in bold)
'gray' => 37, // white (in bold)
);
return "\033[" . ($bold ? 1 : 0) . ";" . $color_map[$color] . "m" . $text . "\033[0m";
}
$html_color_map = array(
'black' => array('normal' => '#000000', 'bold' => '#666666'),
'blue' => array('normal' => '#00009C', 'bold' => '#3C3CFF'),
'green' => array('normal' => '#009000', 'bold' => '#00FF00'),
'cyan' => array('normal' => '#009C9C', 'bold' => '#00FFFF'),
'red' => array('normal' => '#9C0000', 'bold' => '#FF0000'),
'purple' => array('normal' => '#900090', 'bold' => '#F99CF9'),
'brown' => array('normal' => '#C9C909', 'bold' => '#FFFF00'),
'gray' => array('normal' => '#909090', 'bold' => '#FFFFFF'),
);
$html_color = $html_color_map[$color][$bold ? 'bold' : 'normal'];
return '' . kUtil::escape($text, kUtil::ESCAPE_HTML) . '';
}
/**
* Displays last command execution status.
*
* @param string $status_text Status text.
* @param boolean $new_line Jump to next line.
* @param boolean $to_log Also write to log.
*
* @return self
*/
private function displayStatus($status_text, $new_line = true, $to_log = false)
{
$color = substr($status_text, 0, 2) == 'OK' ? 'green' : 'red';
$ret = $this->colorText($status_text, $color, false);
if ( $to_log ) {
echo $this->toLog($ret, $new_line);
}
else {
echo $ret . ($new_line ? PHP_EOL : '');
}
return $this;
}
/**
* Outputs a text and escapes it if necessary.
*
* @param string $text Text.
* @param boolean $new_line Jump to next line.
*
* @return self
*/
private function out($text, $new_line = false)
{
if ( !$this->isCommandLine ) {
$text = kUtil::escape($text);
}
echo $text . ($new_line ? PHP_EOL : '');
return $this;
}
}
Index: branches/5.3.x/core/units/fck/fck_eh.php
===================================================================
--- branches/5.3.x/core/units/fck/fck_eh.php (revision 16502)
+++ branches/5.3.x/core/units/fck/fck_eh.php (revision 16503)
@@ -1,257 +1,259 @@
Array ('self' => true),
);
$this->permMapping = array_merge($this->permMapping, $permissions);
}
/**
* Checks user permission to execute given $event
*
* @param kEvent $event
* @return bool
* @access public
*/
public function CheckPermission(kEvent $event)
{
if ( $this->Application->isAdminUser || $event->Name == 'OnGetsEditorStyles' ) {
// this limits all event execution only to logged-in users in admin
return true;
}
return parent::CheckPermission($event);
}
function CreateXmlHeader()
{
ob_end_clean() ;
// Prevent the browser from caching the result.
// Date in the past
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT') ;
// always modified
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT') ;
// HTTP/1.1
header('Cache-Control: no-store, no-cache, must-revalidate') ;
header('Cache-Control: post-check=0, pre-check=0', false) ;
// HTTP/1.0
header('Pragma: no-cache') ;
// Set the response format.
$this->Application->setContentType('text/xml');
// Create the XML document header.
}
function OnLoadCmsTree($event)
{
$event->status = kEvent::erSTOP;
$category_helper = $this->Application->recallObject('CategoryHelper');
/* @var $category_helper CategoryHelper */
$pages = $category_helper->getStructureTreeAsOptions();
$sql = 'SELECT NamedParentPath, CategoryId
FROM ' . TABLE_PREFIX . 'Categories
WHERE CategoryId IN (' . implode(',', array_keys($pages)) . ')';
$templates = $this->Conn->GetCol($sql, 'CategoryId');
$templates[$this->Application->getBaseCategory()] .= '/Index'; // "Content" category will act as "Home Page"
$res = '' . "\n";
$res .= '' . "\n";
foreach ($pages as $id => $title) {
$template = $templates[$id];
$page_path = preg_replace('/^Content\//i', '', strtolower($template).'.html');
$title = $title . ' (' . $page_path . ')';
$real_url = $this->Application->HREF($template, '_FRONT_END_', array('pass' => 'm'), 'index.php');
$res .= '' . "\n";
}
$res.= "";
$this->CreateXmlHeader();
echo $res;
}
function OnRenameFile($event)
{
$event->status = kEvent::erSTOP;
if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
return;
}
$old_name = $this->Application->GetVar('old_name');
$new_name = $this->Application->GetVar('new_name');
$folder = $this->Application->GetVar('folder');
$sServerDir = WRITEABLE . '/user_files/' . $folder . '/';
if (!file_exists($sServerDir.$old_name) || !is_file($sServerDir.$old_name)) {
echo 204;
return;
}
$fck_helper = $this->Application->recallObject('FCKHelper');
/* @var $fck_helper fckFCKHelper*/
if ( !$fck_helper->IsAllowedExtension($folder, $new_name) ) {
echo 203;
return;
}
if ( !rename($sServerDir . $old_name, $sServerDir . $new_name) ) {
// echo $sServerDir.$old_name.' -> '.$sServerDir.$new_name;
echo 205;
return;
}
echo '0';
}
function OnDeleteFiles($event)
{
$event->status = kEvent::erSTOP;
if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
return;
}
$files = trim($this->Application->GetVar('files'),'|');
// echo $files;
$a_files = explode('|', $files);
$folder = $this->Application->GetVar('folder');
$sServerDir = WRITEABLE . '/user_files/' . $folder . '/';
foreach ($a_files AS $file) {
@unlink($sServerDir.$file);
}
// print_r($a_files);
}
function OnGetFoldersFilesList($event)
{
$this->CreateXmlHeader();
$fck_helper = $this->Application->recallObject('FCKHelper');
/* @var $fck_helper fckFCKHelper */
$ret = ''."\n" ;
$ret .= ""."\n";
$ret .= $fck_helper->PrintFolders();
$ret .= $fck_helper->PrintFiles();
$ret .= ""."\n";
echo $ret;
exit;
}
function OnCreateFolder($event)
{
$event->status = kEvent::erSTOP;
if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
return;
}
$new_folder = $this->Application->GetVar('new_folder');
$current_folder = $this->Application->GetVar('current_folder');
$folderPath = WRITEABLE . '/user_files' . '/' . $current_folder . "/" . $new_folder;
if ( file_exists( $folderPath ) && is_dir($folderPath)) {
echo "101";
}
if ( !file_exists( $folderPath ) )
{
// Turn off all error reporting.
error_reporting( 0 ) ;
// Enable error tracking to catch the error.
ini_set( 'track_errors', '1' ) ;
// To create the folder with 0777 permissions, we need to set umask to zero.
$oldumask = umask(0) ;
mkdir( $folderPath, 0777 ) ;
umask( $oldumask ) ;
$sErrorMsg = $php_errormsg ;
// Restore the configurations.
ini_restore( 'track_errors' ) ;
ini_restore( 'error_reporting' ) ;
if ($sErrorMsg)
echo $sErrorMsg ;
else
echo '0';
}
}
/**
* Uploads a file from FCK file browser
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnUploadFile(kEvent $event)
{
$event->status = kEvent::erSTOP;
if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
return;
}
$fck_helper = $this->Application->recallObject('FCKHelper');
/* @var $fck_helper fckFCKHelper*/
$fck_helper->UploadFile();
}
/**
* Returns compressed CSS file
*
* @param kEvent $event
*/
function OnGetsEditorStyles($event)
{
+ kUtil::safeDefine('DBG_SKIP_REPORTING', 1);
+
/** @var ThemeItem $theme */
$theme = $this->Application->recallObject('theme.current');
/** @var MinifyHelper $minify_helper */
$minify_helper = $this->Application->recallObject('MinifyHelper');
$this->Application->InitParser();
$styles_css = $minify_helper->CompressScriptTag(array('files' => $theme->getStylesheetFile(true)));
$event->redirect = 'external:' . $styles_css;
}
}
Index: branches/5.3.x/core/ckeditor/plugins/my_link/plugin.js
===================================================================
--- branches/5.3.x/core/ckeditor/plugins/my_link/plugin.js (revision 16502)
+++ branches/5.3.x/core/ckeditor/plugins/my_link/plugin.js (revision 16503)
@@ -1,706 +1,682 @@
(
function() {
CKEDITOR.plugins.add('my_link');
CKEDITOR.on(
'dialogDefinition',
function(ev) {
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
var editor = this;
if (dialogName == 'link') {
var plugin = CKEDITOR.plugins.link;
var $internal_page_mapping = {}; // mapping between @@PAGE_ID@@ and real url (for anchor loading)
var load_page_anchors = function ($page_id, dialog) {
var $url;
var $tmp_iframe = $('#tmp_iframe');
var $internal_page_anchors = dialog.getContentElement( 'info', 'internalPageAnchors' );
var $current_anchor = $('#' + $internal_page_anchors.domId).data('anchor');
$internal_page_anchors.clear();
$internal_page_anchors.add( 'Loading ...', '' );
if ( $tmp_iframe.length == 0 ) {
$tmp_iframe = $('').appendTo('body');
$tmp_iframe.get(0).onload = function () {
var $anchors = $('a[name]', $tmp_iframe.get(0).contentWindow.document);
$internal_page_anchors.allowOnChange = false;
$internal_page_anchors.clear();
$internal_page_anchors.add( $anchors.length > 0 ? '' : '(No anchors available in the document)', '' );
$anchors.each(
function () {
$internal_page_anchors.add( $(this).attr('name') );
}
);
$internal_page_anchors.setValue($current_anchor);
$internal_page_anchors.allowOnChange = true;
}
}
$url = $internal_page_mapping[$page_id];
$url += ($url.indexOf('?') ? '&' : '?') + 'skip_last_template=1';
$tmp_iframe.attr('src', $url);
}
// Handles the event when the "Type" selection box is changed.
var linkTypeChanged = function()
{
var dialog = this.getDialog(),
partIds = [ 'internalOptions', 'urlOptions', 'anchorOptions', 'emailOptions' ],
typeValue = this.getValue(),
uploadTab = dialog.definition.getContents( 'upload' ),
uploadInitiallyHidden = uploadTab && uploadTab.hidden;
switch ( typeValue ) {
case 'url':
if ( editor.config.linkShowTargetTab )
dialog.showPage( 'target' );
if ( !uploadInitiallyHidden )
dialog.showPage( 'upload' );
break;
case 'internal':
if ( editor.config.linkShowTargetTab )
dialog.showPage( 'target' );
if ( !uploadInitiallyHidden )
dialog.hidePage( 'upload' );
// select internal page according to link url (on link type change)
var $url = dialog.getContentElement('info', 'url').getValue();
if ( $internal_page_mapping[$url] !== undefined ) {
dialog.getContentElement('info', 'internal_page').setValue($url);
}
break;
default:
dialog.hidePage( 'target' );
if ( !uploadInitiallyHidden )
dialog.hidePage( 'upload' );
break;
}
for ( var i = 0 ; i < partIds.length ; i++ )
{
var element = dialog.getContentElement( 'info', partIds[i] );
if ( !element )
continue;
element = element.getElement().getParent().getParent();
if ( partIds[i] == typeValue + 'Options' )
element.show();
else
element.hide();
}
dialog.layout();
};
var javascriptProtocolRegex = /^javascript:/,
emailRegex = /^mailto:([^?]+)(?:\?(.+))?$/,
emailSubjectRegex = /subject=([^;?:@&=$,\/]*)/,
emailBodyRegex = /body=([^;?:@&=$,\/]*)/,
anchorRegex = /^#(.*)$/,
urlRegex = /^((?:http|https|ftp|news):\/\/)?(.*)$/,
selectableTargets = /^(_(?:self|top|parent|blank))$/,
encodedEmailLinkRegex = /^javascript:void\(location\.href='mailto:'\+String\.fromCharCode\(([^)]+)\)(?:\+'(.*)')?\)$/,
functionCallProtectedEmailLinkRegex = /^javascript:([^(]+)\(([^)]+)\)$/,
internalPageRegex = /^@@[\d]+@@(#.*$|$)/; // CUSTOM
var popupRegex =
/\s*window.open\(\s*this\.href\s*,\s*(?:'([^']*)'|null)\s*,\s*'([^']*)'\s*\)\s*;\s*return\s*false;*\s*/;
var popupFeaturesRegex = /(?:^|,)([^=]+)=(\d+|yes|no)/gi;
var parseLink = function( editor, element )
{
var href = ( element && ( element.data( 'cke-saved-href' ) || element.getAttribute( 'href' ) ) ) || '',
javascriptMatch,
emailMatch,
anchorMatch,
urlMatch,
retval = {};
if ( ( javascriptMatch = href.match( javascriptProtocolRegex ) ) )
{
var emailProtection = editor.config.emailProtection || '';
if ( emailProtection == 'encode' )
{
href = href.replace( encodedEmailLinkRegex,
function ( match, protectedAddress, rest )
{
return 'mailto:' +
String.fromCharCode.apply( String, protectedAddress.split( ',' ) ) +
( rest && unescapeSingleQuote( rest ) );
});
}
// Protected email link as function call.
else if ( emailProtection )
{
href.replace( functionCallProtectedEmailLinkRegex, function( match, funcName, funcArgs )
{
if ( funcName == compiledProtectionFunction.name )
{
retval.type = 'email';
var email = retval.email = {};
var paramRegex = /[^,\s]+/g,
paramQuoteRegex = /(^')|('$)/g,
paramsMatch = funcArgs.match( paramRegex ),
paramsMatchLength = paramsMatch.length,
paramName,
paramVal;
for ( var i = 0; i < paramsMatchLength; i++ )
{
paramVal = decodeURIComponent( unescapeSingleQuote( paramsMatch[ i ].replace( paramQuoteRegex, '' ) ) );
paramName = compiledProtectionFunction.params[ i ].toLowerCase();
email[ paramName ] = paramVal;
}
email.address = [ email.name, email.domain ].join( '@' );
}
} );
}
}
if ( !retval.type )
{
if ( ( anchorMatch = href.match( anchorRegex ) ) )
{
retval.type = 'anchor';
retval.anchor = {};
retval.anchor.name = retval.anchor.id = anchorMatch[1];
}
// Protected email link as encoded string.
else if ( ( emailMatch = href.match( emailRegex ) ) )
{
var subjectMatch = href.match( emailSubjectRegex ),
bodyMatch = href.match( emailBodyRegex );
retval.type = 'email';
var email = ( retval.email = {} );
email.address = emailMatch[ 1 ];
subjectMatch && ( email.subject = decodeURIComponent( subjectMatch[ 1 ] ) );
bodyMatch && ( email.body = decodeURIComponent( bodyMatch[ 1 ] ) );
}
// CUSTOM: begin
else if ( href && href.match( internalPageRegex ) )
{
retval.type = 'internal';
retval.url = {};
retval.url.protocol = '';
retval.anchor = {};
if ( href.match(/^(.*)#(.*)$/) ) {
retval.url.url = RegExp.$1;
retval.internal_anchor = RegExp.$2;
}
else {
retval.url.url = href;
retval.internal_anchor = '';
}
}
// CUSTOM: end
// urlRegex matches empty strings, so need to check for href as well.
else if ( href && ( urlMatch = href.match( urlRegex ) ) )
{
retval.type = 'url';
retval.url = {};
retval.url.protocol = urlMatch[1];
retval.url.url = urlMatch[2];
}
// CUSTOM: begin
else {
retval.type = 'internal';
retval.url = {};
retval.url.protocol = '';
retval.url.url = '';
retval.internal_anchor = '';
}
// CUSTOM: end
}
// Load target and popup settings.
if ( element )
{
var target = element.getAttribute( 'target' );
retval.target = {};
retval.adv = {};
// IE BUG: target attribute is an empty string instead of null in IE if it's not set.
if ( !target )
{
var onclick = element.data( 'cke-pa-onclick' ) || element.getAttribute( 'onclick' ),
onclickMatch = onclick && onclick.match( popupRegex );
if ( onclickMatch )
{
retval.target.type = 'popup';
retval.target.name = onclickMatch[1];
var featureMatch;
while ( ( featureMatch = popupFeaturesRegex.exec( onclickMatch[2] ) ) )
{
// Some values should remain numbers (#7300)
if ( ( featureMatch[2] == 'yes' || featureMatch[2] == '1' ) && !( featureMatch[1] in { height:1, width:1, top:1, left:1 } ) )
retval.target[ featureMatch[1] ] = true;
else if ( isFinite( featureMatch[2] ) )
retval.target[ featureMatch[1] ] = featureMatch[2];
}
}
}
else
{
var targetMatch = target.match( selectableTargets );
if ( targetMatch )
retval.target.type = retval.target.name = target;
else
{
retval.target.type = 'frame';
retval.target.name = target;
}
}
var me = this;
var advAttr = function( inputName, attrName )
{
var value = element.getAttribute( attrName );
if ( value !== null )
retval.adv[ inputName ] = value || '';
};
advAttr( 'advId', 'id' );
advAttr( 'advLangDir', 'dir' );
advAttr( 'advAccessKey', 'accessKey' );
retval.adv.advName =
element.data( 'cke-saved-name' )
|| element.getAttribute( 'name' )
|| '';
advAttr( 'advLangCode', 'lang' );
advAttr( 'advTabIndex', 'tabindex' );
advAttr( 'advTitle', 'title' );
advAttr( 'advContentType', 'type' );
advAttr( 'advCSSClasses', 'class' );
advAttr( 'advCharset', 'charset' );
advAttr( 'advStyles', 'style' );
advAttr( 'advRel', 'rel' );
}
// Find out whether we have any anchors in the editor.
// Get all IMG elements in CK document.
var elements = editor.document.getElementsByTag( 'img' ),
realAnchors = new CKEDITOR.dom.nodeList( editor.document.$.anchors ),
anchors = retval.anchors = [];
for ( var i = 0; i < elements.count() ; i++ )
{
var item = elements.getItem( i );
if ( item.data( 'cke-realelement' ) && item.data( 'cke-real-element-type' ) == 'anchor' )
anchors.push( editor.restoreRealElement( item ) );
}
for ( i = 0 ; i < realAnchors.count() ; i++ )
anchors.push( realAnchors.getItem( i ) );
for ( i = 0 ; i < anchors.length ; i++ )
{
item = anchors[ i ];
anchors[ i ] = { name : item.getAttribute( 'name' ), id : item.getAttribute( 'id' ) };
}
// Record down the selected element in the dialog.
this._.selectedElement = element;
return retval;
};
var infoTab = dialogDefinition.getContents( 'info' );
var linkType = infoTab.get('linkType');
linkType['items'].unshift( ['Internal Web Page', 'internal'] );
linkType['default'] = 'internal';
linkType['onChange'] = linkTypeChanged;
infoTab.add(
{
type : 'vbox',
id : 'internalOptions',
children :
[
{
type : 'select',
id : 'internal_page',
label : 'Internal Page Name',
items : [
[ '' ]
],
onChange: function ( ) {
// place internal page ID from this dropdown into URL field of the dialog
var dialog = this.getDialog();
this.allowOnChange = false;
dialog.getContentElement('info', 'url').setValue( this.getValue() );
this.allowOnChange = true;
load_page_anchors( this.getValue(), dialog );
},
setup : function( data ) {
var $me = this;
var $link_url = data.url ? data.url.url : '';
var $ajax_url = CKEDITOR.basePath.replace( /core.*$/, 'admin/index.php?events[fck]=OnLoadCmsTree' );
var $internal_page_anchors = this.getDialog().getContentElement( 'info', 'internalPageAnchors' );
$('#' + $internal_page_anchors.domId).data('anchor', data.internal_anchor);
$.ajax(
{
type: 'GET',
url: $ajax_url,
async: false,
success: function ($xml) {
$me.allowOnChange = false;
$me.clear();
$me.add( '' );
$('CmsPage', $xml).each(
function ($i) {
var $node = $(this);
var cms_id = $node.attr('st_id');
var cms_page = $node.attr('path');
var real_url = $node.attr('real_url');
$internal_page_mapping[cms_page] = real_url;
$me.add( $node.attr('title').replace(/&/g, '&') , cms_page );
if ( $link_url && $link_url.match(cms_page + '$') ) {
$me.setValue(cms_page);
load_page_anchors( cms_page, $me.getDialog() );
}
}
);
// undo CKEditor default "htmlspecialchars" on all HTML
$('option', '#' + $me.domId).each(
function ($index) {
var $option = $(this),
$level_indicator = '—',
$html = $option.html().replace(/&/g, '&'),
$level = ($html.length - $html.replace(new RegExp($level_indicator, 'g'), '').length ) / $level_indicator.length;
if ( $html.match(/^(.*?) (.*?) (\(.*?\))$/) ) {
// "—— rss (news/rss.html)"
$level_indicator = RegExp.$1;
var $page_name = RegExp.$2,
$page_url = RegExp.$3;
$html = $level_indicator + ' ' + $page_name + ' ' + $page_url;
}
$option.html($html);
}
);
$me.allowOnChange = true;
$me.getDialog().layout();
},
dataType: 'xml'
}
);
var linkType = this.getDialog().getContentElement( 'info', 'linkType' );
if ( linkType && linkType.getValue() == 'internal' ) {
this.focus();
}
},
},
{
type : 'select',
id : 'internalPageAnchors',
label : 'Select an Anchor',
items : [
[ '' ]
],
commit : function( data ) {
data.internal_anchor = this.getValue();
this.allowOnChange = false;
}
}
]
}
);
dialogDefinition['onShow'] = function() {
this.fakeObj = false;
var editor = this.getParentEditor(),
selection = editor.getSelection(),
element = null;
// Fill in all the relevant fields if there's already one link selected.
if ( ( element = plugin.getSelectedLink( editor ) ) && element.hasAttribute( 'href' ) )
selection.selectElement( element );
else if ( ( element = selection.getSelectedElement() ) && element.is( 'img' )
&& element.data( 'cke-real-element-type' )
&& element.data( 'cke-real-element-type' ) == 'anchor' )
{
this.fakeObj = element;
element = editor.restoreRealElement( this.fakeObj );
selection.selectElement( this.fakeObj );
}
else
element = null;
this.setupContent( parseLink.apply( this, [ editor, element ] ) );
};
dialogDefinition['onOk'] = function() {
var attributes = {},
removeAttributes = [],
data = {},
me = this,
editor = this.getParentEditor();
this.commitContent( data );
// Compose the URL.
switch ( data.type || 'internal' )
{
// CUSTOM: begin
case 'internal':
var url = ( data.url && data.url.url ) || '';
var anchor = data.internal_anchor || '';
attributes[ 'data-cke-saved-href' ] = url;
if ( anchor ) {
attributes[ 'data-cke-saved-href' ] += '#' + anchor;
}
break;
// CUSTOM: end
case 'url':
var protocol = ( data.url && data.url.protocol != undefined ) ? data.url.protocol : 'http://',
url = ( data.url && data.url.url ) || '';
attributes[ 'data-cke-saved-href' ] = ( url.indexOf( '/' ) === 0 ) ? url : protocol + url;
break;
case 'anchor':
var name = ( data.anchor && data.anchor.name ),
id = ( data.anchor && data.anchor.id );
attributes[ 'data-cke-saved-href' ] = '#' + ( name || id || '' );
break;
case 'email':
var linkHref,
email = data.email,
address = email.address,
emailProtection = editor.config.emailProtection || '';
switch( emailProtection )
{
case '' :
case 'encode' :
{
var subject = encodeURIComponent( email.subject || '' ),
body = encodeURIComponent( email.body || '' );
// Build the e-mail parameters first.
var argList = [];
subject && argList.push( 'subject=' + subject );
body && argList.push( 'body=' + body );
argList = argList.length ? '?' + argList.join( '&' ) : '';
if ( emailProtection == 'encode' )
{
linkHref = [ 'javascript:void(location.href=\'mailto:\'+',
protectEmailAddressAsEncodedString( address ) ];
// parameters are optional.
argList && linkHref.push( '+\'', escapeSingleQuote( argList ), '\'' );
linkHref.push( ')' );
}
else
linkHref = [ 'mailto:', address, argList ];
break;
}
default :
{
// Separating name and domain.
var nameAndDomain = address.split( '@', 2 );
email.name = nameAndDomain[ 0 ];
email.domain = nameAndDomain[ 1 ];
linkHref = [ 'javascript:', protectEmailLinkAsFunction( email ) ];
}
}
attributes[ 'data-cke-saved-href' ] = linkHref.join( '' );
break;
}
// Popups and target.
if ( data.target )
{
if ( data.target.type == 'popup' )
{
var onclickList = [ 'window.open(this.href, \'',
data.target.name || '', '\', \'' ];
var featureList = [ 'resizable', 'status', 'location', 'toolbar', 'menubar', 'fullscreen',
'scrollbars', 'dependent' ];
var featureLength = featureList.length;
var addFeature = function( featureName )
{
if ( data.target[ featureName ] )
featureList.push( featureName + '=' + data.target[ featureName ] );
};
for ( var i = 0 ; i < featureLength ; i++ )
featureList[i] = featureList[i] + ( data.target[ featureList[i] ] ? '=yes' : '=no' ) ;
addFeature( 'width' );
addFeature( 'left' );
addFeature( 'height' );
addFeature( 'top' );
onclickList.push( featureList.join( ',' ), '\'); return false;' );
attributes[ 'data-cke-pa-onclick' ] = onclickList.join( '' );
// Add the "target" attribute. (#5074)
removeAttributes.push( 'target' );
}
else
{
if ( data.target.type != 'notSet' && data.target.name )
attributes.target = data.target.name;
else
removeAttributes.push( 'target' );
removeAttributes.push( 'data-cke-pa-onclick', 'onclick' );
}
}
// Advanced attributes.
if ( data.adv )
{
var advAttr = function( inputName, attrName )
{
var value = data.adv[ inputName ];
if ( value )
attributes[attrName] = value;
else
removeAttributes.push( attrName );
};
advAttr( 'advId', 'id' );
advAttr( 'advLangDir', 'dir' );
advAttr( 'advAccessKey', 'accessKey' );
if ( data.adv[ 'advName' ] )
{
attributes[ 'name' ] = attributes[ 'data-cke-saved-name' ] = data.adv[ 'advName' ];
attributes[ 'class' ] = ( attributes[ 'class' ] ? attributes[ 'class' ] + ' ' : '' ) + 'cke_anchor';
}
else
removeAttributes = removeAttributes.concat( [ 'data-cke-saved-name', 'name' ] );
advAttr( 'advLangCode', 'lang' );
advAttr( 'advTabIndex', 'tabindex' );
advAttr( 'advTitle', 'title' );
advAttr( 'advContentType', 'type' );
advAttr( 'advCSSClasses', 'class' );
advAttr( 'advCharset', 'charset' );
advAttr( 'advStyles', 'style' );
advAttr( 'advRel', 'rel' );
}
+ var selection = editor.getSelection();
// Browser need the "href" fro copy/paste link to work. (#6641)
- attributes.href = attributes[ 'data-cke-saved-href' ];
+ attributes.href = attributes['data-cke-saved-href'];
- if ( !this._.selectedElement )
- {
- // Create element if current selection is collapsed.
- var selection = editor.getSelection(),
- ranges = selection.getRanges( true );
- if ( ranges.length == 1 && ranges[0].collapsed )
- {
+ if ( !this._.selectedElement ) {
+ var range = selection.getRanges(1)[0];
+
+ // Use link URL as text with a collapsed cursor.
+ if ( range.collapsed ) {
// Short mailto link text view (#5736).
- var text = new CKEDITOR.dom.text( data.type == 'email' ?
- data.email.address : attributes[ 'data-cke-saved-href' ], editor.document );
- ranges[0].insertNode( text );
- ranges[0].selectNodeContents( text );
- selection.selectRanges( ranges );
+ var text = new CKEDITOR.dom.text(data.type == 'email' ? data.email.address : attributes['data-cke-saved-href'], editor.document);
+ range.insertNode(text);
+ range.selectNodeContents(text);
}
// Apply style.
- var style = new CKEDITOR.style( { element : 'a', attributes : attributes } );
- style.type = CKEDITOR.STYLE_INLINE; // need to override... dunno why.
- style.apply( editor.document );
+ var style = new CKEDITOR.style({element: 'a', attributes: attributes});
+ style.type = CKEDITOR.STYLE_INLINE; // need to override... dunno why.
+ style.applyToRange(range);
+ range.select();
}
- else
- {
+ else {
// We're only editing an existing link, so just overwrite the attributes.
var element = this._.selectedElement,
- href = element.data( 'cke-saved-href' ),
+ href = element.data('cke-saved-href'),
textView = element.getHtml();
- // IE BUG: Setting the name attribute to an existing link doesn't work.
- // Must re-create the link from weired syntax to workaround.
- if ( CKEDITOR.env.ie && !( CKEDITOR.document.$.documentMode >= 8 ) && attributes.name != element.getAttribute( 'name' ) )
- {
- var newElement = new CKEDITOR.dom.element( '