/**
  * Logic to save an item
  *
  * @access public
  * @return void
  * @since 1.0
  */
 function save()
 {
     // Check for request forgeries
     JRequest::checkToken() or jexit('Invalid Token');
     //echo '<html>  <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <body>';
     // Initialize variables
     $app = JFactory::getApplication();
     $db = JFactory::getDBO();
     $user = JFactory::getUser();
     $config = JFactory::getConfig();
     $session = JFactory::getSession();
     $task = JRequest::getVar('task');
     $ctrl_task = 'task=items.';
     // *********************
     // Get data from request
     // *********************
     // Retrieve form data these are subject to basic filtering
     $data = JRequest::getVar('jform', array(), 'post', 'array');
     // Core Fields and and item Parameters
     $custom = JRequest::getVar('custom', array(), 'post', 'array');
     // Custom Fields
     $jfdata = JRequest::getVar('jfdata', array(), 'post', 'array');
     // Joomfish Data
     // Set into model: id (needed for loading correct item), and type id (e.g. needed for getting correct type parameters for new items)
     $data_id = (int) $data['id'];
     $isnew = $data_id == 0;
     // If new make sure that type id is set too, before creating the model
     if ($isnew) {
         $typeid = JRequest::setvar('typeid', (int) @$data['type_id']);
     }
     // Get the model
     $model = $this->getModel('item');
     $model->setId($data_id);
     // Make sure id is correct
     // Get some flags this will also trigger item loading if not already loaded
     $isOwner = $model->get('created_by') == $user->get('id');
     // Get merged parameters: component, type, menu (FE)
     $params = new JRegistry();
     $model_params = $model->getComponentTypeParams();
     $params->merge($model_params);
     // Unique id for new items, needed by some fields for temporary data
     $unique_tmp_itemid = JRequest::getVar('unique_tmp_itemid');
     // Auto title for some content types
     if ($params->get('auto_title', 0)) {
         $data['title'] = (int) $data['id'];
     }
     // item id or ZERO for new items
     // *************************************
     // ENFORCE can change category ACL perms
     // *************************************
     $perms = FlexicontentHelperPerm::getPerm();
     // Per content type change category permissions
     $current_type_id = $isnew || !$model->get('type_id') ? (int) @$data['type_id'] : $model->get('type_id');
     // GET current (existing/old) item TYPE ID
     $CanChangeFeatCat = $user->authorise('flexicontent.change.cat.feat', 'com_flexicontent.type.' . $current_type_id);
     $CanChangeSecCat = $user->authorise('flexicontent.change.cat.sec', 'com_flexicontent.type.' . $current_type_id);
     $CanChangeCat = $user->authorise('flexicontent.change.cat', 'com_flexicontent.type.' . $current_type_id);
     $AutoApproveChanges = $perms->AutoApproveChanges;
     $enable_featured_cid_selector = $perms->MultiCat && $CanChangeFeatCat;
     $enable_cid_selector = $perms->MultiCat && $CanChangeSecCat;
     $enable_catid_selector = $isnew && !$params->get('catid_default') || !$isnew && !$model->get('catid') || $CanChangeCat;
     // Enforce maintaining featured categories
     $featured_cats_parent = $params->get('featured_cats_parent', 0);
     $featured_cats = array();
     if ($featured_cats_parent && !$enable_featured_cid_selector) {
         $featured_tree = flexicontent_cats::getCategoriesTree($published_only = 1, $parent_id = $featured_cats_parent, $depth_limit = 0);
         $disabled_cats = $params->get('featured_cats_parent_disable', 1) ? array($featured_cats_parent) : array();
         $featured_cid = array();
         if (!$isnew) {
             foreach ($model->get('categories') as $item_cat) {
                 if (isset($featured_tree[$item_cat]) && !isset($disabled_cats[$item_cat])) {
                     $featured_cid[] = $item_cat;
                 }
             }
         }
         $data['featured_cid'] = $featured_cid;
     }
     // Enforce maintaining secondary categories
     if (!$enable_cid_selector) {
         if ($isnew) {
             $data['cid'] = $params->get('cid_default');
         } else {
             if (isset($featured_cid)) {
                 $featured_cid_arr = array_flip($featured_cid);
                 $sec_cid = array();
                 foreach ($model->get('cats') as $item_cat) {
                     if (!isset($featured_cid_arr[$item_cat])) {
                         $sec_cid[] = $item_cat;
                     }
                 }
                 $data['cid'] = $sec_cid;
             } else {
                 $data['cid'] = $model->get('cats');
             }
         }
     }
     if (!$enable_catid_selector) {
         if ($isnew && $params->get('catid_default')) {
             $data['catid'] = $params->get('catid_default');
         } else {
             if ($model->get('catid')) {
                 $data['catid'] = $model->get('catid');
             }
         }
     }
     // **************************
     // Basic Form data validation
     // **************************
     // Get the JForm object, but do not pass any data we only want the form object,
     // in order to validate the data and not create a filled-in form
     $form = $model->getForm();
     // Validate Form data for core fields and for parameters
     $post = $model->validate($form, $data);
     // Check for validation error
     if (!$post) {
         // Get the validation messages and push up to three validation messages out to the user
         $errors = $form->getErrors();
         for ($i = 0, $n = count($errors); $i < $n && $i < 3; $i++) {
             $app->enqueueMessage($errors[$i] instanceof Exception ? $errors[$i]->getMessage() : $errors[$i], 'error');
         }
         // Set POST form date into the session, so that they get reloaded
         $app->setUserState($form->option . '.edit.' . $form->context . '.data', $data);
         // Save the jform data in the session
         $app->setUserState($form->option . '.edit.' . $form->context . '.custom', $custom);
         // Save the custom fields data in the session
         $app->setUserState($form->option . '.edit.' . $form->context . '.jfdata', $jfdata);
         // Save the falang translations into the session
         $app->setUserState($form->option . '.edit.' . $form->context . '.unique_tmp_itemid', $unique_tmp_itemid);
         // Save temporary unique item id into the session
         // Redirect back to the item form
         $this->setRedirect($_SERVER['HTTP_REFERER']);
         if (JRequest::getVar('fc_doajax_submit')) {
             echo flexicontent_html::get_system_messages_html();
             exit;
             // Ajax submit, do not rerender the view
         }
         return false;
         //die('error');
     }
     // Some values need to be assigned after validation
     $post['attribs'] = @$data['attribs'];
     // Workaround for item's template parameters being clear by validation since they are not present in item.xml
     $post['custom'] =& $custom;
     // Assign array of custom field values, they are in the 'custom' form array instead of jform
     $post['jfdata'] =& $jfdata;
     // Assign array of Joomfish field values, they are in the 'jfdata' form array instead of jform
     // Assign template parameters of the select ilayout as an sub-array (the DB model will handle the merging of parameters)
     $ilayout = $data['attribs']['ilayout'];
     if (!empty($data['layouts'][$ilayout])) {
         //echo "<pre>"; print_r($post['attribs']);
         //$post['attribs'] = array_merge($post['attribs'], $data['layouts'][$ilayout]);
         $post['attribs']['layouts'] = $data['layouts'];
         //echo "<pre>"; print_r($post['attribs']); exit;
     }
     // USEFULL FOR DEBUGING for J2.5 (do not remove commented code)
     //$diff_arr = array_diff_assoc ( $data, $post);
     //echo "<pre>"; print_r($diff_arr); jexit();
     // Make sure Content ID in the REQUEST is set, this is needed in BACKEND, needed in some cases
     // NOTE this is not the same as jform['cid'] which is the category IDs of the Content Item
     JRequest::setVar('cid', array($model->getId()), 'post', 'array');
     // ********************************************************************************
     // PERFORM ACCESS CHECKS, NOTE: we need to check access again, despite having
     // checked them on edit form load, because user may have tampered with the form ...
     // ********************************************************************************
     $itemAccess = $model->getItemAccess();
     $canAdd = $itemAccess->get('access-create');
     // includes check of creating in at least one category
     $canEdit = $itemAccess->get('access-edit');
     // includes privileges edit and edit-own
     $type_id = (int) @$post['type_id'];
     // Typecast to int, (already done for J2.5 via validating)
     if (!$isnew && $model->get('type_id') == $type_id) {
         // Existing item with Type not being ALTERED, content type can be maintained regardless of privilege
         $canCreateType = true;
     } else {
         // New item or existing item with Type is being ALTERED, check privilege to create items of this type
         $canCreateType = $model->canCreateType(array($type_id), true, $types);
     }
     // *****************************************************************
     // Calculate user's CREATE / EDIT privileges on current content item
     // *****************************************************************
     $hasCoupon = false;
     // Normally used in frontend only
     if (!$isnew) {
         // If no edit privilege, check if item is editable till logoff
         if (!$canEdit) {
             if ($session->has('rendered_uneditable', 'flexicontent')) {
                 $rendered_uneditable = $session->get('rendered_uneditable', array(), 'flexicontent');
                 $canEdit = isset($rendered_uneditable[$model->get('id')]) && $rendered_uneditable[$model->get('id')];
                 $hasCoupon = isset($rendered_uneditable[$model->get('id')]) && $rendered_uneditable[$model->get('id')] == 2;
                 // editable via coupon
             }
         }
     } else {
         // No special CREATE allowing case for backend
     }
     // New item: check if user can create in at least one category
     if ($isnew && !$canAdd) {
         JError::raiseWarning(403, JText::_('FLEXI_NO_ACCESS_CREATE'));
         $this->setRedirect($_SERVER['HTTP_REFERER']);
         if (JRequest::getVar('fc_doajax_submit')) {
             echo flexicontent_html::get_system_messages_html();
             exit;
             // Ajax submit, do not rerender the view
         }
         return;
     }
     // Existing item: Check if user can edit current item
     if (!$isnew && !$canEdit) {
         JError::raiseWarning(403, JText::_('FLEXI_NO_ACCESS_EDIT'));
         $this->setRedirect($_SERVER['HTTP_REFERER']);
         if (JRequest::getVar('fc_doajax_submit')) {
             echo flexicontent_html::get_system_messages_html();
             exit;
             // Ajax submit, do not rerender the view
         }
         return;
     }
     if (!$canCreateType) {
         $msg = isset($types[$type_id]) ? JText::sprintf('FLEXI_NO_ACCESS_CREATE_CONTENT_OF_TYPE', JText::_($types[$type_id]->name)) : ' Content Type ' . $type_id . ' was not found OR is not published';
         JError::raiseWarning(403, $msg);
         $this->setRedirect($_SERVER['HTTP_REFERER']);
         if (JRequest::getVar('fc_doajax_submit')) {
             echo flexicontent_html::get_system_messages_html();
             exit;
             // Ajax submit, do not rerender the view
         }
         return;
     }
     // Get "BEFORE SAVE" categories for information mail
     $before_cats = array();
     if (!$isnew) {
         $query = 'SELECT DISTINCT c.id, c.title FROM #__categories AS c' . ' JOIN #__flexicontent_cats_item_relations AS rel ON rel.catid = c.id' . ' WHERE rel.itemid = ' . (int) $model->get('id');
         $db->setQuery($query);
         $before_cats = $db->loadObjectList('id');
         $before_maincat = $model->get('catid');
         $original_item = $model->getItem($post['id'], $check_view_access = false, $no_cache = true, $force_version = 0);
     }
     // ****************************************
     // Try to store the form data into the item
     // ****************************************
     if (!$model->store($post)) {
         // Set error message about saving failed, and also the reason (=model's error message)
         $msg = JText::_('FLEXI_ERROR_STORING_ITEM');
         JError::raiseWarning(500, $msg . ": " . $model->getError());
         // Set POST form date into the session, so that they get reloaded
         $app->setUserState($form->option . '.edit.' . $form->context . '.data', $data);
         // Save the jform data in the session
         $app->setUserState($form->option . '.edit.' . $form->context . '.custom', $custom);
         // Save the custom fields data in the session
         $app->setUserState($form->option . '.edit.' . $form->context . '.jfdata', $jfdata);
         // Save the falang translations into the session
         $app->setUserState($form->option . '.edit.' . $form->context . '.unique_tmp_itemid', $unique_tmp_itemid);
         // Save temporary unique item id into the session
         // Saving has failed check-in and redirect back to the item form,
         // redirect back to the item form reloading the posted data
         $model->checkin();
         $this->setRedirect($_SERVER['HTTP_REFERER']);
         if (JRequest::getVar('fc_doajax_submit')) {
             echo flexicontent_html::get_system_messages_html();
             exit;
             // Ajax submit, do not rerender the view
         }
         return;
         //die('save error');
     }
     // **************************************************
     // Check in model and get item id in case of new item
     // **************************************************
     $model->checkin();
     $post['id'] = $isnew ? (int) $model->get('id') : $post['id'];
     // Get items marked as newly submitted
     $newly_submitted = $session->get('newly_submitted', array(), 'flexicontent');
     if ($isnew) {
         // Mark item as newly submitted, to allow to a proper "THANKS" message after final save & close operation (since user may have clicked add instead of add & close)
         $newly_submitted[$model->get('id')] = 1;
         $session->set('newly_submitted', $newly_submitted, 'flexicontent');
     }
     $newly_submitted_item = @$newly_submitted[$model->get('id')];
     // ***********************************************************************************************************
     // Get newly saved -latest- version (store task gets latest) of the item, and also calculate publish privelege
     // ***********************************************************************************************************
     $item = $model->getItem($post['id'], $check_view_access = false, $no_cache = true, $force_version = -1);
     $canPublish = $model->canEditState($item, $check_cat_perm = true) || $hasCoupon;
     // ********************************************************************************************
     // Use session to detect multiple item saves to avoid sending notification EMAIL multiple times
     // ********************************************************************************************
     $is_first_save = true;
     if ($session->has('saved_fcitems', 'flexicontent')) {
         $saved_fcitems = $session->get('saved_fcitems', array(), 'flexicontent');
         $is_first_save = $isnew ? true : !isset($saved_fcitems[$model->get('id')]);
     }
     // Add item to saved items of the corresponding session array
     $saved_fcitems[$model->get('id')] = $timestamp = time();
     // Current time as seconds since Unix epoc;
     $session->set('saved_fcitems', $saved_fcitems, 'flexicontent');
     // ********************************************
     // Get categories added / removed from the item
     // ********************************************
     $query = 'SELECT DISTINCT c.id, c.title FROM #__categories AS c' . ' JOIN #__flexicontent_cats_item_relations AS rel ON rel.catid = c.id' . ' WHERE rel.itemid = ' . (int) $model->get('id');
     $db->setQuery($query);
     $after_cats = $db->loadObjectList('id');
     if (!$isnew) {
         $cats_added_ids = array_diff(array_keys($after_cats), array_keys($before_cats));
         foreach ($cats_added_ids as $cats_added_id) {
             $cats_added_titles[] = $after_cats[$cats_added_id]->title;
         }
         $cats_removed_ids = array_diff(array_keys($before_cats), array_keys($after_cats));
         foreach ($cats_removed_ids as $cats_removed_id) {
             $cats_removed_titles[] = $before_cats[$cats_removed_id]->title;
         }
         $cats_altered = count($cats_added_ids) + count($cats_removed_ids);
         $after_maincat = $model->get('catid');
     }
     // *******************************************************************************************************************
     // We need to get emails to notify, from Global/item's Content Type parameters -AND- from item's categories parameters
     // *******************************************************************************************************************
     $notify_emails = array();
     if ($is_first_save || $cats_altered || $params->get('nf_enable_debug', 0)) {
         // Get needed flags regarding the saved items
         $approve_version = 2;
         $pending_approval_state = -3;
         $draft_state = -4;
         $current_version = FLEXIUtilities::getCurrentVersions($item->id, true);
         // Get current item version
         $last_version = FLEXIUtilities::getLastVersions($item->id, true);
         // Get last version (=latest one saved, highest version id),
         // $post variables vstate & state may have been (a) tampered in the form, and/or (b) altered by save procedure so better not use them
         $needs_version_reviewal = !$isnew && $last_version > $current_version && !$canPublish && !$AutoApproveChanges;
         $needs_publication_approval = $isnew && $item->state == $pending_approval_state && !$canPublish;
         $draft_from_non_publisher = $item->state == $draft_state && !$canPublish;
         if ($draft_from_non_publisher) {
             // Suppress notifications for draft-state items (new or existing ones), for these each author will publication approval manually via a button
             $nConf = false;
         } else {
             // Get notifications configuration and select appropriate emails for current saving case
             $nConf = $model->getNotificationsConf($params);
             //echo "<pre>"; print_r($nConf); "</pre>";
         }
         if ($nConf) {
             $states_notify_new = $params->get('states_notify_new', array(1, 0, FLEXI_J16GE ? 2 : -1, -3, -4, -5));
             if (empty($states_notify_new)) {
                 $states_notify_new = array();
             } else {
                 if (!is_array($states_notify_new)) {
                     $states_notify_new = !FLEXI_J16GE ? array($states_notify_new) : explode("|", $states_notify_new);
                 }
             }
             $states_notify_existing = $params->get('states_notify_existing', array(1, 0, FLEXI_J16GE ? 2 : -1, -3, -4, -5));
             if (empty($states_notify_existing)) {
                 $states_notify_existing = array();
             } else {
                 if (!is_array($states_notify_existing)) {
                     $states_notify_existing = !FLEXI_J16GE ? array($states_notify_existing) : explode("|", $states_notify_existing);
                 }
             }
             $n_state_ok = in_array($item->state, $states_notify_new);
             $e_state_ok = in_array($item->state, $states_notify_existing);
             if ($needs_publication_approval) {
                 $notify_emails = $nConf->emails->notify_new_pending;
             } else {
                 if ($isnew && $n_state_ok) {
                     $notify_emails = $nConf->emails->notify_new;
                 } else {
                     if ($isnew) {
                         $notify_emails = array();
                     } else {
                         if ($needs_version_reviewal) {
                             $notify_emails = $nConf->emails->notify_existing_reviewal;
                         } else {
                             if (!$isnew && $e_state_ok) {
                                 $notify_emails = $nConf->emails->notify_existing;
                             } else {
                                 if (!$isnew) {
                                     $notify_emails = array();
                                 }
                             }
                         }
                     }
                 }
             }
             if ($needs_publication_approval) {
                 $notify_text = $params->get('text_notify_new_pending');
             } else {
                 if ($isnew) {
                     $notify_text = $params->get('text_notify_new');
                 } else {
                     if ($needs_version_reviewal) {
                         $notify_text = $params->get('text_notify_existing_reviewal');
                     } else {
                         if (!$isnew) {
                             $notify_text = $params->get('text_notify_existing');
                         }
                     }
                 }
             }
             //print_r($notify_emails); jexit();
         }
     }
     // *********************************************************************************************************************
     // If there are emails to notify for current saving case, then send the notifications emails, but
     // *********************************************************************************************************************
     if (!empty($notify_emails) && count($notify_emails)) {
         $notify_vars = new stdClass();
         $notify_vars->needs_version_reviewal = $needs_version_reviewal;
         $notify_vars->needs_publication_approval = $needs_publication_approval;
         $notify_vars->isnew = $isnew;
         $notify_vars->notify_emails = $notify_emails;
         $notify_vars->notify_text = $notify_text;
         $notify_vars->before_cats = $before_cats;
         $notify_vars->after_cats = $after_cats;
         $notify_vars->original_item = @$original_item;
         $model->sendNotificationEmails($notify_vars, $params, $manual_approval_request = 0);
     }
     // ***************************************************
     // CLEAN THE CACHE so that our changes appear realtime
     // ***************************************************
     $cache = FLEXIUtilities::getCache($group = '', 0);
     $cache->clean('com_flexicontent_items');
     $cache->clean('com_flexicontent_filters');
     $cache = FLEXIUtilities::getCache($group = '', 1);
     $cache->clean('com_flexicontent_items');
     $cache->clean('com_flexicontent_filters');
     // ****************************************************************************************************************************
     // Recalculate EDIT PRIVILEGE of new item. Reason for needing to do this is because we can have create permission in a category
     // and thus being able to set this category as item's main category, but then have no edit/editown permission for this category
     // ****************************************************************************************************************************
     $asset = 'com_content.article.' . $model->get('id');
     $canEdit = $user->authorise('core.edit', $asset) || $user->authorise('core.edit.own', $asset) && $isOwner;
     // ALTERNATIVE 1
     //$canEdit = $model->getItemAccess()->get('access-edit'); // includes privileges edit and edit-own
     // ALTERNATIVE 2
     //$rights = FlexicontentHelperPerm::checkAllItemAccess($user->get('id'), 'item', $model->get('id'));
     //$canEdit = in_array('edit', $rights) || (in_array('edit.own', $rights) && $isOwner) ;
     // *******************************************************************************************************
     // Check if user can not edit item further (due to changed main category, without edit/editown permission)
     // *******************************************************************************************************
     if (!$canEdit) {
         if ($task == 'apply' || $task == 'apply_type') {
             // APPLY TASK: Temporarily set item to be editable till closing it and not through all session
             // (we will/should clear this flag when item is closed, since we have another flag to indicate new items
             $rendered_uneditable = $session->get('rendered_uneditable', array(), 'flexicontent');
             $rendered_uneditable[$model->get('id')] = -1;
             $session->set('rendered_uneditable', $rendered_uneditable, 'flexicontent');
             $canEdit = 1;
         } else {
             if ($newly_submitted_item) {
                 // NEW ITEM: Do not use editable till logoff behaviour
                 // ALSO: Clear editable FLAG set in the case that 'apply' button was used during new item creation
                 if (!$params->get('items_session_editable', 0)) {
                     $rendered_uneditable = $session->get('rendered_uneditable', array(), 'flexicontent');
                     if (isset($rendered_uneditable[$model->get('id')])) {
                         unset($rendered_uneditable[$model->get('id')]);
                         $session->set('rendered_uneditable', $rendered_uneditable, 'flexicontent');
                     }
                 }
             } else {
                 // EXISTING ITEM: (if enabled) Use the editable till logoff behaviour
                 if ($params->get('items_session_editable', 0)) {
                     // Set notice for existing item being editable till logoff
                     JError::raiseNotice(403, JText::_('FLEXI_CANNOT_EDIT_AFTER_LOGOFF'));
                     // Allow item to be editable till logoff
                     $rendered_uneditable = $session->get('rendered_uneditable', array(), 'flexicontent');
                     $rendered_uneditable[$model->get('id')] = 1;
                     $session->set('rendered_uneditable', $rendered_uneditable, 'flexicontent');
                     $canEdit = 1;
                 }
             }
         }
         // Set notice about saving an item that cannot be changed further
         if (!$canEdit) {
             $app->enqueueMessage(JText::_('FLEXI_CANNOT_MAKE_FURTHER_CHANGES_TO_CONTENT'), 'message');
         }
     }
     // ****************************************************************
     // Check for new Content Item is being closed, and clear some flags
     // ****************************************************************
     if ($task != 'apply' && $task != 'apply_type' && $newly_submitted_item) {
         // Clear item from being marked as newly submitted
         unset($newly_submitted[$model->get('id')]);
         $session->set('newly_submitted', $newly_submitted, 'flexicontent');
         // The 'apply' task may set 'editable till logoff' FLAG ...
         // CLEAR IT, since NEW content this is meant to be used temporarily
         if (!$params->get('items_session_editable', 0)) {
             $rendered_uneditable = $session->get('rendered_uneditable', array(), 'flexicontent');
             if (isset($rendered_uneditable[$model->get('id')])) {
                 unset($rendered_uneditable[$model->get('id')]);
                 $session->set('rendered_uneditable', $rendered_uneditable, 'flexicontent');
             }
         }
     }
     // ****************************************
     // Saving is done, decide where to redirect
     // ****************************************
     switch ($task) {
         case 'apply':
         case 'apply_type':
             $link = 'index.php?option=com_flexicontent&' . $ctrl_task . 'edit&cid=' . (int) $model->get('id');
             break;
         case 'saveandnew':
             $link = $type_id ? 'index.php?option=com_flexicontent&view=item&typeid=' . $type_id : 'index.php?option=com_flexicontent&view=item';
             break;
         default:
             $link = 'index.php?option=com_flexicontent&view=items';
             break;
     }
     $msg = JText::_('FLEXI_ITEM_SAVED');
     $this->setRedirect($link, $msg);
     if (JRequest::getVar('fc_doajax_submit')) {
         JFactory::getApplication()->enqueueMessage($msg, 'message');
         echo flexicontent_html::get_system_messages_html();
         exit;
         // Ajax submit, do not rerender the view
     }
 }
 function getDiffVersions($current_versions = array(), $last_versions = array())
 {
     // check if the category was chosen to avoid adding data on static contents
     if (!FLEXI_CAT_EXTENSION) {
         return array();
     }
     if (!$current_versions) {
         $current_versions = FLEXIUtilities::getCurrentVersions();
     }
     if (!$last_versions) {
         $last_versions = FLEXIUtilities::getLastVersions();
     }
     $difference = $current_versions;
     foreach ($current_versions as $key1 => $value1) {
         foreach ($last_versions as $key2 => $value2) {
             if ($value1["id"] == $value2["id"] && $value1["version"] == $value2["version"]) {
                 unset($difference[$key1]);
             }
         }
     }
     return $difference;
 }
 /**
  * Method to delete old core fields data in the fields_items_relations table
  * Delete also old versions fields data
  * Alter value fields to mediumtext in order to store large items
  * 
  * @access	public
  * @return	boolean	True on success
  * @since 1.5
  */
 function cleanupOldTables()
 {
     // Check for request forgeries
     JRequest::checkToken('request') or jexit('Invalid Token');
     $db = JFactory::getDBO();
     $app = JFactory::getApplication();
     $queries = array();
     // alter some table field types
     $queries[] = "ALTER TABLE #__flexicontent_fields_item_relations CHANGE `value` `value` MEDIUMTEXT";
     $queries[] = "ALTER TABLE #__flexicontent_items_versions CHANGE `value` `value` MEDIUMTEXT";
     $queries[] = "ALTER TABLE #__flexicontent_items_ext" . "  CHANGE `search_index` `search_index` MEDIUMTEXT" . ", CHANGE `sub_items` `sub_items` TEXT" . ", CHANGE `sub_categories` `sub_categories` TEXT" . ", CHANGE `related_items` `related_items` TEXT";
     foreach ($queries as $query) {
         $db->setQuery($query);
         $db->query();
     }
     $query = "SELECT id,version,created,created_by FROM #__content " . (!FLEXI_J16GE ? "WHERE sectionid='" . FLEXI_SECTION . "'" : "");
     $db->setQuery($query);
     $rows = $db->loadObjectList();
     //$jcorefields = flexicontent_html::getJCoreFields();
     $add_cats = true;
     $add_tags = true;
     $clean_database = true;
     // For all items not having the current version, add it
     $last_versions = FLEXIUtilities::getLastVersions();
     foreach ($rows as $row) {
         $lastversion = @$last_versions[$row->id]['version'];
         if ($row->version > $lastversion) {
             // Get field values of the current item version
             $query = "SELECT f.id,fir.value,f.field_type,f.name,fir.valueorder,fir.suborder,f.iscore " . " FROM #__flexicontent_fields_item_relations as fir" . " JOIN #__flexicontent_fields as f on f.id=fir.field_id " . " WHERE fir.item_id=" . $row->id . " AND f.iscore=0";
             // old versions stored categories & tags into __flexicontent_fields_item_relations
             $db->setQuery($query);
             $fields = $db->loadObjectList();
             // Delete old data
             if ($clean_database && $fields) {
                 $query = 'DELETE FROM #__flexicontent_fields_item_relations WHERE item_id = ' . $row->id;
                 $db->setQuery($query);
                 $db->query();
             }
             // Add the 'maintext' field to the fields array for adding to versioning table
             $f = new stdClass();
             $f->id = 1;
             $f->iscore = 1;
             $f->valueorder = 1;
             $f->suborder = 1;
             $f->field_type = "maintext";
             $f->name = "text";
             $f->value = $row->introtext;
             if (JString::strlen($row->fulltext) > 1) {
                 $f->value .= '<hr id="system-readmore" />' . $row->fulltext;
             }
             if (substr($f->value, 0, 3) != "<p>") {
                 $f->value = "<p>" . $f->value . "</p>";
             }
             $fields[] = $f;
             // Add the 'categories' field to the fields array for adding to versioning table
             $query = "SELECT catid FROM #__flexicontent_cats_item_relations WHERE itemid='" . $row->id . "';";
             $db->setQuery($query);
             $categories = FLEXI_J16GE ? $db->loadColumn() : $db->loadResultArray();
             if (!$categories || !count($categories)) {
                 $categories = array($catid = $row->catid);
                 $query = "INSERT INTO #__flexicontent_cats_item_relations VALUES('{$catid}','" . $row->id . "', '0');";
                 $db->setQuery($query);
                 $db->query();
             }
             $f = new stdClass();
             $f->id = 13;
             $f->iscore = 1;
             $f->valueorder = 1;
             $f->suborder = 1;
             $f->version = (int) $row->version;
             $f->value = serialize($categories);
             if ($add_cats) {
                 $fields[] = $f;
             }
             // Add the 'tags' field to the fields array for adding to versioning table
             $query = "SELECT tid FROM #__flexicontent_tags_item_relations WHERE itemid='" . $row->id . "';";
             $db->setQuery($query);
             $tags = FLEXI_J16GE ? $db->loadColumn() : $db->loadResultArray();
             $f = new stdClass();
             $f->id = 14;
             $f->iscore = 1;
             $f->valueorder = 1;
             $f->suborder = 1;
             $f->version = (int) $row->version;
             $f->value = serialize($tags);
             if ($add_tags) {
                 $fields[] = $f;
             }
             // Add field values to field value versioning table
             foreach ($fields as $field) {
                 // add the new values to the database
                 $obj = new stdClass();
                 $obj->field_id = $field->id;
                 $obj->item_id = $row->id;
                 $obj->valueorder = $field->valueorder;
                 $obj->suborder = $field->suborder;
                 $obj->version = (int) $row->version;
                 $obj->value = $field->value;
                 //echo "version: ".$obj->version.",fieldid : ".$obj->field_id.",value : ".$obj->value.",valueorder : ".$obj->valueorder.",suborder : ".$obj->suborder."<br />";
                 //echo "inserting into __flexicontent_items_versions<br />";
                 $db->insertObject('#__flexicontent_items_versions', $obj);
                 if (!$field->iscore) {
                     unset($obj->version);
                     //echo "inserting into __flexicontent_fields_item_relations<br />";
                     $db->insertObject('#__flexicontent_fields_item_relations', $obj);
                 }
                 //$searchindex 	.= @$field->search;
             }
             // **********************************************************************************
             // Add basic METADATA of current item version (kept in table #__flexicontent_versions)
             // **********************************************************************************
             $v = new stdClass();
             $v->item_id = (int) $row->id;
             $v->version_id = (int) $row->version;
             $v->created = $row->modified && $row->modified != $nullDate ? $row->modified : $row->created;
             $v->created_by = $row->created_by;
             $v->comment = '';
             //echo "inserting into __flexicontent_versions<br />";
             $db->insertObject('#__flexicontent_versions', $v);
         }
     }
     $queries = array();
     // delete unused records
     // 1,'maintext',  2,'created',  3,'createdby',  4,'modified',  5,'modifiedby',  6,'title',  7,'hits'
     // 8,'type',  9,'version',  10,'state',   11,'voting',  12,'favourites',  13,'categories',  14,'tags'
     $queries[] = "DELETE FROM #__flexicontent_fields_item_relations WHERE field_id < 15";
     $queries[] = "DELETE FROM #__flexicontent_items_versions WHERE field_id IN ( 7, 9, 11, 12 )";
     foreach ($queries as $query) {
         $db->setQuery($query);
         $db->query();
     }
     $catscache = JFactory::getCache('com_flexicontent_cats');
     $catscache->clean();
     $model = $this->getModel('flexicontent');
     if ($model->getNoOldFieldsData()) {
         echo '<span class="install-ok"></span>';
     } else {
         echo '<span class="install-notok"></span>';
         jexit();
     }
 }
 /**
  * Method to save field values of the item in field versioning DB table or in ..._fields_item_relations DB table 
  *
  * @access	public
  * @return	boolean	True on success
  * @since	1.0
  */
 function saveFields($isnew, &$item, &$data, &$files)
 {
     $app = JFactory::getApplication();
     $user = JFactory::getUser();
     $dispatcher = JDispatcher::getInstance();
     $cparams = $this->_cparams;
     $use_versioning = $cparams->get('use_versioning', 1);
     $print_logging_info = $cparams->get('print_logging_info');
     $last_version = FLEXIUtilities::getLastVersions($item->id, true);
     $mval_query = true;
     if ($print_logging_info) {
         global $fc_run_times;
     }
     if ($print_logging_info) {
         $start_microtime = microtime(true);
     }
     //*********************************
     // Checks for untranslatable fields
     //*********************************
     // CASE 1. Check if saving an item that translates an original content in site's default language
     // ... Decide whether to retrieve field values of untranslatable fields from the original content item
     $enable_translation_groups = $cparams->get('enable_translation_groups');
     $is_content_default_lang = substr(flexicontent_html::getSiteDefaultLang(), 0, 2) == substr($item->language, 0, 2);
     $get_untraslatable_values = $enable_translation_groups && !$is_content_default_lang && $item->lang_parent_id && $item->lang_parent_id != $item->id;
     // CASE 2. Check if saving an original content item (item's language is site default language)
     // ... Get item ids of translating items
     if ($is_content_default_lang && $this->_id) {
         $query = 'SELECT ie.item_id' . ' FROM #__flexicontent_items_ext as ie' . ' WHERE ie.lang_parent_id = ' . (int) $this->_id . '  AND ie.item_id <> ' . (int) $this->_id;
         // DO NOT include the item itself in associated translations !!
         $this->_db->setQuery($query);
         $assoc_item_ids = FLEXI_J16GE ? $this->_db->loadColumn() : $this->_db->loadResultArray();
     }
     if (empty($assoc_item_ids)) {
         $assoc_item_ids = array();
     }
     // ***************************************************************************************************************************
     // Get item's fields ... and their values (for untranslatable fields the field values from original content item are retrieved
     // ***************************************************************************************************************************
     $fields = $this->getExtrafields($force = true, $get_untraslatable_values ? $item->lang_parent_id : 0);
     // ******************************************************************************************************************
     // Loop through Fields triggering onBeforeSaveField Event handlers, this was seperated from the rest of the process
     // to give chance to ALL fields to check their DATA and cancel item saving process before saving any new field values
     // ******************************************************************************************************************
     $searchindex = array();
     //$qindex = array();
     if ($fields) {
         foreach ($fields as $field) {
             // Set vstate property into the field object to allow this to be changed be the before saving  field event handler
             $field->item_vstate = $data['vstate'];
             if (FLEXI_J16GE) {
                 $is_editable = !$field->valueseditable || $user->authorise('flexicontent.editfieldvalues', 'com_flexicontent.field.' . $field->id);
             } else {
                 if (FLEXI_ACCESS && $user->gid < 25) {
                     $is_editable = !$field->valueseditable || FAccess::checkAllContentAccess('com_content', 'submit', 'users', $user->gmid, 'field', $field->id);
                 } else {
                     $is_editable = 1;
                 }
             }
             // FORM HIDDEN FIELDS (FRONTEND/BACKEND) AND (ACL) UNEDITABLE FIELDS: maintain their DB value ...
             if ($app->isSite() && ($field->formhidden == 1 || $field->formhidden == 3 || $field->parameters->get('frontend_hidden')) || $app->isAdmin() && ($field->formhidden == 2 || $field->formhidden == 3 || $field->parameters->get('backend_hidden')) || !$is_editable) {
                 $postdata[$field->name] = $field->value;
                 // UNTRANSLATABLE (CUSTOM) FIELDS: maintain their DB value ...
             } else {
                 if ($get_untraslatable_values && $field->untranslatable) {
                     $postdata[$field->name] = $field->value;
                     // CORE FIELDS: if not set maintain their DB value ...
                 } else {
                     if ($field->iscore) {
                         $postdata[$field->name] = @$data[$field->name];
                         if (is_array($postdata[$field->name]) && !count($postdata[$field->name]) || !is_array($postdata[$field->name]) && !strlen(trim($postdata[$field->name]))) {
                             $postdata[$field->name] = $field->value;
                         }
                         // OTHER CUSTOM FIELDS (not hidden and not untranslatable)
                     } else {
                         $postdata[$field->name] = !FLEXI_J16GE ? @$data[$field->name] : @$data['custom'][$field->name];
                     }
                 }
             }
             // Unserialize values already serialized values, e.g. (a) if current values used are from DB or (b) are being imported from CSV file
             if (!is_array($postdata[$field->name])) {
                 $postdata[$field->name] = strlen($postdata[$field->name]) ? array($postdata[$field->name]) : array();
             }
             //echo "<b>{$field->field_type}</b>: <br/> <pre>".print_r($postdata[$field->name], true)."</pre>\n";
             foreach ($postdata[$field->name] as $i => $postdata_val) {
                 if (@unserialize($postdata_val) !== false || $postdata_val === 'b:0;') {
                     $postdata[$field->name][$i] = unserialize($postdata_val);
                 }
             }
             // Trigger plugin Event 'onBeforeSaveField'
             $fieldname = $field->iscore ? 'core' : $field->field_type;
             $result = FLEXIUtilities::call_FC_Field_Func($fieldname, 'onBeforeSaveField', array(&$field, &$postdata[$field->name], &$files[$field->name], &$item));
             //$qindex[$field->name] = NULL;
             //$result = FLEXIUtilities::call_FC_Field_Func($fieldname, 'onBeforeSaveField', array( &$field, &$postdata[$field->name], &$files[$field->name], &$item, &$qindex[$field->name] ));
             if ($result === false) {
                 // Field requested to abort item saving
                 $this->setError(JText::sprintf('FLEXI_FIELD_VALUE_IS_INVALID', $field->label));
                 return 'abort';
             }
             // Get vstate property from the field object back to the data array
             $data['vstate'] = $field->item_vstate;
         }
         //echo "<pre>"; print_r($postdata); echo "</pre>"; exit;
     }
     if ($print_logging_info) {
         @($fc_run_times['fields_value_preparation'] = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10);
     }
     // ****************************************************************************************************************************
     // Loop through Fields triggering onIndexAdvSearch, onIndexSearch Event handlers, this was seperated from the before save field
     //  event, so that we will update search indexes only if the above has not canceled saving OR has not canceled version approval
     // ****************************************************************************************************************************
     if ($print_logging_info) {
         $start_microtime = microtime(true);
     }
     if ($fields) {
         $ai_query_vals = array();
         foreach ($fields as $field) {
             $fieldname = $field->iscore ? 'core' : $field->field_type;
             if ($data['vstate'] == 2 || $isnew) {
                 // Trigger plugin Event 'onIndexAdvSearch' to update field-item pair records in advanced search index
                 FLEXIUtilities::call_FC_Field_Func($fieldname, 'onIndexAdvSearch', array(&$field, &$postdata[$field->name], &$item));
                 if (isset($field->ai_query_vals)) {
                     foreach ($field->ai_query_vals as $query_val) {
                         $ai_query_vals[] = $query_val;
                     }
                 }
                 //echo $field->name .":".implode(",", @$field->ai_query_vals ? $field->ai_query_vals : array() )."<br/>";
                 // Trigger plugin Event 'onIndexSearch' to update item 's (basic) search index record
                 FLEXIUtilities::call_FC_Field_Func($fieldname, 'onIndexSearch', array(&$field, &$postdata[$field->name], &$item));
                 if (strlen(@$field->search[$item->id])) {
                     $searchindex[] = $field->search[$item->id];
                 }
                 //echo $field->name .":".@$field->search[$item->id]."<br/>";
             }
         }
     }
     // Remove item's old advanced search index entries
     $query = "DELETE FROM #__flexicontent_advsearch_index WHERE item_id=" . $item->id;
     $this->_db->setQuery($query);
     $this->_db->query();
     // Store item's advanced search index entries
     if (!empty($ai_query_vals)) {
         $query = "INSERT INTO #__flexicontent_advsearch_index " . " (field_id,item_id,extraid,search_index,value_id) VALUES " . implode(",", $ai_query_vals);
         $this->_db->setQuery($query);
         $this->_db->query();
         if ($this->_db->getErrorNum()) {
             JFactory::getApplication()->enqueueMessage(__FUNCTION__ . '(): SQL QUERY ERROR:<br/>' . nl2br($this->_db->getErrorMsg()), 'error');
         }
     }
     // Assigned created basic search index into item object
     $item->search_index = implode(' | ', $searchindex);
     // Check if vstate was set to 1 (no approve new version) while versioning is disabled
     if (!$use_versioning && $data['vstate'] != 2) {
         $data['vstate'] = 2;
         $app->enqueueMessage('vstate cannot be set to 1 (=no approve new version) when versioning is disabled', 'notice');
     }
     if ($print_logging_info) {
         @($fc_run_times['fields_value_indexing'] = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10);
     }
     if ($print_logging_info) {
         $start_microtime = microtime(true);
     }
     // **************************************************************************
     // IF new version is approved, remove old version values from the field table
     // **************************************************************************
     if ($data['vstate'] == 2) {
         //echo "delete __flexicontent_fields_item_relations, item_id: " .$item->id;
         $query = 'DELETE FROM #__flexicontent_fields_item_relations WHERE item_id = ' . $item->id;
         $this->_db->setQuery($query);
         $this->_db->query();
         $query = 'DELETE FROM #__flexicontent_items_versions WHERE item_id=' . $item->id . ' AND version=' . ((int) $last_version + 1);
         $this->_db->setQuery($query);
         $this->_db->query();
         $untranslatable_fields = array();
         if ($fields) {
             foreach ($fields as $field) {
                 if ($field->iscore) {
                     continue;
                 }
                 if (count($assoc_item_ids) && $field->untranslatable) {
                     // Delete field values in all translating items, if current field is untranslatable and current item version is approved
                     // NOTE: item itself is not include in associated translations, no need to check for it and skip itit
                     if (!$mval_query) {
                         $query = 'DELETE FROM #__flexicontent_fields_item_relations WHERE item_id IN (' . implode(',', $assoc_item_ids) . ') AND field_id=' . $field->id;
                         $this->_db->setQuery($query);
                         $this->_db->query();
                     } else {
                         $untranslatable_fields[] = $field->id;
                     }
                 }
             }
         }
         if (count($untranslatable_fields)) {
             $query = 'DELETE FROM #__flexicontent_fields_item_relations WHERE item_id IN (' . implode(',', $assoc_item_ids) . ') AND field_id IN (' . implode(',', $untranslatable_fields) . ')';
             $this->_db->setQuery($query);
             $this->_db->query();
         }
     }
     // *******************************************
     // Loop through Fields saving the field values
     // *******************************************
     if ($fields) {
         // Do not save if versioning disabled or item has no type (version 0)
         $record_versioned_data = $use_versioning && $item->version;
         $ver_query_vals = array();
         $rel_query_vals = array();
         foreach ($fields as $field) {
             // -- Add the new values to the database
             $postvalues = $this->formatToArray($postdata[$field->name]);
             //$qindex_values = $qindex[$field->name];
             $i = 1;
             foreach ($postvalues as $postvalue) {
                 // Create field obj for DB insertion
                 $obj = new stdClass();
                 $obj->field_id = $field->id;
                 $obj->item_id = $item->id;
                 $obj->valueorder = $i;
                 $obj->version = (int) $last_version + 1;
                 // Serialize the properties of the value, normally this is redudant, since the field must have had serialized the parameters of each value already
                 $obj->value = is_array($postvalue) ? serialize($postvalue) : $postvalue;
                 //$obj->qindex01 = isset($qindex_values['qindex01']) ? $qindex_values['qindex01'] : NULL;
                 //$obj->qindex02 = isset($qindex_values['qindex02']) ? $qindex_values['qindex02'] : NULL;
                 //$obj->qindex03 = isset($qindex_values['qindex03']) ? $qindex_values['qindex03'] : NULL;
                 // -- a. Add versioning values, but do not version the 'hits' or 'state' or 'voting' fields
                 if ($record_versioned_data && $field->field_type != 'hits' && $field->field_type != 'state' && $field->field_type != 'voting') {
                     // Insert only if value non-empty
                     if (isset($obj->value) && JString::strlen(trim($obj->value))) {
                         if (!$mval_query) {
                             $this->_db->insertObject('#__flexicontent_items_versions', $obj);
                         } else {
                             $ver_query_vals[] = "(" . $obj->field_id . "," . $obj->item_id . "," . $obj->valueorder . "," . $obj->version . "," . $this->_db->Quote($obj->value) . ")";
                         }
                     }
                 }
                 //echo $field->field_type." - ".$field->name." - ".JString::strlen(trim($obj->value))." ".$field->iscore."<br/>";
                 // -- b. If item is new OR version is approved, AND field is not core (aka stored in the content table or in special table), then add field value to field values table
                 if (($isnew || $data['vstate'] == 2) && !$field->iscore) {
                     // UNSET version it it used only verion data table, and insert only if value non-empty
                     unset($obj->version);
                     if (isset($obj->value) && JString::strlen(trim($obj->value))) {
                         if (!$mval_query) {
                             $this->_db->insertObject('#__flexicontent_fields_item_relations', $obj);
                         } else {
                             $rel_query_vals[] = "(" . $obj->field_id . "," . $obj->item_id . "," . $obj->valueorder . "," . $this->_db->Quote($obj->value) . ")";
                         }
                         // Save field value in all translating items, if current field is untranslatable
                         // NOTE: item itself is not include in associated translations, no need to check for it and skip it
                         if (count($assoc_item_ids) && $field->untranslatable) {
                             foreach ($assoc_item_ids as $t_item_id) {
                                 $obj->item_id = $t_item_id;
                                 if (!$mval_query) {
                                     $this->_db->insertObject('#__flexicontent_fields_item_relations', $obj);
                                 } else {
                                     $rel_query_vals[] = "(" . $obj->field_id . "," . $obj->item_id . "," . $obj->valueorder . "," . $this->_db->Quote($obj->value) . ")";
                                 }
                             }
                         }
                     }
                 }
                 $i++;
             }
         }
         // *********************************************
         // Insert values in item fields versioning table
         // *********************************************
         if (count($ver_query_vals)) {
             $query = "INSERT INTO #__flexicontent_items_versions " . " (field_id,item_id,valueorder,version,value" . ") VALUES " . "\n" . implode(",\n", $ver_query_vals);
             $this->_db->setQuery($query);
             $this->_db->query();
             if ($this->_db->getErrorNum()) {
                 JFactory::getApplication()->enqueueMessage(__FUNCTION__ . '(): SQL QUERY ERROR:<br/>' . nl2br($this->_db->getErrorMsg()), 'error');
             }
         }
         // *******************************************
         // Insert values in item fields relation table
         // *******************************************
         if (count($rel_query_vals)) {
             $query = "INSERT INTO #__flexicontent_fields_item_relations " . " (field_id,item_id,valueorder,value" . ") VALUES " . "\n" . implode(",\n", $rel_query_vals);
             $this->_db->setQuery($query);
             $this->_db->query();
             if ($this->_db->getErrorNum()) {
                 JFactory::getApplication()->enqueueMessage(__FUNCTION__ . '(): SQL QUERY ERROR:<br/>' . nl2br($this->_db->getErrorMsg()), 'error');
             }
         }
         // **************************************************************
         // Save other versioned item data into the field versioning table
         // **************************************************************
         // a. Save a version of item properties that do not have a corresponding CORE Field
         if ($record_versioned_data) {
             $obj = new stdClass();
             $obj->field_id = -2;
             // ID of Fake Field used to contain item properties not having a corresponding CORE field
             $obj->item_id = $item->id;
             $obj->valueorder = 1;
             $obj->version = (int) $last_version + 1;
             $item_data = array();
             $iproperties = array('alias', 'catid', 'metadesc', 'metakey', 'metadata', 'attribs');
             if (FLEXI_J16GE) {
                 $j16ge_iproperties = array('urls', 'images');
                 $iproperties = array_merge($iproperties, $j16ge_iproperties);
             }
             foreach ($iproperties as $iproperty) {
                 $item_data[$iproperty] = $item->{$iproperty};
             }
             $obj->value = serialize($item_data);
             $this->_db->insertObject('#__flexicontent_items_versions', $obj);
         }
         // b. Finally save a version of the posted JoomFish translated data for J1.5, if such data are editted inside the item edit form
         if (FLEXI_FISH && !empty($data['jfdata']) && $record_versioned_data) {
             $obj = new stdClass();
             $obj->field_id = -1;
             // ID of Fake Field used to contain the Joomfish translated item data
             $obj->item_id = $item->id;
             $obj->valueorder = 1;
             $obj->version = (int) $last_version + 1;
             $item_lang = substr($item->language, 0, 2);
             $data['jfdata'][$item_lang]['alias'] = $item->alias;
             $data['jfdata'][$item_lang]['metadesc'] = $item->metadesc;
             $data['jfdata'][$item_lang]['metakey'] = $item->metakey;
             $obj->value = serialize($data['jfdata']);
             $this->_db->insertObject('#__flexicontent_items_versions', $obj);
         }
     }
     if ($print_logging_info) {
         @($fc_run_times['fields_value_saving'] = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10);
     }
     // ******************************
     // Trigger onAfterSaveField Event
     // ******************************
     if ($fields) {
         if ($print_logging_info) {
             $start_microtime = microtime(true);
         }
         foreach ($fields as $field) {
             $fieldname = $field->iscore ? 'core' : $field->field_type;
             $result = FLEXIUtilities::call_FC_Field_Func($fieldname, 'onAfterSaveField', array(&$field, &$postdata[$field->name], &$files[$field->name], &$item));
             // *** $result is ignored
         }
         if ($print_logging_info) {
             @($fc_run_times['onAfterSaveField_event'] = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10);
         }
     }
     return true;
 }
    /**
     * Method to save field values of the item in field versioning DB table or in ..._fields_item_relations DB table 
     *
     * @access	public
     * @return	boolean	True on success
     * @since	1.0
     */
    function saveFields($isnew, &$item, &$data, &$files, &$old_item = null, &$core_data_via_events = null)
    {
        if (!$old_item) {
            $old_item =& $item;
        }
        $app = JFactory::getApplication();
        $user = JFactory::getUser();
        $dispatcher = JDispatcher::getInstance();
        $cparams = $this->_cparams;
        $use_versioning = $cparams->get('use_versioning', 1);
        $print_logging_info = $cparams->get('print_logging_info');
        $last_version = (int) FLEXIUtilities::getLastVersions($item->id, true);
        $mval_query = true;
        if ($print_logging_info) {
            global $fc_run_times;
        }
        if ($print_logging_info) {
            $start_microtime = microtime(true);
        }
        // ********************************
        // Checks for untranslatable fields
        // ********************************
        // CASE 1. Check if saving an item that translates an original content in site's default language
        // ... Decide whether to retrieve field values of untranslatable fields from the original content item
        $enable_translation_groups = flexicontent_db::useAssociations();
        //$cparams->get('enable_translation_groups');
        $site_default = substr(flexicontent_html::getSiteDefaultLang(), 0, 2);
        $is_content_default_lang = $site_default == substr($item->language, 0, 2);
        $get_untraslatable_values = $enable_translation_groups && !$is_content_default_lang;
        if ($enable_translation_groups) {
            $langAssocs = $this->getLangAssocs();
            //}
            //if ($enable_translation_groups /*&& $is_content_default_lang*/)
            //{
            // ... Get item ids of the associated items, so that we save into the untranslatable fields
            $_langAssocs = $langAssocs;
            unset($_langAssocs[$this->_id]);
            $assoc_item_ids = array_keys($_langAssocs);
        }
        if (empty($assoc_item_ids)) {
            $assoc_item_ids = array();
        }
        // ***************************************************************************************************************************
        // Get item's fields ... and their values (for untranslatable fields the field values from original content item are retrieved
        // ***************************************************************************************************************************
        $original_content_id = 0;
        if ($get_untraslatable_values) {
            foreach ($langAssocs as $content_id => $_assoc) {
                if ($site_default == substr($_assoc->language, 0, 2)) {
                    $original_content_id = $content_id;
                    break;
                }
            }
        }
        //JFactory::getApplication()->enqueueMessage(__FUNCTION__.'(): '.$original_content_id.' '.print_r($assoc_item_ids, true),'message');
        $fields = $this->getExtrafields($force = true, $original_content_id, $old_item);
        // ******************************************************************************************************************
        // Loop through Fields triggering onBeforeSaveField Event handlers, this was seperated from the rest of the process
        // to give chance to ALL fields to check their DATA and cancel item saving process before saving any new field values
        // ******************************************************************************************************************
        $searchindex = array();
        //$qindex = array();
        $core_data_via_events = array();
        // Extra validation for some core fields via onBeforeSaveField
        if ($fields) {
            $core_via_post = array('title' => 1, 'text' => 1);
            foreach ($fields as $field) {
                // Set vstate property into the field object to allow this to be changed be the before saving  field event handler
                $field->item_vstate = $data['vstate'];
                $is_editable = !$field->valueseditable || $user->authorise('flexicontent.editfieldvalues', 'com_flexicontent.field.' . $field->id);
                $maintain_dbval = false;
                // FORM HIDDEN FIELDS (FRONTEND/BACKEND) AND (ACL) UNEDITABLE FIELDS: maintain their DB value ...
                if ($app->isSite() && ($field->formhidden == 1 || $field->formhidden == 3 || $field->parameters->get('frontend_hidden')) || $app->isAdmin() && ($field->formhidden == 2 || $field->formhidden == 3 || $field->parameters->get('backend_hidden')) || !$is_editable) {
                    $postdata[$field->name] = $field->value;
                    $maintain_dbval = true;
                    // UNTRANSLATABLE (CUSTOM) FIELDS: maintain their DB value ...
                    /*} else if ( $get_untraslatable_values && $field->untranslatable ) {
                    		$postdata[$field->name] = $field->value;
                    		$maintain_dbval = true;*/
                } else {
                    if ($field->iscore) {
                        // (posted) CORE FIELDS: if not set maintain their DB value ...
                        if (isset($core_via_post[$field->name])) {
                            if (isset($data[$field->name])) {
                                $postdata[$field->name] = $data[$field->name];
                            } else {
                                $postdata[$field->name] = $field->value;
                                $maintain_dbval = true;
                            }
                            // (not posted) CORE FIELDS: get current value
                        } else {
                            // Get value from the updated item instead of old data
                            $postdata[$field->name] = $this->getCoreFieldValue($field, 0);
                        }
                        // OTHER CUSTOM FIELDS (not hidden and not untranslatable)
                    } else {
                        $postdata[$field->name] = @$data['custom'][$field->name];
                    }
                }
                // Unserialize values already serialized values, e.g. (a) if current values used are from DB or (b) are being imported from CSV file
                if (!is_array($postdata[$field->name])) {
                    $postdata[$field->name] = strlen($postdata[$field->name]) ? array($postdata[$field->name]) : array();
                }
                foreach ($postdata[$field->name] as $i => $postdata_val) {
                    if (@unserialize($postdata_val) !== false || $postdata_val === 'b:0;') {
                        $postdata[$field->name][$i] = unserialize($postdata_val);
                    }
                }
                // Trigger plugin Event 'onBeforeSaveField'
                if (!$field->iscore || isset($core_via_post[$field->name])) {
                    $field_type = $field->iscore ? 'core' : $field->field_type;
                    $result = FLEXIUtilities::call_FC_Field_Func($field_type, 'onBeforeSaveField', array(&$field, &$postdata[$field->name], &$files[$field->name], &$item));
                    if ($result === false) {
                        // Field requested to abort item saving
                        $this->setError(JText::sprintf('FLEXI_FIELD_VALUE_IS_INVALID', $field->label));
                        return 'abort';
                    }
                    // For CORE field get the modified data, which will be used for storing in DB (these will be re-bind later)
                    if (isset($core_via_post[$field->name])) {
                        $core_data_via_events[$field->name] = isset($postdata[$field->name][0]) ? $postdata[$field->name][0] : '';
                        // The validation may have skipped it !!
                    }
                } else {
                    // Currently other CORE fields, these are skipped we will not call onBeforeSaveField() on them, neither rebind them
                }
                //$qindex[$field->name] = NULL;
                //$result = FLEXIUtilities::call_FC_Field_Func($field_type, 'onBeforeSaveField', array( &$field, &$postdata[$field->name], &$files[$field->name], &$item, &$qindex[$field->name] ));
                //if ($result===false) { ... }
                // Get vstate property from the field object back to the data array ... in case it was modified, since some field may decide to prevent approval !
                $data['vstate'] = $field->item_vstate;
            }
            //echo "<pre>"; print_r($postdata); echo "</pre>"; exit;
        }
        if ($print_logging_info) {
            @($fc_run_times['fields_value_preparation'] = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10);
        }
        // **********************
        // Empty per field TABLES
        // **********************
        $filterables = FlexicontentFields::getSearchFields('id', $indexer = 'advanced', null, null, $_load_params = true, 0, $search_type = 'filter');
        $filterables = array_keys($filterables);
        $filterables = array_flip($filterables);
        $tbl_prefix = $app->getCfg('dbprefix') . 'flexicontent_advsearch_index_field_';
        $query = "SELECT TABLE_NAME\n\t\t\tFROM INFORMATION_SCHEMA.TABLES\n\t\t\tWHERE TABLE_NAME LIKE '" . $tbl_prefix . "%'\n\t\t\t";
        $this->_db->setQuery($query);
        $tbl_names = $this->_db->loadColumn();
        foreach ($tbl_names as $tbl_name) {
            $_field_id = str_replace($tbl_prefix, '', $tbl_name);
            // Drop the table of no longer filterable field
            if (!isset($filterables[$_field_id])) {
                $this->_db->setQuery('DROP TABLE IF EXISTS ' . $tbl_name);
            } else {
                // Remove item's old advanced search index entries
                $query = "DELETE FROM " . $tbl_name . " WHERE item_id=" . $item->id;
                $this->_db->setQuery($query);
                $this->_db->query();
            }
        }
        // VERIFY all search tables exist
        $tbl_names_flipped = array_flip($tbl_names);
        foreach ($filterables as $_field_id => $_ignored) {
            $tbl_name = $app->getCfg('dbprefix') . 'flexicontent_advsearch_index_field_' . $_field_id;
            if (isset($tbl_names_flipped[$tbl_name])) {
                continue;
            }
            $query = '
			CREATE TABLE IF NOT EXISTS `' . $tbl_name . '` (
			  `sid` int(11) NOT NULL auto_increment,
			  `field_id` int(11) NOT NULL,
			  `item_id` int(11) NOT NULL,
			  `extraid` int(11) NOT NULL,
			  `search_index` longtext NOT NULL,
			  `value_id` varchar(255) NULL,
			  PRIMARY KEY (`field_id`,`item_id`,`extraid`),
			  KEY `sid` (`sid`),
			  KEY `field_id` (`field_id`),
			  KEY `item_id` (`item_id`),
			  FULLTEXT `search_index` (`search_index`),
			  KEY `value_id` (`value_id`)
			) ENGINE=MyISAM CHARACTER SET `utf8` COLLATE `utf8_general_ci`
			';
            $this->_db->setQuery($query);
            $this->_db->query();
        }
        // ****************************************************************************************************************************
        // Loop through Fields triggering onIndexAdvSearch, onIndexSearch Event handlers, this was seperated from the before save field
        //  event, so that we will update search indexes only if the above has not canceled saving OR has not canceled version approval
        // ****************************************************************************************************************************
        if ($print_logging_info) {
            $start_microtime = microtime(true);
        }
        $ai_query_vals = array();
        $ai_query_vals_f = array();
        if ($fields) {
            foreach ($fields as $field) {
                $field_type = $field->iscore ? 'core' : $field->field_type;
                if ($data['vstate'] == 2 || $isnew) {
                    // Trigger plugin Event 'onIndexAdvSearch' to update field-item pair records in advanced search index
                    FLEXIUtilities::call_FC_Field_Func($field_type, 'onIndexAdvSearch', array(&$field, &$postdata[$field->name], &$item));
                    if (isset($field->ai_query_vals)) {
                        foreach ($field->ai_query_vals as $query_val) {
                            $ai_query_vals[] = $query_val;
                        }
                        if (isset($filterables[$field->id])) {
                            // Current for advanced index only
                            foreach ($field->ai_query_vals as $query_val) {
                                $ai_query_vals_f[$field->id][] = $query_val;
                            }
                        }
                    }
                    //echo $field->name .":".implode(",", @$field->ai_query_vals ? $field->ai_query_vals : array() )."<br/>";
                    // Trigger plugin Event 'onIndexSearch' to update item 's (basic) search index record
                    FLEXIUtilities::call_FC_Field_Func($field_type, 'onIndexSearch', array(&$field, &$postdata[$field->name], &$item));
                    if (strlen(@$field->search[$item->id])) {
                        $searchindex[] = $field->search[$item->id];
                    }
                    //echo $field->name .":".@$field->search[$item->id]."<br/>";
                }
            }
        }
        // Remove item's old advanced search index entries
        $query = "DELETE FROM #__flexicontent_advsearch_index WHERE item_id=" . $item->id;
        $this->_db->setQuery($query);
        $this->_db->query();
        // Store item's advanced search index entries
        $queries = array();
        if (count($ai_query_vals)) {
            $queries[] = "INSERT INTO #__flexicontent_advsearch_index " . " (field_id,item_id,extraid,search_index,value_id) VALUES " . implode(",", $ai_query_vals);
            $this->_db->setQuery($query);
            $this->_db->query();
            if ($this->_db->getErrorNum()) {
                JFactory::getApplication()->enqueueMessage(__FUNCTION__ . '(): SQL QUERY ERROR:<br/>' . nl2br($this->_db->getErrorMsg()), 'error');
            }
        }
        foreach ($ai_query_vals_f as $_field_id => $_query_vals) {
            // Current for advanced index only
            $queries[] = "INSERT INTO #__flexicontent_advsearch_index_field_" . $_field_id . " (field_id,item_id,extraid,search_index,value_id) VALUES " . implode(",", $_query_vals);
        }
        foreach ($queries as $query) {
            $this->_db->setQuery($query);
            $this->_db->query();
        }
        // Assigned created basic search index into item object
        $search_prefix = $cparams->get('add_search_prefix') ? 'vvv' : '';
        // SEARCH WORD Prefix
        $item->search_index = implode(' | ', $searchindex);
        if ($search_prefix && $item->search_index) {
            $item->search_index = preg_replace('/(\\b[^\\s]+\\b)/', $search_prefix . '$0', trim($item->search_index));
        }
        // Check if vstate was set to 1 (no approve new version) while versioning is disabled
        if (!$use_versioning && $data['vstate'] != 2) {
            $data['vstate'] = 2;
            $app->enqueueMessage('vstate cannot be set to 1 (=no approve new version) when versioning is disabled', 'notice');
        }
        if ($print_logging_info) {
            @($fc_run_times['fields_value_indexing'] = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10);
        }
        if ($print_logging_info) {
            $start_microtime = microtime(true);
        }
        // **************************************************************************
        // IF new version is approved, remove old version values from the field table
        // **************************************************************************
        if ($data['vstate'] == 2) {
            //echo "delete __flexicontent_fields_item_relations, item_id: " .$item->id;
            $query = 'DELETE FROM #__flexicontent_fields_item_relations WHERE item_id = ' . $item->id;
            $this->_db->setQuery($query);
            $this->_db->query();
            $query = 'DELETE FROM #__flexicontent_items_versions WHERE item_id=' . $item->id . ' AND version=' . ((int) $last_version + 1);
            $this->_db->setQuery($query);
            $this->_db->query();
            $untranslatable_fields = array();
            if ($fields) {
                foreach ($fields as $field) {
                    if ($field->iscore) {
                        continue;
                    }
                    if (count($assoc_item_ids) && $field->untranslatable) {
                        // Delete field values in all translating items, if current field is untranslatable and current item version is approved
                        // NOTE: item itself is not include in associated translations, no need to check for it and skip itit
                        if (!$mval_query) {
                            $query = 'DELETE FROM #__flexicontent_fields_item_relations WHERE item_id IN (' . implode(',', $assoc_item_ids) . ') AND field_id=' . $field->id;
                            $this->_db->setQuery($query);
                            $this->_db->query();
                        } else {
                            $untranslatable_fields[] = $field->id;
                        }
                    }
                }
            }
            if (count($untranslatable_fields)) {
                $query = 'DELETE FROM #__flexicontent_fields_item_relations WHERE item_id IN (' . implode(',', $assoc_item_ids) . ') AND field_id IN (' . implode(',', $untranslatable_fields) . ')';
                $this->_db->setQuery($query);
                $this->_db->query();
            }
        }
        // *******************************************
        // Loop through Fields saving the field values
        // *******************************************
        if ($fields) {
            // Do not save if versioning disabled or item has no type (version 0)
            $record_versioned_data = $use_versioning && $item->version;
            $ver_query_vals = array();
            $rel_query_vals = array();
            foreach ($fields as $field) {
                // -- Add the new values to the database
                $postvalues = $this->formatToArray($postdata[$field->name]);
                //$qindex_values = $qindex[$field->name];
                $i = 1;
                foreach ($postvalues as $postvalue) {
                    // Create field obj for DB insertion
                    $obj = new stdClass();
                    $obj->field_id = $field->id;
                    $obj->item_id = $item->id;
                    $obj->valueorder = $i;
                    $obj->suborder = 1;
                    $obj->version = (int) $last_version + 1;
                    $use_ingroup = $field->parameters->get('use_ingroup', 0);
                    // Serialize the properties of the value, normally this is redudant, since the field must have had serialized the parameters of each value already
                    if (!empty($field->use_suborder) && is_array($postvalue)) {
                        $obj->value = null;
                    } else {
                        $obj->value = is_array($postvalue) ? serialize($postvalue) : $postvalue;
                    }
                    //$obj->qindex01 = isset($qindex_values['qindex01']) ? $qindex_values['qindex01'] : NULL;
                    //$obj->qindex02 = isset($qindex_values['qindex02']) ? $qindex_values['qindex02'] : NULL;
                    //$obj->qindex03 = isset($qindex_values['qindex03']) ? $qindex_values['qindex03'] : NULL;
                    // -- a. Add versioning values, but do not version the 'hits' or 'state' or 'voting' fields
                    if ($record_versioned_data && $field->field_type != 'hits' && $field->field_type != 'state' && $field->field_type != 'voting') {
                        // Insert only if value non-empty
                        if (!empty($field->use_suborder) && is_array($postvalue)) {
                            $obj->suborder = 1;
                            foreach ($postvalue as $v) {
                                $obj->value = $v;
                                if (!$mval_query) {
                                    $this->_db->insertObject('#__flexicontent_items_versions', $obj);
                                } else {
                                    $ver_query_vals[] = "(" . $obj->field_id . "," . $obj->item_id . "," . $obj->valueorder . "," . $obj->suborder . "," . $obj->version . "," . $this->_db->Quote($obj->value) . ")";
                                }
                                $obj->suborder++;
                            }
                            unset($v);
                        } else {
                            if (isset($obj->value) && ($use_ingroup || strlen(trim($obj->value)))) {
                                if (!$mval_query) {
                                    $this->_db->insertObject('#__flexicontent_items_versions', $obj);
                                } else {
                                    $ver_query_vals[] = "(" . $obj->field_id . "," . $obj->item_id . "," . $obj->valueorder . "," . $obj->suborder . "," . $obj->version . "," . $this->_db->Quote($obj->value) . ")";
                                }
                            }
                        }
                    }
                    //echo $field->field_type." - ".$field->name." - ".strlen(trim($obj->value))." ".$field->iscore."<br/>";
                    // -- b. If item is new OR version is approved, AND field is not core (aka stored in the content table or in special table), then add field value to field values table
                    if (($isnew || $data['vstate'] == 2) && !$field->iscore) {
                        // UNSET version it it used only verion data table, and insert only if value non-empty
                        unset($obj->version);
                        if (!empty($field->use_suborder) && is_array($postvalue)) {
                            $obj->suborder = 1;
                            foreach ($postvalue as $v) {
                                $obj->value = $v;
                                if (!$mval_query) {
                                    $this->_db->insertObject('#__flexicontent_fields_item_relations', $obj);
                                } else {
                                    $rel_query_vals[] = "(" . $obj->field_id . "," . $obj->item_id . "," . $obj->valueorder . "," . $obj->suborder . "," . $this->_db->Quote($obj->value) . ")";
                                }
                                $obj->suborder++;
                            }
                            unset($v);
                        } else {
                            if (isset($obj->value) && ($use_ingroup || strlen(trim($obj->value)))) {
                                if (!$mval_query) {
                                    $this->_db->insertObject('#__flexicontent_fields_item_relations', $obj);
                                } else {
                                    $rel_query_vals[] = "(" . $obj->field_id . "," . $obj->item_id . "," . $obj->valueorder . "," . $obj->suborder . "," . $this->_db->Quote($obj->value) . ")";
                                }
                                // Save field value in all translating items, if current field is untranslatable
                                // NOTE: item itself is not include in associated translations, no need to check for it and skip it
                                if (count($assoc_item_ids) && $field->untranslatable) {
                                    foreach ($assoc_item_ids as $t_item_id) {
                                        //echo "setting Untranslatable value for item_id: ".$t_item_id ." field_id: ".$field->id."<br/>";
                                        $obj->item_id = $t_item_id;
                                        if (!$mval_query) {
                                            $this->_db->insertObject('#__flexicontent_fields_item_relations', $obj);
                                        } else {
                                            $rel_query_vals[] = "(" . $obj->field_id . "," . $obj->item_id . "," . $obj->valueorder . "," . $obj->suborder . "," . $this->_db->Quote($obj->value) . ")";
                                        }
                                    }
                                }
                            }
                        }
                    }
                    $i++;
                }
            }
            // *********************************************
            // Insert values in item fields versioning table
            // *********************************************
            if (count($ver_query_vals)) {
                $query = "INSERT INTO #__flexicontent_items_versions " . " (field_id,item_id,valueorder,suborder,version,value" . ") VALUES " . "\n" . implode(",\n", $ver_query_vals);
                $this->_db->setQuery($query);
                $this->_db->query();
                if ($this->_db->getErrorNum()) {
                    JFactory::getApplication()->enqueueMessage(__FUNCTION__ . '(): SQL QUERY ERROR:<br/>' . nl2br($this->_db->getErrorMsg()), 'error');
                }
            }
            // *******************************************
            // Insert values in item fields relation table
            // *******************************************
            if (count($rel_query_vals)) {
                $query = "INSERT INTO #__flexicontent_fields_item_relations " . " (field_id,item_id,valueorder,suborder,value" . ") VALUES " . "\n" . implode(",\n", $rel_query_vals);
                $this->_db->setQuery($query);
                $this->_db->query();
                if ($this->_db->getErrorNum()) {
                    JFactory::getApplication()->enqueueMessage(__FUNCTION__ . '(): SQL QUERY ERROR:<br/>' . nl2br($this->_db->getErrorMsg()), 'error');
                }
            }
            // **************************************************************
            // Save other versioned item data into the field versioning table
            // **************************************************************
            // a. Save a version of item properties that do not have a corresponding CORE Field
            if ($record_versioned_data) {
                $obj = new stdClass();
                $obj->field_id = -2;
                // ID of Fake Field used to contain item properties not having a corresponding CORE field
                $obj->item_id = $item->id;
                $obj->valueorder = 1;
                $obj->suborder = 1;
                $obj->version = (int) $last_version + 1;
                $item_data = array();
                $iproperties = array('alias', 'catid', 'metadesc', 'metakey', 'metadata', 'attribs', 'urls', 'images');
                foreach ($iproperties as $iproperty) {
                    $item_data[$iproperty] = $item->{$iproperty};
                }
                $obj->value = serialize($item_data);
                $this->_db->insertObject('#__flexicontent_items_versions', $obj);
            }
            // b. Finally save a version of the posted JoomFish translated data for J1.5, if such data are editted inside the item edit form
            /*if ( FLEXI_FISH && !empty($data['jfdata']) && $record_versioned_data )
            		{
            			$obj = new stdClass();
            			$obj->field_id 		= -1;  // ID of Fake Field used to contain the Joomfish translated item data
            			$obj->item_id 		= $item->id;
            			$obj->valueorder	= 1;
            			$obj->suborder    = 1;
            			$obj->version			= (int)$last_version+1;
            			
            			$item_lang = substr($item->language ,0,2);
            			$data['jfdata'][$item_lang]['title'] = $item->title;
            			$data['jfdata'][$item_lang]['alias'] = $item->alias;
            			$data['jfdata'][$item_lang]['text'] = $item->text;
            			$data['jfdata'][$item_lang]['metadesc'] = $item->metadesc;
            			$data['jfdata'][$item_lang]['metakey'] = $item->metakey;
            			$obj->value = serialize($data['jfdata']);
            			$this->_db->insertObject('#__flexicontent_items_versions', $obj);
            		}*/
        }
        if ($print_logging_info) {
            @($fc_run_times['fields_value_saving'] = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10);
        }
        // ******************************
        // Trigger onAfterSaveField Event
        // ******************************
        if ($fields) {
            if ($print_logging_info) {
                $start_microtime = microtime(true);
            }
            foreach ($fields as $field) {
                $field_type = $field->iscore ? 'core' : $field->field_type;
                $result = FLEXIUtilities::call_FC_Field_Func($field_type, 'onAfterSaveField', array(&$field, &$postdata[$field->name], &$files[$field->name], &$item));
                // *** $result is ignored
            }
            if ($print_logging_info) {
                @($fc_run_times['onAfterSaveField_event'] = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10);
            }
        }
        return true;
    }
	/**
	 * Logic to save an item
	 *
	 * @access public
	 * @return void
	 * @since 1.0
	 */
	function save()
	{
		// Check for request forgeries
		JRequest::checkToken() or jexit( 'Invalid Token' );
		
		// Initialize variables
		$app     = JFactory::getApplication();
		$db      = JFactory::getDBO();
		$user    = JFactory::getUser();
		$menu    = $app->getMenu()->getActive();
		$config  = JFactory::getConfig();
		$session = JFactory::getSession();
		$task	   = JRequest::getVar('task');
		$model   = $this->getModel(FLEXI_ITEMVIEW);
		$isnew   = !$model->getId();
		$ctrl_task = FLEXI_J16GE ? 'task=items.' : 'controller=items&task=';
		
		$fc_params  = JComponentHelper::getParams( 'com_flexicontent' );
		$dolog      = $fc_params->get('print_logging_info');
		
		// Get the COMPONENT only parameters
		$comp_params = JComponentHelper::getComponent('com_flexicontent')->params;
		$params = FLEXI_J16GE ? clone ($comp_params) : new JParameter( $comp_params ); // clone( JComponentHelper::getParams('com_flexicontent') );
		
		// Merge the type parameters
		$tparams = $model->getTypeparams();
		$tparams = FLEXI_J16GE ? new JRegistry($tparams) : new JParameter($tparams);
		$params->merge($tparams);
		
		// Merge the menu parameters
		if ($menu) {
			$menu_params = FLEXI_J16GE ? $menu->params : new JParameter($menu->params);
			$params->merge($menu_params);
		}
		
		// Get needed parameters
		$submit_redirect_url_fe = $params->get('submit_redirect_url_fe', '');
		$allowunauthorize       = $params->get('allowunauthorize', 0);
		
		
		
		// *********************
		// Get data from request
		// *********************
		
		if (FLEXI_J16GE) {
			// Retrieve form data these are subject to basic filtering
			$data   = JRequest::getVar('jform', array(), 'post', 'array');   // Core Fields and and item Parameters
			$custom = JRequest::getVar('custom', array(), 'post', 'array');  // Custom Fields
			$jfdata = JRequest::getVar('jfdata', array(), 'post', 'array');  // Joomfish Data
			if ( ! @ $data['rules'] ) $data['rules'] = array();
		}
		
		else {
			// Retrieve form data these are subject to basic filtering
			$data = JRequest::get( 'post' );  // Core & Custom Fields and item Parameters
		}
		
		// Set data id into model in case not already set ?
		$model->setId((int) $data['id']);
		
		
		
		// *************************************
		// ENFORCE can change category ACL perms
		// *************************************
		
		$perms = FlexicontentHelperPerm::getPerm();
		// Per content type change category permissions
		if (FLEXI_J16GE) {
			$current_type_id  = ($isnew || !$model->get('type_id')) ? $data['type_id'] : $model->get('type_id');  // GET current (existing/old) item TYPE ID
			$CanChangeFeatCat = $user->authorise('flexicontent.change.cat.feat', 'com_flexicontent.type.' . $current_type_id);
			$CanChangeSecCat  = $user->authorise('flexicontent.change.cat.sec', 'com_flexicontent.type.' . $current_type_id);
			$CanChangeCat     = $user->authorise('flexicontent.change.cat', 'com_flexicontent.type.' . $current_type_id);
		} else {
			$CanChangeFeatCat = 1;
			$CanChangeSecCat  = 1;
			$CanChangeCat     = 1;
		}
		
		$featured_cats_parent = $params->get('featured_cats_parent', 0);
		$featured_cats = array();
		
		$enable_featured_cid_selector = $perms->MultiCat && $CanChangeFeatCat;
		$enable_cid_selector   = $perms->MultiCat && $CanChangeSecCat;
		$enable_catid_selector = ($isnew && !$tparams->get('catid_default')) || (!$isnew && !$model->get('catid')) || $CanChangeCat;
		
		// Enforce maintaining featured categories
		$featured_cats_parent = $params->get('featured_cats_parent', 0);
		$featured_cats = array();
		if ( $featured_cats_parent && !$enable_featured_cid_selector )
		{
			$featured_tree = flexicontent_cats::getCategoriesTree($published_only=1, $parent_id=$featured_cats_parent, $depth_limit=0);
			$featured_cid = array();
			if (!$isnew) {
				foreach($model->get('categories') as $item_cat) if (isset($featured_tree[$item_cat])) $featured_cid[] = $item_cat;
			}
			$data['featured_cid'] = $featured_cid;
		}
		
		// Enforce maintaining secondary categories
		if (!$enable_cid_selector) {
			if ($isnew) {
				$data['cid'] = $tparams->get('cid_default');
			} else if ( isset($featured_cid) ) {
				$featured_cid_arr = array_flip($featured_cid);
				$sec_cid = array();
				foreach($model->get('cats') as $item_cat) if (!isset($featured_cid_arr[$item_cat])) $sec_cid[] = $item_cat;
				$data['cid'] = $sec_cid;
			} else {
				$data['cid'] = $model->get('cats');
			}
		}
		
		if (!$enable_catid_selector) {
			if ($isnew && $tparams->get('catid_default'))
				$data['catid'] = $tparams->get('catid_default');
			else if ($model->get('catid'))
				$data['catid'] = $model->get('catid');
		}
		
		
		
		// **************************
		// Basic Form data validation
		// **************************
		
		if (FLEXI_J16GE)
		{
			// *** MANUALLY CHECK CAPTCHA ***
			$use_captcha    = $params->get('use_captcha', 1);     // 1 for guests, 2 for any user
			$captcha_formop = $params->get('captcha_formop', 0);  // 0 for submit, 1 for submit/edit (aka always)
			$is_submitop = ((int) $data['id']) == 0;
			$display_captcha = $use_captcha >= 2 || ( $use_captcha == 1 &&  $user->guest );
			$display_captcha = $display_captcha && ( $is_submitop || $captcha_formop);  // for submit operation we do not need to check 'captcha_formop' ...
			if ($display_captcha)
			{
				// Try to force the use of recaptcha plugin
				JFactory::getConfig()->set('captcha', 'recaptcha');
				
				if ( $app->getCfg('captcha') == 'recaptcha' && JPluginHelper::isEnabled('captcha', 'recaptcha') ) {
					JPluginHelper::importPlugin('captcha');
					$dispatcher = JDispatcher::getInstance();
					$result = $dispatcher->trigger('onCheckAnswer', JRequest::getString('recaptcha_response_field'));
					if (!$result[0]) {
						$errmsg  = JText::_('FLEXI_CAPTCHA_FAILED');
						$errmsg .= ' '.JText::_('FLEXI_MUST_REFILL_SOME_FIELDS');
						echo "<script>alert('".$errmsg."');";
						echo "window.history.back();";
						echo "</script>";
						jexit();
					}
				}
			}
			
			// Validate Form data for core fields and for parameters
			$form = $model->getForm();          // Do not pass any data we only want the form object in order to validate the data and not create a filled-in form
			$post = $model->validate($form, $data);
			
			// Check for validation error
			if (!$post) {
				// Get the validation messages.
				$errors	= $form->getErrors();
	
				// Push up to three validation messages out to the user.
				for ($i = 0, $n = count($errors); $i < $n && $i < 3; $i++) {
					if ($errors[$i] instanceof Exception)
						$app->enqueueMessage($errors[$i]->getMessage(), 'notice');
					else
						$app->enqueueMessage($errors[$i], 'notice');
				}
	
				// Save the jform data in the session.
				$app->setUserState($form->option.'.edit.'.$form->context.'.data', $data);
				// Save the custom fields data in the session.
				$app->setUserState($form->option.'.edit.'.$form->context.'.custom', $custom);
				
				// Redirect back to the registration form.
				$this->setRedirect( $_SERVER['HTTP_REFERER'] );
				return false;
				//die('error');
			}
			
			/*if (!$post) {
				//JError::raiseWarning( 500, "Error while validating data: " . $model->getError() );
				echo "Error while validating data: " . $model->getError();
				echo '<span class="fc_return_msg">'.JText::sprintf('FLEXI_CLICK_HERE_TO_RETURN', '"JavaScript:window.history.back();"').'</span>';
				jexit();
			}*/
			
			// Some values need to be assigned after validation
			$post['attribs'] = @$data['attribs'];  // Workaround for item's template parameters being clear by validation since they are not present in item.xml
			$post['custom']  = & $custom;          // Assign array of custom field values, they are in the 'custom' form array instead of jform
			$post['jfdata']  = & $jfdata;          // Assign array of Joomfish field values, they are in the 'jfdata' form array instead of jform
			
			// Assign template parameters of the select ilayout as an sub-array (the DB model will handle the merging of parameters)
			$ilayout = @ $data['attribs']['ilayout'];  // normal not be set if frontend template editing is not shown
			if( $ilayout && !empty($data['layouts'][$ilayout]) )   $post['attribs']['layouts'] = $data['layouts'];
			//echo "<pre>"; print_r($post['attribs']); exit;
		}
		
		else {
			$post = $data;
			
			// Some values need to be assigned after validation
			$post['text'] = JRequest::getVar( 'text', '', 'post', 'string', JREQUEST_ALLOWRAW ); // Workaround for allowing raw text field
			
			// Assign template parameters of the select ilayout as an sub-array (the DB model will handle the merging of parameters)
			$ilayout = @ $post['params']['ilayout'];  // normal not be set if frontend template editing is not shown
			if( $ilayout && !empty($post['layouts'][$ilayout]) )  $post['params']['layouts'] = $post['layouts'];
			//echo "<pre>"; print_r($post['params']); exit;
			
		}
		
		// USEFULL FOR DEBUGING for J2.5 (do not remove commented code)
		//$diff_arr = array_diff_assoc ( $data, $post);
		//echo "<pre>"; print_r($diff_arr); jexit();
		
		
		// ********************************************************************************
		// PERFORM ACCESS CHECKS, NOTE: we need to check access again, despite having
		// checked them on edit form load, because user may have tampered with the form ... 
		// ********************************************************************************
		
		$type_id = (int) @ $post['type_id'];  // Typecast to int, (already done for J2.5 via validating)
		if ( !$isnew && $model->get('type_id') == $type_id ) {
			// Existing item with Type not being ALTERED, content type can be maintained regardless of privilege
			$canCreateType = true;
		} else {
			// New item or existing item with Type is being ALTERED, check privilege to create items of this type
			$canCreateType = $model->canCreateType( array($type_id), true, $types );
		}
		
		
		// ****************************************************************
		// Calculate user's privileges on current content item
		// ... canPublish IS RECALCULATED after saving, maybe comment out ?
		// ****************************************************************
		
		if (!$isnew) {
			
			if (FLEXI_J16GE) {
				$asset = 'com_content.article.' . $model->get('id');
				$canPublish = $user->authorise('core.edit.state', $asset) || ($user->authorise('core.edit.state.own', $asset) && $model->get('created_by') == $user->get('id'));
				$canEdit = $user->authorise('core.edit', $asset) || ($user->authorise('core.edit.own', $asset) && $model->get('created_by') == $user->get('id'));
				// ALTERNATIVE 1
				//$canEdit = $model->getItemAccess()->get('access-edit'); // includes privileges edit and edit-own
				// ALTERNATIVE 2
				//$rights = FlexicontentHelperPerm::checkAllItemAccess($user->get('id'), 'item', $model->get('id'));
				//$canEdit = in_array('edit', $rights) || (in_array('edit.own', $rights) && $model->get('created_by') == $user->get('id')) ;
			} else if ($user->gid >= 25) {
				$canPublish = true;
				$canEdit = true;
			} else if (FLEXI_ACCESS) {
				$rights 	= FAccess::checkAllItemAccess('com_content', 'users', $user->gmid, $model->get('id'), $model->get('catid'));
				$canPublish = in_array('publish', $rights) || (in_array('publishown', $rights) && $model->get('created_by') == $user->get('id')) ;
				$canEdit = in_array('edit', $rights) || (in_array('editown', $rights) && $model->get('created_by') == $user->get('id')) ;
			} else {
				$canPublish = $user->authorize('com_content', 'publish', 'content', 'all');
				$canEdit = $user->authorize('com_content', 'edit', 'content', 'all') || ($user->authorize('com_content', 'edit', 'content', 'own') && $model->get('created_by') == $user->get('id'));
				//$canPublish = ($user->gid >= 21);  // At least J1.5 Publisher
				//$canEdit = ($user->gid >= 20);  // At least J1.5 Editor
			}
			
			if ( !$canEdit ) {
				// No edit privilege, check if item is editable till logoff
				if ($session->has('rendered_uneditable', 'flexicontent')) {
					$rendered_uneditable = $session->get('rendered_uneditable', array(),'flexicontent');
					$canEdit = isset($rendered_uneditable[$model->get('id')]) && $rendered_uneditable[$model->get('id')];
				}
			}

		} else {
			
			if (FLEXI_J16GE) {
				$canAdd = $model->getItemAccess()->get('access-create'); // includes check of creating in at least one category
				$not_authorised = !$canAdd;
				
				$canPublish	= $user->authorise('core.edit.state', 'com_flexicontent') || $user->authorise('core.edit.state.own', 'com_flexicontent');
			} else if ($user->gid >= 25) {
				$canAdd = 1;
			} else if (FLEXI_ACCESS) {
				$canAdd = FAccess::checkUserElementsAccess($user->gmid, 'submit');
				$canAdd = @$canAdd['content'] || @$canAdd['category'];
				
				$canPublishAll 		= FAccess::checkAllContentAccess('com_content','publish','users',$user->gmid,'content','all');
				$canPublishOwnAll	= FAccess::checkAllContentAccess('com_content','publishown','users',$user->gmid,'content','all');
				$canPublish	= ($user->gid < 25) ? $canPublishAll || $canPublishOwnAll : 1;
			} else {
				$canAdd	= $user->authorize('com_content', 'add', 'content', 'all');
				//$canAdd = ($user->gid >= 19);  // At least J1.5 Author
				$not_authorised = ! $canAdd;
				$canPublish	= ($user->gid >= 21);
			}
			
			if ( $allowunauthorize ) {
				$canAdd = true;
				$canCreateType = true;
			}
		}
		
		// ... we use some strings from administrator part
		// load english language file for 'com_flexicontent' component then override with current language file
		JFactory::getLanguage()->load('com_flexicontent', JPATH_ADMINISTRATOR, 'en-GB', true);
		JFactory::getLanguage()->load('com_flexicontent', JPATH_ADMINISTRATOR, null, true);
		
		// Check for new content
		if ( ($isnew && !$canAdd) || (!$isnew && !$canEdit)) {
			$msg = JText::_( 'FLEXI_ALERTNOTAUTH' );
			if (FLEXI_J16GE) throw new Exception($msg, 403); else JError::raiseError(403, $msg);
		}
		
		if ( !$canCreateType ) {
			$msg = isset($types[$type_id]) ?
				JText::sprintf( 'FLEXI_NO_ACCESS_CREATE_CONTENT_OF_TYPE', JText::_($types[$type_id]->name) ) :
				' Content Type '.$type_id.' was not found OR is not published';
			if (FLEXI_J16GE) throw new Exception($msg, 403); else JError::raiseError(403, $msg);
			return;
		}
		
		// Get "BEFORE SAVE" categories for information mail
		$before_cats = array();
		if ( !$isnew )
		{
			$query 	= 'SELECT DISTINCT c.id, c.title FROM #__categories AS c'
				. ' JOIN #__flexicontent_cats_item_relations AS rel ON rel.catid = c.id'
				. ' WHERE rel.itemid = '.(int) $model->get('id');
			$db->setQuery( $query );
			$before_cats = $db->loadObjectList('id');
			$before_maincat = $model->get('catid');
			$original_item = $model->getItem($post['id'], $check_view_access=false, $no_cache=true, $force_version=0);
		}
		
		
		// ****************************************
		// Try to store the form data into the item
		// ****************************************
		if ( ! $model->store($post) )
		{
			// Set error message about saving failed, and also the reason (=model's error message)
			$msg = JText::_( 'FLEXI_ERROR_STORING_ITEM' );
			JError::raiseWarning( 500, $msg .": " . $model->getError() );

			// Since an error occured, check if (a) the item is new and (b) was not created
			if ($isnew && !$model->get('id')) {
				$msg = '';
				$link = 'index.php?option=com_flexicontent&'.$ctrl_task.'add&id=0&typeid='.$type_id.'&'. (FLEXI_J30GE ? JSession::getFormToken() : JUtility::getToken()) .'=1';
				$this->setRedirect($link, $msg);
			} else {
				$msg = '';
				$link = 'index.php?option=com_flexicontent&'.$ctrl_task.'edit&id='.$model->get('id').'&'. (FLEXI_J30GE ? JSession::getFormToken() : JUtility::getToken()) .'=1';
				$this->setRedirect($link, $msg);
			}
			
			// Saving has failed check-in and return, (above redirection will be used)
			$model->checkin();
			return;
		}
		
		
		// **************************************************
		// Check in model and get item id in case of new item
		// **************************************************
		$model->checkin();
		$post['id'] = $isnew ? (int) $model->get('id') : $post['id'];
		
		// Get items marked as newly submitted
		$newly_submitted = $session->get('newly_submitted', array(), 'flexicontent');
		if ($isnew) {
			// Mark item as newly submitted, to allow to a proper "THANKS" message after final save & close operation (since user may have clicked add instead of add & close)
			$newly_submitted[$model->get('id')] = 1;
			$session->set('newly_submitted', $newly_submitted, 'flexicontent');
		}
		$newly_submitted_item = @ $newly_submitted[$model->get('id')];
		
		
		// ***********************************************************************************************************
		// Get newly saved -latest- version (store task gets latest) of the item, and also calculate publish privelege
		// ***********************************************************************************************************
		$item = $model->getItem($post['id'], $check_view_access=false, $no_cache=true, $force_version=-1);
		$canPublish = $model->canEditState( $item, $check_cat_perm=true );
		
		
		// ********************************************************************************************
		// Use session to detect multiple item saves to avoid sending notification EMAIL multiple times
		// ********************************************************************************************
		$is_first_save = true;
		if ($session->has('saved_fcitems', 'flexicontent')) {
			$saved_fcitems = $session->get('saved_fcitems', array(), 'flexicontent');
			$is_first_save = $isnew ? true : !isset($saved_fcitems[$model->get('id')]);
		}
		// Add item to saved items of the corresponding session array
		$saved_fcitems[$model->get('id')] = $timestamp = time();  // Current time as seconds since Unix epoc;
		$session->set('saved_fcitems', $saved_fcitems, 'flexicontent');
		
		
		// ********************************************
		// Get categories added / removed from the item
		// ********************************************
		$query 	= 'SELECT DISTINCT c.id, c.title FROM #__categories AS c'
			. ' JOIN #__flexicontent_cats_item_relations AS rel ON rel.catid = c.id'
			. ' WHERE rel.itemid = '.(int) $model->get('id');
		$db->setQuery( $query );
		$after_cats = $db->loadObjectList('id');
		if ( !$isnew ) {
			$cats_added_ids = array_diff(array_keys($after_cats), array_keys($before_cats));
			foreach($cats_added_ids as $cats_added_id) {
				$cats_added_titles[] = $after_cats[$cats_added_id]->title;
			}
			
			$cats_removed_ids = array_diff(array_keys($before_cats), array_keys($after_cats));
			foreach($cats_removed_ids as $cats_removed_id) {
				$cats_removed_titles[] = $before_cats[$cats_removed_id]->title;
			}
			$cats_altered = count($cats_added_ids) + count($cats_removed_ids);
			$after_maincat = $model->get('catid');
		}
		
		
		// *******************************************************************************************************************
		// We need to get emails to notify, from Global/item's Content Type parameters -AND- from item's categories parameters
		// *******************************************************************************************************************
		$notify_emails = array();
		if ( $is_first_save || $cats_altered || $params->get('nf_enable_debug',0) )
		{
			// Get needed flags regarding the saved items
			$approve_version = 2;
			$pending_approval_state = -3;
			$draft_state = -4;
			
			$current_version = FLEXIUtilities::getCurrentVersions($item->id, true); // Get current item version
			$last_version    = FLEXIUtilities::getLastVersions($item->id, true);    // Get last version (=latest one saved, highest version id),
			
			// $post variables vstate & state may have been (a) tampered in the form, and/or (b) altered by save procedure so better not use them
			$needs_version_reviewal     = !$isnew && ($last_version > $current_version) && !$canPublish;
			$needs_publication_approval =  $isnew && ($item->state == $pending_approval_state) && !$canPublish;
			
			$draft_from_non_publisher = $item->state==$draft_state && !$canPublish;
			
			if ($draft_from_non_publisher) {
				// Suppress notifications for draft-state items (new or existing ones), for these each author will publication approval manually via a button
				$nConf = false;
			} else {
				// Get notifications configuration and select appropriate emails for current saving case
				$nConf = $model->getNotificationsConf($params);  //echo "<pre>"; print_r($nConf); "</pre>";
			}
			
			if ($nConf)
			{
				$states_notify_new = $params->get('states_notify_new', array(1,0,(FLEXI_J16GE ? 2:-1),-3,-4,-5));
				if ( empty($states_notify_new) )						$states_notify_new = array();
				else if ( ! is_array($states_notify_new) )	$states_notify_new = !FLEXI_J16GE ? array($states_notify_new) : explode("|", $states_notify_new);
				
				$states_notify_existing = $params->get('states_notify_existing', array(1,0,(FLEXI_J16GE ? 2:-1),-3,-4,-5));
				if ( empty($states_notify_existing) )						$states_notify_existing = array();
				else if ( ! is_array($states_notify_existing) )	$states_notify_existing = !FLEXI_J16GE ? array($states_notify_existing) : explode("|", $states_notify_existing);

				$n_state_ok = in_array($item->state, $states_notify_new);
				$e_state_ok = in_array($item->state, $states_notify_existing);
				
				if ($needs_publication_approval)   $notify_emails = $nConf->emails->notify_new_pending;
				else if ($isnew && $n_state_ok)    $notify_emails = $nConf->emails->notify_new;
				else if ($isnew)                   $notify_emails = array();
				else if ($needs_version_reviewal)  $notify_emails = $nConf->emails->notify_existing_reviewal;
				else if (!$isnew && $e_state_ok)   $notify_emails = $nConf->emails->notify_existing;
				else if (!$isnew)                  $notify_emails = array();
				
				if ($needs_publication_approval)   $notify_text = $params->get('text_notify_new_pending');
				else if ($isnew)                   $notify_text = $params->get('text_notify_new');
				else if ($needs_version_reviewal)  $notify_text = $params->get('text_notify_existing_reviewal');
				else if (!$isnew)                  $notify_text = $params->get('text_notify_existing');
				//print_r($notify_emails); jexit();
			}
		}
		
		
		// *********************************************************************************************************************
		// If there are emails to notify for current saving case, then send the notifications emails, but 
		// *********************************************************************************************************************
		if ( !empty($notify_emails) && count($notify_emails) ) {
			$notify_vars = new stdClass();
			$notify_vars->needs_version_reviewal     = $needs_version_reviewal;
			$notify_vars->needs_publication_approval = $needs_publication_approval;
			$notify_vars->isnew         = $isnew;
			$notify_vars->notify_emails = $notify_emails;
			$notify_vars->notify_text   = $notify_text;
			$notify_vars->before_cats   = $before_cats;
			$notify_vars->after_cats    = $after_cats;
			$notify_vars->original_item = @ $original_item;
			
			$model->sendNotificationEmails($notify_vars, $params, $manual_approval_request=0);
		}
		
		
		// ***************************************************
		// CLEAN THE CACHE so that our changes appear realtime
		// ***************************************************
		if (FLEXI_J16GE) {
			$cache = FLEXIUtilities::getCache($group='', 0);
			$cache->clean('com_flexicontent_items');
			$cache->clean('com_flexicontent_filters');
			$cache = FLEXIUtilities::getCache($group='', 1);
			$cache->clean('com_flexicontent_items');
			$cache->clean('com_flexicontent_filters');
		} else {
			$itemcache = JFactory::getCache('com_flexicontent_items');
			$itemcache->clean();
			$filtercache = JFactory::getCache('com_flexicontent_filters');
			$filtercache->clean();
		}
		
		
		// ****************************************************************************************************************************
		// Recalculate EDIT PRIVILEGE of new item. Reason for needing to do this is because we can have create permission in a category
		// and thus being able to set this category as item's main category, but then have no edit/editown permission for this category
		// ****************************************************************************************************************************
		if (FLEXI_J16GE) {
			$asset = 'com_content.article.' . $model->get('id');
			$canEdit = $user->authorise('core.edit', $asset) || ($user->authorise('core.edit.own', $asset) && $model->get('created_by') == $user->get('id'));
			// ALTERNATIVE 1
			//$canEdit = $model->getItemAccess()->get('access-edit'); // includes privileges edit and edit-own
			// ALTERNATIVE 2
			//$rights = FlexicontentHelperPerm::checkAllItemAccess($user->get('id'), 'item', $model->get('id'));
			//$canEdit = in_array('edit', $rights) || (in_array('edit.own', $rights) && $model->get('created_by') == $user->get('id')) ;
		} else if (FLEXI_ACCESS && $user->gid < 25) {
			$rights 	= FAccess::checkAllItemAccess('com_content', 'users', $user->gmid, $model->get('id'), $model->get('catid'));
			$canEdit = in_array('edit', $rights) || (in_array('editown', $rights) && $model->get('created_by') == $user->get('id')) ;
		} else {
			// This is meaningful when executed in frontend, since all backend users (managers and above) can edit items
			$canEdit = $user->authorize('com_content', 'edit', 'content', 'all') || ($user->authorize('com_content', 'edit', 'content', 'own') && $model->get('created_by') == $user->get('id'));
		}
		
		
		// *******************************************************************************************************
		// Check if user can not edit item further (due to changed main category, without edit/editown permission)
		// *******************************************************************************************************
		if (!$canEdit)
		{
			if ($task=='apply') {
				// APPLY TASK: Temporarily set item to be editable till closing it
				$rendered_uneditable = $session->get('rendered_uneditable', array(),'flexicontent');
				$rendered_uneditable[$model->get('id')]  = 1;
				$session->set('rendered_uneditable', $rendered_uneditable, 'flexicontent');
				$canEdit = 1;
			}
			
			else if ( $newly_submitted_item ) {
				// NEW ITEM: Do not use editable till logoff behaviour
				// ALSO: Clear editable FLAG set in the case that 'apply' button was used during new item creation
				if ( !$params->get('items_session_editable', 0) ) {
					$rendered_uneditable = $session->get('rendered_uneditable', array(),'flexicontent');
					if ( isset($rendered_uneditable[$model->get('id')]) ) {
						unset( $rendered_uneditable[$model->get('id')] );
						$session->set('rendered_uneditable', $rendered_uneditable, 'flexicontent');
					}
				}
			}
			
			else {
				// EXISTING ITEM: (if enabled) Use the editable till logoff behaviour
				if ( $params->get('items_session_editable', 0) ) {
					
					// Set notice for existing item being editable till logoff 
					JError::raiseNotice( 403, JText::_( 'FLEXI_CANNOT_EDIT_AFTER_LOGOFF' ) );
					
					// Allow item to be editable till logoff
					$rendered_uneditable = $session->get('rendered_uneditable', array(),'flexicontent');
					$rendered_uneditable[$model->get('id')]  = 1;
					$session->set('rendered_uneditable', $rendered_uneditable, 'flexicontent');
					$canEdit = 1;
				}
			}
			
			// Set notice about saving an item that cannot be changed further
			if ( !$canEdit ) {
				$app->enqueueMessage(JText::_( 'FLEXI_CANNOT_MAKE_FURTHER_CHANGES_TO_CONTENT' ), 'message' );
			}
		}
		
		
		// ****************************************************************
		// Check for new Content Item is being closed, and clear some flags
		// ****************************************************************
		
		if ($task!='apply' && $newly_submitted_item )
		{
			// Clear item from being marked as newly submitted
			unset($newly_submitted[$model->get('id')]);
			$session->set('newly_submitted', $newly_submitted, 'flexicontent');
			
			// The 'apply' task may set 'editable till logoff' FLAG ...
			// CLEAR IT, since NEW content this is meant to be used temporarily
			if ( !$params->get('items_session_editable', 0) ) {
				$rendered_uneditable = $session->get('rendered_uneditable', array(),'flexicontent');
				if ( isset($rendered_uneditable[$model->get('id')]) ) {
					unset( $rendered_uneditable[$model->get('id')] );
					$session->set('rendered_uneditable', $rendered_uneditable, 'flexicontent');
				}
			}
		}
		
		
		// ****************************************
		// Saving is done, decide where to redirect
		// ****************************************
		
		// REDIRECT CASE FOR APPLYING: Save and reload the item edit form
		if ($task=='apply') {
			$msg = JText::_( 'FLEXI_ITEM_SAVED' );
			
			// Create the URL
			global $globalcats;
			$Itemid = JRequest::getInt('Itemid', 0);  // maintain current menu item if this was given
			$item_url = JRoute::_(FlexicontentHelperRoute::getItemRoute($item->id.':'.$item->alias, $globalcats[$item->catid]->slug, $Itemid));
			$link = $item_url
				.(strstr($item_url, '?') ? '&' : '?').'task=edit'
				;
			
			// Important pass referer back to avoid making the form itself the referer
			// but also check that referer URL is 'safe' (allowed) , e.g. not an offsite URL, otherwise set referer to HOME page
			$referer = JRequest::getString('referer', JURI::base(), 'post');
			if ( ! flexicontent_html::is_safe_url($referer) ) $referer = JURI::base();
			$return = '&return='.base64_encode( $referer );
			$link .= $return;
		}
		
		// REDIRECT CASES FOR SAVING
		else {
			
			// REDIRECT CASE: Return to a custom page after creating a new item (e.g. a thanks page)
			if ( $newly_submitted_item && $submit_redirect_url_fe ) {
				$link = $submit_redirect_url_fe;
				$msg = JText::_( 'FLEXI_ITEM_SAVED' );
			}
			// REDIRECT CASE: Save and preview the latest version
			else if ($task=='save_a_preview') {
				$msg = JText::_( 'FLEXI_ITEM_SAVED' );
				$link = JRoute::_(FlexicontentHelperRoute::getItemRoute($model->_item->id.':'.$model->_item->alias, $model->_item->catid, 0, $model->_item).'&preview=1', false);
			}
			// REDIRECT CASE: Return to the form 's referer (previous page) after item saving
			else {
				$msg = $newly_submitted_item ? JText::_( 'FLEXI_THANKS_SUBMISSION' ) : JText::_( 'FLEXI_ITEM_SAVED' );
				
				// Check that referer URL is 'safe' (allowed) , e.g. not an offsite URL, otherwise for returning to HOME page
				$link = JRequest::getString('referer', JURI::base(), 'post');
				if ( ! flexicontent_html::is_safe_url($link) ) {
					if ( $dolog ) JFactory::getApplication()->enqueueMessage( 'refused redirection to possible unsafe URL: '.$link, 'notice' );
					$link = JURI::base();
				}
			}
		}
		
		$this->setRedirect($link, $msg);
	}
        function save_import()
	{
		// Check for request forgeries
		//JRequest::checkToken() or jexit( 'Invalid Token' );
		
		
		
                //mara
                
                $arr_countries = array('AL' => 'Albania',
 'AM' => 'Armenia' ,
 'ARG' =>'Argentina' ,
 'AU' => 'Australia' ,
 'AUT' => 'Austria' ,
 'AZ' =>'Azerbaidjan' ,
 'BEL' => 'Belgium' ,
 'BGD' => 'Bangladesh' ,
 'BLG' => 'Bulgaria' ,
 'BLR' => 'Belarus' ,
 'BOS' => 'Bosnia-Herzegovina' ,
 'BRA' => 'Brazil' ,
 'CAN' => 'Canada' ,
 'CB' => 'Cuba' ,
 'CHE' => 'Switzerland' ,
 'CHI' => 'China' ,
 'CHL' => 'Chile' ,
 'CS' => 'Czech Republic' ,
 'DEU' => 'Germany' ,
 'DNK' => 'Denmark' ,
 'EGP' => 'Egypt' ,
 'EST' => 'Estonia' ,
 'FIN' => 'Finland' ,
 'FRA' => 'France' ,
 'GBR' => 'Great Britain' ,
 'GBRUSA' => 'Great Britain' ,
 'GEO' => 'Georgia' ,
 'GR' => 'Greece' ,
 'HK' => 'Hong Kong' ,
 'HOR' => 'Croatia' ,
 'HUN' => 'Hungary' ,
 'IDZ' => 'Indonesia' ,
 'IND' => 'India' ,
 'IOR' => 'Jordan' ,
 'IRA' => 'Iran' ,
 'IRL' => 'Ireland' ,
 'ISL' => 'Iceland' ,
 'ISR' => 'Israel' ,
 'ITA' => 'Italy' ,
 'JPN' => 'Japan' ,
 'KAZ' => 'Kazakhstan' ,
 'KGZ' => 'Kyrgyzstan' ,
 'KIP' => 'Cyprus' ,
 'LAT' => 'Latvia' ,
 'LIT' => 'Lithuania' ,
 'LUX' => 'Luxembourg' ,
 'MAK' => 'Macedonia' ,
 'ME' => 'Montenegro' ,
 'MEK' => 'Mexico' ,
 'MLT' => 'Malta' ,
 'MOL' => 'Moldavia' ,
 'MON' => 'Monaco' ,
 'NID' => 'Netherlands' ,
 'NOR' => 'Norway' ,
 'PAK' => 'Pakistan' ,
 'POL' => 'Poland' ,
 'PORT' => 'Portugal' ,
 'PR' => 'Puerto Rico' ,
 'PS' => 'Palestinian Territory' ,
 'RS' => 'Serbia' ,
 'RUM' => 'Romania' ,
 'RUS' => 'Russian Federation' ,
 'SA' => 'Saudi Arabia' ,
 'SCH' => 'Serbia' ,
 'SGP' => 'Singapore' ,
 'SHE' => 'Switzerland' ,
 'SKO' => 'South Korea' ,
 'SLO' => 'Slovenia' ,
 'SLR' => 'Slovak Republic' ,
 'SM' => 'San Marino' ,
 'SPN' => 'Spain' ,
 'SWE' => 'Sweden' ,
 'SYR' => 'Syria' ,
 'TA' => 'Thailand' ,
 'TUR' => 'Turkey' ,
 'UAR' => 'South Africa' ,
 'UGS' => 'Serbia' ,
 'UKR' => 'Ukraine' ,
 'unk' => 'Unknown' ,
 'USA' => 'United States' ,
 'USAFRA' => 'United States' ,
 'Uzb' => 'Uzbekistan' ,
 'VTN' => 'Vietnam');
                
                
		mysql_connect('localhost', 'root', 'staSPE8e');
                mysql_select_db('vidal');
                mysql_query("SET NAMES utf8");
                $query = "SELECT pa.ATCCode,m.LatName,n.NozologyCode,Document.DocumentID,"
                        ."Document.RusName,Document.EngName,Document.CompiledComposition,Document.PhInfluence,Document.PhKinetics,"
                        ."Document.Dosage,Document.OverDosage,Document.Interaction,Document.Lactation,Document.SideEffects,"
                        ."Document.StorageCondition,Document.Indication,Document.ContraIndication,Document.SpecialInstruction "
                        . "FROM Document"
                        ." LEFT JOIN Document_IndicNozology as n ON Document.DocumentID = n.DocumentID"
                        ." LEFT JOIN Molecule_Document as md ON md.DocumentID = Document.DocumentID"
                        ." LEFT JOIN Molecule as m ON m.MoleculeID = md.MoleculeID"
                        ." LEFT JOIN Product_Document as pd ON pd.DocumentID = Document.DocumentID"
                        ." LEFT JOIN Product_ATC as pa ON pd.ProductID = pa.ProductID"
                        ." GROUP BY Document.DocumentID";
                $result = mysql_query($query) or die(mysql_error());
		while($all = mysql_fetch_array($result)){
                    
                    
                    // Initialize variables
		$app     = JFactory::getApplication();
		$db      = JFactory::getDBO();
		$user    = JFactory::getUser();
		$menu    = $app->getMenu()->getActive();
		$config  = JFactory::getConfig();
		$session = JFactory::getSession();
		$task	   = JRequest::getVar('task');
		$model   = $this->getModel(FLEXI_ITEMVIEW);
		$isnew   = !$model->getId();
		$ctrl_task = FLEXI_J16GE ? 'task=items.' : 'controller=items&task=';
		
		$fc_params  = JComponentHelper::getParams( 'com_flexicontent' );
		$dolog      = $fc_params->get('print_logging_info');
		
		// Get the COMPONENT only parameters
		$comp_params = JComponentHelper::getComponent('com_flexicontent')->params;
		$params = FLEXI_J16GE ? clone ($comp_params) : new JParameter( $comp_params ); // clone( JComponentHelper::getParams('com_flexicontent') );
		
		// Merge the type parameters
		$tparams = $model->getTypeparams();
		$tparams = FLEXI_J16GE ? new JRegistry($tparams) : new JParameter($tparams);
		$params->merge($tparams);
		
		// Merge the menu parameters
		if ($menu) {
			$menu_params = FLEXI_J16GE ? $menu->params : new JParameter($menu->params);
			$params->merge($menu_params);
		}
		
		// Get needed parameters
		$submit_redirect_url_fe = $params->get('submit_redirect_url_fe', '');
		$allowunauthorize       = $params->get('allowunauthorize', 0);
                    
                    $data = array();
                    
                    $data['title'] = $all['RusName'];
                    //content
                    $data['text'] = $all['CompiledComposition'].$all['PhInfluence'].$all['PhKinetics'].$all['Dosage'].$all['OverDosage'].$all['Interaction'].$all['Lactation'].$all['SideEffects'].$all['StorageCondition'].$all['Indication'].$all['ContraIndication'].$all['SpecialInstruction'];
                    $data['state'] = 1;
                    $data['catid'] = 45;
                    $data['type_id'] = 2;
                    $data['id'] = 0;
                    //insert into content

                    //flexicontent_fields_item_relations
                    //15 field RusName EngName

                    //19 field Zabolev
                    $zab = '';
                    if($all['NozologyCode']){
                        $tmp = $all['NozologyCode'];
                        $zab_cif = substr($tmp,1,2);
                        $alpha = substr($tmp,0,1);
                        switch($alpha){
                            case 'A' : $zab = 'A00–B99'; break;
                            case 'B' : $zab = 'A00–B99';break;
                            case 'C' : $zab = 'C00–D48';break;
                            case 'D' : $zab = $zab_cif <= 48 ? 'C00–D48' : 'D50–D89';break;
                            case 'E' : $zab = 'E00–E90';break;
                            case 'F' : $zab = 'F00–F99';break;
                            case 'G' : $zab = 'G00–G99';break;
                            case 'H' : $zab = $zab_cif <= 59 ? 'H00–H59' : 'H60–H95';break;
                            case 'I' : $zab = 'I00–I99';break;
                            case 'J' : $zab = 'J00–J99';break;
                            case 'K' : $zab = 'K00–K93';break;
                            case 'L' : $zab = 'L00–L99';break;
                            case 'M' : $zab = 'M00–M99';break;
                            case 'N' : $zab = 'N00–N99';break;
                            case 'O' : $zab = 'O00–O99';break;
                            case 'P' : $zab = 'P00–P96'; break;
                            case 'R' : $zab = 'R00–R99'; break;
                            case 'S' : $zab = 'S00–T98'; break;
                            case 'V' : $zab = 'V01–Y98';break;
                            case 'Z' : $zab = 'Z00–Z99';break;
                            case 'U' : $zab = 'U00–U99'; break;
                            default: $zab = '';
                        }
                    }
                    $custom = array();
                    $custom['zabolevanie'] = $zab;
                    $custom['field24'] = $all['ATCCode'];
                    /*$custom['field24_1'] = '';
                    $custom['field24_2'] = '';
                    $custom['field24_3'] = '';
                    $custom['field24_4'] = '';
                    $custom['field24_5'] = '';*/
                    $custom['preparat'][0] = addslashes($all['RusName']);
                    $custom['preparat'][1] = addslashes($all['EngName']);
                    $custom['field22'][0] = addslashes($all['LatName']);
                    
                    
                    $query = "SELECT p.DateOfCloseRegistration, p.RegistrationNumber, p.Composition, p.ZipInfo, "
                            ." c.LocalName, c.CountryCode "
                        . "FROM Product as p"
                        ." JOIN Product_Company as pc ON pc.ProductID = p.ProductID"
                        ." JOIN Company as c ON c.CompanyID = pc.CompanyID"    
                        ." JOIN Product_Document d ON d.ProductID = p.ProductID"
                        ." WHERE d.DocumentID = ".$all['DocumentID'];
                    $result1 = mysql_query($query) or die(mysql_error());
                    $field_pr = array();
                    $z = 0;
                    while($proizv = mysql_fetch_array($result1)){
                        if(isset($arr_countries[$proizv['CountryCode']])){
                            $custom['field21'][0]['country'][$z] =  addslashes($arr_countries[$proizv['CountryCode']]);
                            $custom['field21'][0]['naimen'][$z] =  addslashes($proizv['LocalName']);//."<br />".$proizv['Composition']);
                            $custom['field21'][0]['vypusk'][$z] =  addslashes($proizv['ZipInfo']);
                            $custom['field21'][0]['reg'][$z] =  addslashes($proizv['RegistrationNumber']);
                            $custom['field21'][0]['date'][$z] =  addslashes($proizv['DateOfCloseRegistration']);
                        }
                        $z++;
                    }
                    
                                // *********************
                                // Get data from request
                                // *********************

                                if (FLEXI_J16GE) {
                                        // Retrieve form data these are subject to basic filtering
                                       // $data   = JRequest::getVar('jform', array(), 'post', 'array');   // Core Fields and and item Parameters
                                       // $custom = JRequest::getVar('custom', array(), 'post', 'array');  // Custom Fields
                                        $jfdata = JRequest::getVar('jfdata', array(), 'post', 'array');  // Joomfish Data
                                        if ( ! @ $data['rules'] ) $data['rules'] = array();
                                }

                                else {
                                        // Retrieve form data these are subject to basic filtering
                                        $data = JRequest::get( 'post' );  // Core & Custom Fields and item Parameters
                                }

                                // Set data id into model in case not already set ?
                                $model->setId((int) $data['id']);



                                // *************************************
                                // ENFORCE can change category ACL perms
                                // *************************************

                                $perms = FlexicontentHelperPerm::getPerm();
                                // Per content type change category permissions
                                if (FLEXI_J16GE) {
                                        $current_type_id  = ($isnew || !$model->get('type_id')) ? $data['type_id'] : $model->get('type_id');  // GET current (existing/old) item TYPE ID
                                        $CanChangeFeatCat = $user->authorise('flexicontent.change.cat.feat', 'com_flexicontent.type.' . $current_type_id);
                                        $CanChangeSecCat  = $user->authorise('flexicontent.change.cat.sec', 'com_flexicontent.type.' . $current_type_id);
                                        $CanChangeCat     = $user->authorise('flexicontent.change.cat', 'com_flexicontent.type.' . $current_type_id);
                                } else {
                                        $CanChangeFeatCat = 1;
                                        $CanChangeSecCat  = 1;
                                        $CanChangeCat     = 1;
                                }

                                $featured_cats_parent = $params->get('featured_cats_parent', 0);
                                $featured_cats = array();

                                $enable_featured_cid_selector = $perms->MultiCat && $CanChangeFeatCat;
                                $enable_cid_selector   = $perms->MultiCat && $CanChangeSecCat;
                                $enable_catid_selector = ($isnew && !$tparams->get('catid_default')) || (!$isnew && !$model->get('catid')) || $CanChangeCat;

                                // Enforce maintaining featured categories
                                $featured_cats_parent = $params->get('featured_cats_parent', 0);
                                $featured_cats = array();
                                if ( $featured_cats_parent && !$enable_featured_cid_selector )
                                {
                                        $featured_tree = flexicontent_cats::getCategoriesTree($published_only=1, $parent_id=$featured_cats_parent, $depth_limit=0);
                                        $featured_cid = array();
                                        if (!$isnew) {
                                                foreach($model->get('categories') as $item_cat) if (isset($featured_tree[$item_cat])) $featured_cid[] = $item_cat;
                                        }
                                        $data['featured_cid'] = $featured_cid;
                                }

                                // Enforce maintaining secondary categories
                                if (!$enable_cid_selector) {
                                        if ($isnew) {
                                                $data['cid'] = $tparams->get('cid_default');
                                        } else if ( isset($featured_cid) ) {
                                                $featured_cid_arr = array_flip($featured_cid);
                                                $sec_cid = array();
                                                foreach($model->get('cats') as $item_cat) if (!isset($featured_cid_arr[$item_cat])) $sec_cid[] = $item_cat;
                                                $data['cid'] = $sec_cid;
                                        } else {
                                                $data['cid'] = $model->get('cats');
                                        }
                                }

                                if (!$enable_catid_selector) {
                                        if ($isnew && $tparams->get('catid_default'))
                                                $data['catid'] = $tparams->get('catid_default');
                                        else if ($model->get('catid'))
                                                $data['catid'] = $model->get('catid');
                                }



                                // **************************
                                // Basic Form data validation
                                // **************************

                                if (FLEXI_J16GE)
                                {
                                        // *** MANUALLY CHECK CAPTCHA ***
                                        $use_captcha    = $params->get('use_captcha', 1);     // 1 for guests, 2 for any user
                                        $captcha_formop = $params->get('captcha_formop', 0);  // 0 for submit, 1 for submit/edit (aka always)
                                        $is_submitop = ((int) $data['id']) == 0;
                                        $display_captcha = $use_captcha >= 2 || ( $use_captcha == 1 &&  $user->guest );
                                        $display_captcha = $display_captcha && ( $is_submitop || $captcha_formop);  // for submit operation we do not need to check 'captcha_formop' ...
                                        if ($display_captcha)
                                        {
                                                // Try to force the use of recaptcha plugin
                                                JFactory::getConfig()->set('captcha', 'recaptcha');

                                                if ( $app->getCfg('captcha') == 'recaptcha' && JPluginHelper::isEnabled('captcha', 'recaptcha') ) {
                                                        JPluginHelper::importPlugin('captcha');
                                                        $dispatcher = JDispatcher::getInstance();
                                                        $result = $dispatcher->trigger('onCheckAnswer', JRequest::getString('recaptcha_response_field'));
                                                        if (!$result[0]) {
                                                                $errmsg  = JText::_('FLEXI_CAPTCHA_FAILED');
                                                                $errmsg .= ' '.JText::_('FLEXI_MUST_REFILL_SOME_FIELDS');
                                                                echo "<script>alert('".$errmsg."');";
                                                                echo "window.history.back();";
                                                                echo "</script>";
                                                                jexit();
                                                        }
                                                }
                                        }

                                        // Validate Form data for core fields and for parameters
                                        $form = $model->getForm();          // Do not pass any data we only want the form object in order to validate the data and not create a filled-in form
                                        $post = $model->validate($form, $data);

                                        // Check for validation error
                                        if (!$post) {
                                                // Get the validation messages.
                                                $errors	= $form->getErrors();

                                                // Push up to three validation messages out to the user.
                                                for ($i = 0, $n = count($errors); $i < $n && $i < 3; $i++) {
                                                        if ($errors[$i] instanceof Exception)
                                                                $app->enqueueMessage($errors[$i]->getMessage(), 'notice');
                                                        else
                                                                $app->enqueueMessage($errors[$i], 'notice');
                                                }

                                                // Save the jform data in the session.
                                                $app->setUserState($form->option.'.edit.'.$form->context.'.data', $data);
                                                // Save the custom fields data in the session.
                                                $app->setUserState($form->option.'.edit.'.$form->context.'.custom', $custom);

                                                // Redirect back to the registration form.
                                                $this->setRedirect( $_SERVER['HTTP_REFERER'] );
                                                return false;
                                                //die('error');
                                        }

                                        /*if (!$post) {
                                                //JError::raiseWarning( 500, "Error while validating data: " . $model->getError() );
                                                echo "Error while validating data: " . $model->getError();
                                                echo '<span class="fc_return_msg">'.JText::sprintf('FLEXI_CLICK_HERE_TO_RETURN', '"JavaScript:window.history.back();"').'</span>';
                                                jexit();
                                        }*/

                                        // Some values need to be assigned after validation
                                        $post['attribs'] = @$data['attribs'];  // Workaround for item's template parameters being clear by validation since they are not present in item.xml
                                        $post['custom']  = & $custom;          // Assign array of custom field values, they are in the 'custom' form array instead of jform
                                        $post['jfdata']  = & $jfdata;          // Assign array of Joomfish field values, they are in the 'jfdata' form array instead of jform

                                        // Assign template parameters of the select ilayout as an sub-array (the DB model will handle the merging of parameters)
                                        $ilayout = @ $data['attribs']['ilayout'];  // normal not be set if frontend template editing is not shown
                                        if( $ilayout && !empty($data['layouts'][$ilayout]) )   $post['attribs']['layouts'] = $data['layouts'];
                                        //echo "<pre>"; print_r($post['attribs']); exit;
                                }

                                else {
                                        $post = $data;

                                        // Some values need to be assigned after validation
                                        $post['text'] = JRequest::getVar( 'text', '', 'post', 'string', JREQUEST_ALLOWRAW ); // Workaround for allowing raw text field

                                        // Assign template parameters of the select ilayout as an sub-array (the DB model will handle the merging of parameters)
                                        $ilayout = @ $post['params']['ilayout'];  // normal not be set if frontend template editing is not shown
                                        if( $ilayout && !empty($post['layouts'][$ilayout]) )  $post['params']['layouts'] = $post['layouts'];
                                        //echo "<pre>"; print_r($post['params']); exit;

                                }

                                // USEFULL FOR DEBUGING for J2.5 (do not remove commented code)
                                //$diff_arr = array_diff_assoc ( $data, $post);
                                //echo "<pre>"; print_r($diff_arr); jexit();


                                // ********************************************************************************
                                // PERFORM ACCESS CHECKS, NOTE: we need to check access again, despite having
                                // checked them on edit form load, because user may have tampered with the form ... 
                                // ********************************************************************************

                                $type_id = (int) @ $post['type_id'];  // Typecast to int, (already done for J2.5 via validating)
                                if ( !$isnew && $model->get('type_id') == $type_id ) {
                                        // Existing item with Type not being ALTERED, content type can be maintained regardless of privilege
                                        $canCreateType = true;
                                } else {
                                        // New item or existing item with Type is being ALTERED, check privilege to create items of this type
                                        $canCreateType = $model->canCreateType( array($type_id), true, $types );
                                }


                                // ****************************************************************
                                // Calculate user's privileges on current content item
                                // ... canPublish IS RECALCULATED after saving, maybe comment out ?
                                // ****************************************************************

                                if (!$isnew) {

                                        if (FLEXI_J16GE) {
                                                $asset = 'com_content.article.' . $model->get('id');
                                                $canPublish = $user->authorise('core.edit.state', $asset) || ($user->authorise('core.edit.state.own', $asset) && $model->get('created_by') == $user->get('id'));
                                                $canEdit = $user->authorise('core.edit', $asset) || ($user->authorise('core.edit.own', $asset) && $model->get('created_by') == $user->get('id'));
                                                // ALTERNATIVE 1
                                                //$canEdit = $model->getItemAccess()->get('access-edit'); // includes privileges edit and edit-own
                                                // ALTERNATIVE 2
                                                //$rights = FlexicontentHelperPerm::checkAllItemAccess($user->get('id'), 'item', $model->get('id'));
                                                //$canEdit = in_array('edit', $rights) || (in_array('edit.own', $rights) && $model->get('created_by') == $user->get('id')) ;
                                        } else if ($user->gid >= 25) {
                                                $canPublish = true;
                                                $canEdit = true;
                                        } else if (FLEXI_ACCESS) {
                                                $rights 	= FAccess::checkAllItemAccess('com_content', 'users', $user->gmid, $model->get('id'), $model->get('catid'));
                                                $canPublish = in_array('publish', $rights) || (in_array('publishown', $rights) && $model->get('created_by') == $user->get('id')) ;
                                                $canEdit = in_array('edit', $rights) || (in_array('editown', $rights) && $model->get('created_by') == $user->get('id')) ;
                                        } else {
                                                $canPublish = $user->authorize('com_content', 'publish', 'content', 'all');
                                                $canEdit = $user->authorize('com_content', 'edit', 'content', 'all') || ($user->authorize('com_content', 'edit', 'content', 'own') && $model->get('created_by') == $user->get('id'));
                                                //$canPublish = ($user->gid >= 21);  // At least J1.5 Publisher
                                                //$canEdit = ($user->gid >= 20);  // At least J1.5 Editor
                                        }

                                        if ( !$canEdit ) {
                                                // No edit privilege, check if item is editable till logoff
                                                if ($session->has('rendered_uneditable', 'flexicontent')) {
                                                        $rendered_uneditable = $session->get('rendered_uneditable', array(),'flexicontent');
                                                        $canEdit = isset($rendered_uneditable[$model->get('id')]) && $rendered_uneditable[$model->get('id')];
                                                }
                                        }

                                } else {

                                        if (FLEXI_J16GE) {
                                                $canAdd = $model->getItemAccess()->get('access-create'); // includes check of creating in at least one category
                                                $not_authorised = !$canAdd;

                                                $canPublish	= $user->authorise('core.edit.state', 'com_flexicontent') || $user->authorise('core.edit.state.own', 'com_flexicontent');
                                        } else if ($user->gid >= 25) {
                                                $canAdd = 1;
                                        } else if (FLEXI_ACCESS) {
                                                $canAdd = FAccess::checkUserElementsAccess($user->gmid, 'submit');
                                                $canAdd = @$canAdd['content'] || @$canAdd['category'];

                                                $canPublishAll 		= FAccess::checkAllContentAccess('com_content','publish','users',$user->gmid,'content','all');
                                                $canPublishOwnAll	= FAccess::checkAllContentAccess('com_content','publishown','users',$user->gmid,'content','all');
                                                $canPublish	= ($user->gid < 25) ? $canPublishAll || $canPublishOwnAll : 1;
                                        } else {
                                                $canAdd	= $user->authorize('com_content', 'add', 'content', 'all');
                                                //$canAdd = ($user->gid >= 19);  // At least J1.5 Author
                                                $not_authorised = ! $canAdd;
                                                $canPublish	= ($user->gid >= 21);
                                        }

                                        if ( $allowunauthorize ) {
                                                $canAdd = true;
                                                $canCreateType = true;
                                        }
                                }

                                // ... we use some strings from administrator part
                                // load english language file for 'com_flexicontent' component then override with current language file
                                JFactory::getLanguage()->load('com_flexicontent', JPATH_ADMINISTRATOR, 'en-GB', true);
                                JFactory::getLanguage()->load('com_flexicontent', JPATH_ADMINISTRATOR, null, true);

                                // Check for new content
                                if ( ($isnew && !$canAdd) || (!$isnew && !$canEdit)) {
                                        $msg = JText::_( 'FLEXI_ALERTNOTAUTH' );
                                        if (FLEXI_J16GE) throw new Exception($msg, 403); else JError::raiseError(403, $msg);
                                }

                                if ( !$canCreateType ) {
                                        $msg = isset($types[$type_id]) ?
                                                JText::sprintf( 'FLEXI_NO_ACCESS_CREATE_CONTENT_OF_TYPE', JText::_($types[$type_id]->name) ) :
                                                ' Content Type '.$type_id.' was not found OR is not published';
                                        if (FLEXI_J16GE) throw new Exception($msg, 403); else JError::raiseError(403, $msg);
                                        return;
                                }

                                // Get "BEFORE SAVE" categories for information mail
                                $before_cats = array();
                                if ( !$isnew )
                                {
                                        $query 	= 'SELECT DISTINCT c.id, c.title FROM #__categories AS c'
                                                . ' JOIN #__flexicontent_cats_item_relations AS rel ON rel.catid = c.id'
                                                . ' WHERE rel.itemid = '.(int) $model->get('id');
                                        $db->setQuery( $query );
                                        $before_cats = $db->loadObjectList('id');
                                        $before_maincat = $model->get('catid');
                                        $original_item = $model->getItem($post['id'], $check_view_access=false, $no_cache=true, $force_version=0);
                                }


                                // ****************************************
                                // Try to store the form data into the item
                                // ****************************************
                                if ( ! $model->store($post) )
                                {
                                        // Set error message about saving failed, and also the reason (=model's error message)
                                        $msg = JText::_( 'FLEXI_ERROR_STORING_ITEM' );
                                        JError::raiseWarning( 500, $msg .": " . $model->getError() );

                                        // Since an error occured, check if (a) the item is new and (b) was not created
                                        if ($isnew && !$model->get('id')) {
                                                $msg = '';
                                                $link = 'index.php?option=com_flexicontent&'.$ctrl_task.'add&id=0&typeid='.$type_id.'&'. (FLEXI_J30GE ? JSession::getFormToken() : JUtility::getToken()) .'=1';
                                                $this->setRedirect($link, $msg);
                                        } else {
                                                $msg = '';
                                                $link = 'index.php?option=com_flexicontent&'.$ctrl_task.'edit&id='.$model->get('id').'&'. (FLEXI_J30GE ? JSession::getFormToken() : JUtility::getToken()) .'=1';
                                                $this->setRedirect($link, $msg);
                                        }

                                        // Saving has failed check-in and return, (above redirection will be used)
                                        $model->checkin();
                                        return;
                                }


                                // **************************************************
                                // Check in model and get item id in case of new item
                                // **************************************************
                                $model->checkin();
                                $post['id'] = $isnew ? (int) $model->get('id') : $post['id'];

                                // Get items marked as newly submitted
                                $newly_submitted = $session->get('newly_submitted', array(), 'flexicontent');
                                if ($isnew) {
                                        // Mark item as newly submitted, to allow to a proper "THANKS" message after final save & close operation (since user may have clicked add instead of add & close)
                                        $newly_submitted[$model->get('id')] = 1;
                                        $session->set('newly_submitted', $newly_submitted, 'flexicontent');
                                }
                                $newly_submitted_item = @ $newly_submitted[$model->get('id')];


                                // ***********************************************************************************************************
                                // Get newly saved -latest- version (store task gets latest) of the item, and also calculate publish privelege
                                // ***********************************************************************************************************
                                $item = $model->getItem($post['id'], $check_view_access=false, $no_cache=true, $force_version=-1);
                                $canPublish = $model->canEditState( $item, $check_cat_perm=true );


                                // ********************************************************************************************
                                // Use session to detect multiple item saves to avoid sending notification EMAIL multiple times
                                // ********************************************************************************************
                                $is_first_save = true;
                                if ($session->has('saved_fcitems', 'flexicontent')) {
                                        $saved_fcitems = $session->get('saved_fcitems', array(), 'flexicontent');
                                        $is_first_save = $isnew ? true : !isset($saved_fcitems[$model->get('id')]);
                                }
                                // Add item to saved items of the corresponding session array
                                $saved_fcitems[$model->get('id')] = $timestamp = time();  // Current time as seconds since Unix epoc;
                                $session->set('saved_fcitems', $saved_fcitems, 'flexicontent');


                                // ********************************************
                                // Get categories added / removed from the item
                                // ********************************************
                                $query 	= 'SELECT DISTINCT c.id, c.title FROM #__categories AS c'
                                        . ' JOIN #__flexicontent_cats_item_relations AS rel ON rel.catid = c.id'
                                        . ' WHERE rel.itemid = '.(int) $model->get('id');
                                $db->setQuery( $query );
                                $after_cats = $db->loadObjectList('id');
                                if ( !$isnew ) {
                                        $cats_added_ids = array_diff(array_keys($after_cats), array_keys($before_cats));
                                        foreach($cats_added_ids as $cats_added_id) {
                                                $cats_added_titles[] = $after_cats[$cats_added_id]->title;
                                        }

                                        $cats_removed_ids = array_diff(array_keys($before_cats), array_keys($after_cats));
                                        foreach($cats_removed_ids as $cats_removed_id) {
                                                $cats_removed_titles[] = $before_cats[$cats_removed_id]->title;
                                        }
                                        $cats_altered = count($cats_added_ids) + count($cats_removed_ids);
                                        $after_maincat = $model->get('catid');
                                }


                                // *******************************************************************************************************************
                                // We need to get emails to notify, from Global/item's Content Type parameters -AND- from item's categories parameters
                                // *******************************************************************************************************************
                                $notify_emails = array();
                                if ( $is_first_save || $cats_altered || $params->get('nf_enable_debug',0) )
                                {
                                        // Get needed flags regarding the saved items
                                        $approve_version = 2;
                                        $pending_approval_state = -3;
                                        $draft_state = -4;

                                        $current_version = FLEXIUtilities::getCurrentVersions($item->id, true); // Get current item version
                                        $last_version    = FLEXIUtilities::getLastVersions($item->id, true);    // Get last version (=latest one saved, highest version id),

                                        // $post variables vstate & state may have been (a) tampered in the form, and/or (b) altered by save procedure so better not use them
                                        $needs_version_reviewal     = !$isnew && ($last_version > $current_version) && !$canPublish;
                                        $needs_publication_approval =  $isnew && ($item->state == $pending_approval_state) && !$canPublish;

                                        $draft_from_non_publisher = $item->state==$draft_state && !$canPublish;

                                        if ($draft_from_non_publisher) {
                                                // Suppress notifications for draft-state items (new or existing ones), for these each author will publication approval manually via a button
                                                $nConf = false;
                                        } else {
                                                // Get notifications configuration and select appropriate emails for current saving case
                                                $nConf = $model->getNotificationsConf($params);  //echo "<pre>"; print_r($nConf); "</pre>";
                                        }

                                        if ($nConf)
                                        {
                                                $states_notify_new = $params->get('states_notify_new', array(1,0,(FLEXI_J16GE ? 2:-1),-3,-4,-5));
                                                if ( empty($states_notify_new) )						$states_notify_new = array();
                                                else if ( ! is_array($states_notify_new) )	$states_notify_new = !FLEXI_J16GE ? array($states_notify_new) : explode("|", $states_notify_new);

                                                $states_notify_existing = $params->get('states_notify_existing', array(1,0,(FLEXI_J16GE ? 2:-1),-3,-4,-5));
                                                if ( empty($states_notify_existing) )						$states_notify_existing = array();
                                                else if ( ! is_array($states_notify_existing) )	$states_notify_existing = !FLEXI_J16GE ? array($states_notify_existing) : explode("|", $states_notify_existing);

                                                $n_state_ok = in_array($item->state, $states_notify_new);
                                                $e_state_ok = in_array($item->state, $states_notify_existing);

                                                if ($needs_publication_approval)   $notify_emails = $nConf->emails->notify_new_pending;
                                                else if ($isnew && $n_state_ok)    $notify_emails = $nConf->emails->notify_new;
                                                else if ($isnew)                   $notify_emails = array();
                                                else if ($needs_version_reviewal)  $notify_emails = $nConf->emails->notify_existing_reviewal;
                                                else if (!$isnew && $e_state_ok)   $notify_emails = $nConf->emails->notify_existing;
                                                else if (!$isnew)                  $notify_emails = array();

                                                if ($needs_publication_approval)   $notify_text = $params->get('text_notify_new_pending');
                                                else if ($isnew)                   $notify_text = $params->get('text_notify_new');
                                                else if ($needs_version_reviewal)  $notify_text = $params->get('text_notify_existing_reviewal');
                                                else if (!$isnew)                  $notify_text = $params->get('text_notify_existing');
                                                //print_r($notify_emails); jexit();
                                        }
                                }


                                // *********************************************************************************************************************
                                // If there are emails to notify for current saving case, then send the notifications emails, but 
                                // *********************************************************************************************************************
                                if ( !empty($notify_emails) && count($notify_emails) ) {
                                        $notify_vars = new stdClass();
                                        $notify_vars->needs_version_reviewal     = $needs_version_reviewal;
                                        $notify_vars->needs_publication_approval = $needs_publication_approval;
                                        $notify_vars->isnew         = $isnew;
                                        $notify_vars->notify_emails = $notify_emails;
                                        $notify_vars->notify_text   = $notify_text;
                                        $notify_vars->before_cats   = $before_cats;
                                        $notify_vars->after_cats    = $after_cats;
                                        $notify_vars->original_item = @ $original_item;

                                        $model->sendNotificationEmails($notify_vars, $params, $manual_approval_request=0);
                                }


                                // ***************************************************
                                // CLEAN THE CACHE so that our changes appear realtime
                                // ***************************************************
                                if (FLEXI_J16GE) {
                                        $cache = FLEXIUtilities::getCache($group='', 0);
                                        $cache->clean('com_flexicontent_items');
                                        $cache->clean('com_flexicontent_filters');
                                        $cache = FLEXIUtilities::getCache($group='', 1);
                                        $cache->clean('com_flexicontent_items');
                                        $cache->clean('com_flexicontent_filters');
                                } else {
                                        $itemcache = JFactory::getCache('com_flexicontent_items');
                                        $itemcache->clean();
                                        $filtercache = JFactory::getCache('com_flexicontent_filters');
                                        $filtercache->clean();
                                }


                                // ****************************************************************************************************************************
                                // Recalculate EDIT PRIVILEGE of new item. Reason for needing to do this is because we can have create permission in a category
                                // and thus being able to set this category as item's main category, but then have no edit/editown permission for this category
                                // ****************************************************************************************************************************
                                if (FLEXI_J16GE) {
                                        $asset = 'com_content.article.' . $model->get('id');
                                        $canEdit = $user->authorise('core.edit', $asset) || ($user->authorise('core.edit.own', $asset) && $model->get('created_by') == $user->get('id'));
                                        // ALTERNATIVE 1
                                        //$canEdit = $model->getItemAccess()->get('access-edit'); // includes privileges edit and edit-own
                                        // ALTERNATIVE 2
                                        //$rights = FlexicontentHelperPerm::checkAllItemAccess($user->get('id'), 'item', $model->get('id'));
                                        //$canEdit = in_array('edit', $rights) || (in_array('edit.own', $rights) && $model->get('created_by') == $user->get('id')) ;
                                } else if (FLEXI_ACCESS && $user->gid < 25) {
                                        $rights 	= FAccess::checkAllItemAccess('com_content', 'users', $user->gmid, $model->get('id'), $model->get('catid'));
                                        $canEdit = in_array('edit', $rights) || (in_array('editown', $rights) && $model->get('created_by') == $user->get('id')) ;
                                } else {
                                        // This is meaningful when executed in frontend, since all backend users (managers and above) can edit items
                                        $canEdit = $user->authorize('com_content', 'edit', 'content', 'all') || ($user->authorize('com_content', 'edit', 'content', 'own') && $model->get('created_by') == $user->get('id'));
                                }


                                // *******************************************************************************************************
                                // Check if user can not edit item further (due to changed main category, without edit/editown permission)
                                // *******************************************************************************************************
                                if (!$canEdit)
                                {
                                        if ($task=='apply') {
                                                // APPLY TASK: Temporarily set item to be editable till closing it
                                                $rendered_uneditable = $session->get('rendered_uneditable', array(),'flexicontent');
                                                $rendered_uneditable[$model->get('id')]  = 1;
                                                $session->set('rendered_uneditable', $rendered_uneditable, 'flexicontent');
                                                $canEdit = 1;
                                        }

                                        else if ( $newly_submitted_item ) {
                                                // NEW ITEM: Do not use editable till logoff behaviour
                                                // ALSO: Clear editable FLAG set in the case that 'apply' button was used during new item creation
                                                if ( !$params->get('items_session_editable', 0) ) {
                                                        $rendered_uneditable = $session->get('rendered_uneditable', array(),'flexicontent');
                                                        if ( isset($rendered_uneditable[$model->get('id')]) ) {
                                                                unset( $rendered_uneditable[$model->get('id')] );
                                                                $session->set('rendered_uneditable', $rendered_uneditable, 'flexicontent');
                                                        }
                                                }
                                        }

                                        else {
                                                // EXISTING ITEM: (if enabled) Use the editable till logoff behaviour
                                                if ( $params->get('items_session_editable', 0) ) {

                                                        // Set notice for existing item being editable till logoff 
                                                        JError::raiseNotice( 403, JText::_( 'FLEXI_CANNOT_EDIT_AFTER_LOGOFF' ) );

                                                        // Allow item to be editable till logoff
                                                        $rendered_uneditable = $session->get('rendered_uneditable', array(),'flexicontent');
                                                        $rendered_uneditable[$model->get('id')]  = 1;
                                                        $session->set('rendered_uneditable', $rendered_uneditable, 'flexicontent');
                                                        $canEdit = 1;
                                                }
                                        }

                                        // Set notice about saving an item that cannot be changed further
                                        if ( !$canEdit ) {
                                                $app->enqueueMessage(JText::_( 'FLEXI_CANNOT_MAKE_FURTHER_CHANGES_TO_CONTENT' ), 'message' );
                                        }
                                }


                                // ****************************************************************
                                // Check for new Content Item is being closed, and clear some flags
                                // ****************************************************************

                                if ($task!='apply' && $newly_submitted_item )
                                {
                                        // Clear item from being marked as newly submitted
                                        unset($newly_submitted[$model->get('id')]);
                                        $session->set('newly_submitted', $newly_submitted, 'flexicontent');

                                        // The 'apply' task may set 'editable till logoff' FLAG ...
                                        // CLEAR IT, since NEW content this is meant to be used temporarily
                                        if ( !$params->get('items_session_editable', 0) ) {
                                                $rendered_uneditable = $session->get('rendered_uneditable', array(),'flexicontent');
                                                if ( isset($rendered_uneditable[$model->get('id')]) ) {
                                                        unset( $rendered_uneditable[$model->get('id')] );
                                                        $session->set('rendered_uneditable', $rendered_uneditable, 'flexicontent');
                                                }
                                        }
                                }


                               
                }
		//$this->setRedirect($link, $msg);
	}