Alex Trofimov před 12 roky
rodič
revize
4ef880281b

+ 1 - 0
inc/classes/BxDol.php

@@ -68,6 +68,7 @@ interface iBxDolProfileService {
     public function serviceProfileIcon ($iContentId);
     public function serviceProfileName ($iContentId);
     public function serviceProfileUrl ($iContentId);
+    public function serviceProfilesSearch ($sTerm, $iLimit);
 }
 
 /** @} */

+ 4 - 2
inc/classes/BxDolModuleQuery.php

@@ -102,6 +102,8 @@ class BxDolModuleQuery extends BxDolDb implements iBxDolSingleton {
             $sWhereClause .= $this->prepare(" AND `enabled`=?", (int)$aParams['active']);
         }
 
+        $sOrderByClause = " ORDER BY " . (isset($aParams['order_by']) ? $aParams['order_by'] : '`title`');
+
         $sSql = "SELECT 
             	`id`,
             	`type`,
@@ -119,8 +121,8 @@ class BxDolModuleQuery extends BxDolDb implements iBxDolSingleton {
             	`date`, 
             	`enabled` 
         	FROM `sys_modules` 
-        	WHERE 1" . $sWhereClause . " 
-        	ORDER BY `title`";
+        	WHERE 1 " . $sWhereClause . 
+            $sOrderByClause;
         return $this->fromMemory('sys_modules' . $sPostfix, 'getAll', $sSql);
     }
 

+ 1 - 0
inc/classes/BxDolSearch.php

@@ -396,6 +396,7 @@ class BxDolSearchResult implements iBxDolReplaceable
             $aData = $this->getSearchDataByParams();
             return $aData;
         }
+        return array();
     }
 
     /**

+ 4 - 1
modules/base/text/classes/BxBaseModTextDb.php

@@ -16,14 +16,17 @@ bx_import('BxDolModuleDb');
  */
 class BxBaseModTextDb extends BxDolModuleDb
 {
+    protected $_oConfig;
+
     function __construct(&$oConfig) 
     {
         parent::__construct($oConfig);
+        $this->_oConfig = $oConfig;
     }
 
     function getContentInfoById ($iContentId) 
     {
-        $sQuery = $this->prepare ("SELECT `c`.* FROM `" . $this->_sPrefix . "posts` AS `c` WHERE `c`.`id` = ?", $iContentId);
+        $sQuery = $this->prepare ("SELECT `c`.* FROM `" . $this->_oConfig->CNF['TABLE_ENTRIES'] . "` AS `c` WHERE `c`.`id` = ?", $iContentId);
         return $this->getRow($sQuery);
     }
 }

+ 2 - 1
modules/base/text/classes/BxBaseModTextFormsEntryHelper.php

@@ -65,7 +65,8 @@ class BxBaseModTextFormsEntryHelper extends BxDolProfileForms
 
         // alert
         bx_import('BxDolPrivacy');
-        bx_alert($this->_oModule->getName(), 'added', $iContentId, false, array('privacy_view' => $aContentInfo[$CNF['FIELD_ALLOW_VIEW_TO']]));
+        $aParams = isset($aContentInfo[$CNF['FIELD_ALLOW_VIEW_TO']]) ? array('privacy_view' => $aContentInfo[$CNF['FIELD_ALLOW_VIEW_TO']]) : array();
+        bx_alert($this->_oModule->getName(), 'added', $iContentId, false, $aParams);
 
         // redirect 
         $this->_redirectAndExit('page.php?i=' . $CNF['URI_VIEW_ENTRY'] . '&id=' . $iContentId);

+ 1 - 1
modules/base/text/classes/BxBaseModTextTemplate.php

@@ -110,7 +110,7 @@ class BxBaseModTextTemplate extends BxDolModuleTemplate
         $CNF = &$oModule->_oConfig->CNF;
 
         $aVars = $aData;
-        $aVars['entry_title'] = $aData[$CNF['FIELD_TITLE']];
+        $aVars['entry_title'] = isset($aData[$CNF['FIELD_TITLE']]) ? $aData[$CNF['FIELD_TITLE']] : '';
         $aVars['entry_text'] = $aData[$CNF['FIELD_TEXT']];
 
         return $this->parseHtmlByName($sTemplateName, $aVars);

+ 3 - 0
modules/boonex/messages/classes/BxMsgConfig.php

@@ -19,6 +19,9 @@ class BxMsgConfig extends BxBaseModTextConfig
 
         $this->CNF = array (
 
+            // database tables
+            'TABLE_ENTRIES' => $aModule['db_prefix'] . 'conversations',
+
             // database fields
             'FIELD_ID' => 'id',
             'FIELD_AUTHOR' => 'author',

+ 7 - 1
modules/boonex/messages/classes/BxMsgDb.php

@@ -16,10 +16,16 @@ bx_import('BxBaseModTextDb');
  */
 class BxMsgDb extends BxBaseModTextDb 
 {
-    function __construct(&$oConfig) 
+    public function __construct(&$oConfig) 
     {
         parent::__construct($oConfig);
     }
+
+    public function conversationToFolder($iConversationId, $iFolderId, $iProfileCollaborator, $iReadCommentsNum = -1) 
+    {
+        $sQuery = $this->prepare("INSERT INTO `" . $this->getPrefix() . "conv2folder` SET `conv_id` = ?, `folder_id` = ?, `collaborator` = ?, `read_comments` = ?", $iConversationId, $iFolderId, $iProfileCollaborator, $iReadCommentsNum);
+        return $this->query($sQuery);
+    }
 }
 
 /** @} */ 

+ 30 - 2
modules/boonex/messages/classes/BxMsgFormEntry.php

@@ -27,11 +27,39 @@ class BxMsgFormEntry extends BxBaseModTextFormEntry
         BxDolTemplate::getInstance()->addJs($aJs);
     }
 
+    public function insert ($aValsToAdd = array(), $isIgnore = false) 
+    {
+        $iContentId = parent::insert ($aValsToAdd, $isIgnore);
+        if (!$iContentId)
+            return 0;
+
+        bx_import('BxDolProfile');
+
+        // for author place conversation to "sent" folder
+        $this->_oModule->_oDb->conversationToFolder($iContentId, BX_MSG_FOLDER_SENT, bx_get_logged_profile_id(), 0);
+
+        // check for spam
+        $bSpam = false;
+        bx_alert('system', 'check_spam', 0, getLoggedId(), array('is_spam' => &$bSpam, 'content' => $this->getCleanValue('text'), 'where' => 'messages'));
+        $iFolder = $bSpam ? BX_MSG_FOLDER_SPAM : BX_MSG_FOLDER_INBOX;
+
+        // for all collaborators to "inbox" or "spam"
+        $aRecipients = array_unique($this->getCleanValue('recipients'), SORT_NUMERIC);
+        foreach ($aRecipients as $iProfile) {
+            $oProfile = BxDolProfile::getInstance($iProfile);
+            if ($oProfile)
+                $this->_oModule->_oDb->conversationToFolder($iContentId, $iFolder, $oProfile->id());
+        }
+
+        return $iContentId;
+    }
+
     protected function genCustomInputRecipients ($aInput) {
         $sVals = '';
         if (!empty($aInput['value']) && is_array($aInput['value'])) {
-            foreach ($aInput['value'] as $sVal)
-               $sVals .= '<span class="bx-def-color-bg-hl bx-def-round-corners">' . $sVal . '<input type="hidden" name="' . $aInput['name'] . '[]" value="' . $sVal . '" /></span>';
+            foreach ($aInput['value'] as $sVal) {
+               $sVals .= '<span class="bx-def-color-bg-hl bx-def-round-corners">' . BxDolProfile::getInstance($sVal)->getDisplayName() . '<input type="hidden" name="' . $aInput['name'] . '[]" value="' . $sVal . '" /></span>';
+            }
             $sVals = trim($sVals, ',');
         }
         $sId = $aInput['name'] . time();

+ 8 - 6
modules/boonex/messages/classes/BxMsgModule.php

@@ -11,6 +11,12 @@
 
 bx_import ('BxBaseModTextModule');
 
+define('BX_MSG_FOLDER_INBOX', 1);
+define('BX_MSG_FOLDER_SENT', 2);
+define('BX_MSG_FOLDER_DRAFTS', 3);
+define('BX_MSG_FOLDER_SPAM', 4);
+define('BX_MSG_FOLDER_TRASH', 5);
+
 /**
  * Messages module
  */
@@ -24,13 +30,9 @@ class BxMsgModule extends BxBaseModTextModule
     public function actionAjaxGetRecipients () 
     {
         $sTerm = bx_get('term');
-        
-        $a = array (
-            array ('label' => 'aaa', 'value' => 1),
-            array ('label' => 'bbb', 'value' => 2),
-            array ('label' => 'ccc', 'value' => 3),
-        );
 
+        $a = BxDolService::call('system', 'profiles_search', array($sTerm), 'TemplServiceProfiles');
+        
         header('Content-Type:text/javascript');
         echo(json_encode($a));
     }

+ 6 - 0
modules/boonex/messages/install/langs/en.xml

@@ -68,4 +68,10 @@
 
     <string name="_bx_msg_txt_sample_single"><![CDATA[message]]></string>
 
+    <string name="_bx_msg_folder_inbox"><![CDATA[Inbox]]></string>
+    <string name="_bx_msg_folder_sent"><![CDATA[Sent]]></string>
+    <string name="_bx_msg_folder_drafts"><![CDATA[Drafts]]></string>
+    <string name="_bx_msg_folder_spam"><![CDATA[Spam]]></string>
+    <string name="_bx_msg_folder_trash"><![CDATA[Trash]]></string>
+
 </resources>

+ 2 - 2
modules/boonex/messages/install/sql/enable.sql

@@ -158,9 +158,9 @@ INSERT INTO `sys_acl_matrix` (`IDLevel`, `IDAction`) VALUES
 
 -- COMMENTS
 INSERT INTO `sys_objects_cmts` (`Name`, `Table`, `CharsPostMin`, `CharsPostMax`, `CharsDisplayMax`, `Nl2br`, `PerView`, `PerViewReplies`, `BrowseType`, `IsBrowseSwitch`, `PostFormPosition`, `NumberOfLevels`, `IsDisplaySwitch`, `IsRatable`, `ViewingThreshold`, `IsOn`, `RootStylePrefix`, `BaseUrl`, `ObjectVote`, `TriggerTable`, `TriggerFieldId`, `TriggerFieldTitle`, `TriggerFieldComments`, `ClassName`, `ClassFile`) VALUES
-('bx_messages', 'bx_messages_cmts', 1, 5000, 1000, 1, 5, 3, 'tail', 1, 'bottom', 1, 1, 0, -3, 1, 'cmt', 'page/view-message&id={object_id}', '', 'bx_messages_msg', 'id', '', 'comments', '', '');
+('bx_messages', 'bx_messages_cmts', 1, 5000, 1000, 1, 5, 3, 'tail', 1, 'bottom', 1, 1, 0, -3, 1, 'cmt', 'page/view-message&id={object_id}', '', 'bx_messages_conversations', 'id', '', 'comments', '', '');
 
 -- VIEWS
 INSERT INTO `sys_objects_view` (`name`, `table_track`, `period`, `is_on`, `trigger_table`, `trigger_field_id`, `trigger_field_count`, `class_name`, `class_file`) VALUES 
-('bx_messages', 'bx_messages_views_track', '86400', '1', 'bx_messages_msg', 'id', 'views', '', '');
+('bx_messages', 'bx_messages_views_track', '86400', '1', 'bx_messages_conversations', 'id', 'views', '', '');
 

+ 18 - 20
modules/boonex/messages/install/sql/install.sql

@@ -1,7 +1,7 @@
 
 -- TABLE: entries
 
-CREATE TABLE IF NOT EXISTS `bx_messages_msg` (
+CREATE TABLE IF NOT EXISTS `bx_messages_conversations` (
   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
   `author` int(10) unsigned NOT NULL,
   `added` int(11) NOT NULL,
@@ -12,33 +12,31 @@ CREATE TABLE IF NOT EXISTS `bx_messages_msg` (
   PRIMARY KEY (`id`)
 );
 
-CREATE TABLE IF NOT EXISTS `bx_messages_conversations` (
+CREATE TABLE IF NOT EXISTS `bx_messages_conv2folder` (
   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
-  `msg_id` int(10) unsigned DEFAULT NULL,
-  `collaborators` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
+  `conv_id` int(10) unsigned NOT NULL,
+  `folder_id` int(10) unsigned NOT NULL,
+  `collaborator` int(10) unsigned NOT NULL,
+  `read_comments` int(11) NOT NULL default '-1',
   PRIMARY KEY (`id`),
-  KEY `msg_id` (`msg_id`),
-  KEY `collaborators` (`collaborators`)
+  UNIQUE KEY `collaborator_folder_conv` (`collaborator`,`folder_id`, `conv_id`),
+  KEY `conv_id` (`conv_id`)
 );
 
 CREATE TABLE IF NOT EXISTS `bx_messages_folders` (
   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
   `author` int(10) unsigned NOT NULL,
-  `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
+  `name` varchar(255) NOT NULL DEFAULT '',
   PRIMARY KEY (`id`),
   KEY `author` (`author`)
 );
 
-CREATE TABLE IF NOT EXISTS `bx_messages_conv2folder` (
-  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
-  `conv_id` int(10) unsigned NOT NULL,
-  `folder_id` int(10) unsigned NOT NULL,
-  `collaborator` int(10) unsigned NOT NULL,
-  `read` tinyint(4) NOT NULL DEFAULT '0',
-  PRIMARY KEY (`id`),
-  KEY `collaborator_folder` (`collaborator`,`folder_id`),
-  KEY `conv_id` (`conv_id`)
-);
+INSERT INTO `bx_messages_folders` (`id`, `author`, `name`) VALUES
+(1, 0, '_bx_msg_folder_inbox'),
+(2, 0, '_bx_msg_folder_sent'),
+(3, 0, '_bx_msg_folder_drafts'),
+(4, 0, '_bx_msg_folder_spam'),
+(5, 0, '_bx_msg_folder_trash');
 
 -- TABLE: storages & transcoders
 
@@ -101,12 +99,12 @@ CREATE TABLE `bx_messages_views_track` (
   `viewer_nip` int(11) unsigned NOT NULL default '0',
   `date` int(11) NOT NULL default '0',
   KEY `id` (`object_id`,`viewer_id`,`viewer_nip`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+);
 
 -- FORMS
 
 INSERT INTO `sys_objects_form`(`object`, `module`, `title`, `action`, `form_attrs`, `table`, `key`, `uri`, `uri_title`, `submit_name`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
-('bx_messages', 'bx_messages', '_bx_msg_form_entry', '', 'a:1:{s:7:\"enctype\";s:19:\"multipart/form-data\";}', 'bx_messages_msg', 'id', '', '', 'do_submit', '', 0, 1, 'BxMsgFormEntry', 'modules/boonex/messages/classes/BxMsgFormEntry.php');
+('bx_messages', 'bx_messages', '_bx_msg_form_entry', '', 'a:1:{s:7:\"enctype\";s:19:\"multipart/form-data\";}', 'bx_messages_conversations', 'id', '', '', 'do_submit', '', 0, 1, 'BxMsgFormEntry', 'modules/boonex/messages/classes/BxMsgFormEntry.php');
 
 INSERT INTO `sys_form_displays`(`object`, `display_name`, `module`, `view_mode`, `title`) VALUES 
 ('bx_messages', 'bx_messages_entry_add', 'bx_messages', 0, '_bx_msg_form_entry_display_add'),
@@ -117,7 +115,7 @@ INSERT INTO `sys_form_inputs`(`object`, `module`, `name`, `value`, `values`, `ch
 ('bx_messages', 'bx_messages', 'delete_confirm', 1, '', 0, 'checkbox', '_bx_msg_form_entry_input_sys_delete_confirm', '_bx_msg_form_entry_input_delete_confirm', '_bx_msg_form_entry_input_delete_confirm_info', 1, 0, 0, '', '', '', 'Avail', '', '_bx_msg_form_entry_input_delete_confirm_error', '', '', 1, 0),
 ('bx_messages', 'bx_messages', 'do_submit', '_bx_msg_form_entry_input_do_submit', '', 0, 'submit', '_bx_msg_form_entry_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
 ('bx_messages', 'bx_messages', 'text', '', '', 0, 'textarea', '_bx_msg_form_entry_input_sys_text', '_bx_msg_form_entry_input_text', '', 1, 0, 2, '', '', '', 'Avail', '', '_bx_msg_form_entry_input_text_err', 'XssHtml', '', 1, 0),
-('bx_messages', 'bx_messages', 'recipients', '', '', 0, 'custom', '_bx_msg_form_entry_input_sys_recipients', '_bx_msg_form_entry_input_recipients', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_msg_form_entry_input_recipients_err', 'Xss', '', 1, 0),
+('bx_messages', 'bx_messages', 'recipients', '', '', 0, 'custom', '_bx_msg_form_entry_input_sys_recipients', '_bx_msg_form_entry_input_recipients', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_msg_form_entry_input_recipients_err', '', '', 1, 0),
 ('bx_messages', 'bx_messages', 'pictures', '', '', 0, 'files', '_bx_msg_form_entry_input_sys_pictures', '_bx_msg_form_entry_input_pictures', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0);
 
 INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES

+ 1 - 1
modules/boonex/messages/install/sql/uninstall.sql

@@ -1,7 +1,7 @@
 
 -- TABLE: entries
 
-DROP TABLE IF EXISTS `bx_messages_msg`, `bx_messages_conversations`, `bx_messages_folders`, `bx_messages_conv2folder`;
+DROP TABLE IF EXISTS `bx_messages_conversations`, `bx_messages_conv2folder`, `bx_messages_folders`;
 
 -- TABLE: storages & transcoders
 

+ 1 - 0
modules/boonex/messages/template/css/main.css

@@ -55,6 +55,7 @@
 }
 
 /* view page */
+.bx-notes-entity-content p:first-child,
 .bx-notes-entity-content h1:first-child {
     margin-top:0;
 }

+ 0 - 1
modules/boonex/messages/template/entry-text.html

@@ -1,6 +1,5 @@
 
 <div class="bx-notes-entity-content bx-def-constraint-list bx-def-constraint-pre bx-def-constraint-object bx-def-constraint-iframe bx-def-constraint-img">
-    <h1>__title__</h1>
     __text__
 </div>
 

+ 3 - 0
modules/boonex/notes/classes/BxNotesConfig.php

@@ -19,6 +19,9 @@ class BxNotesConfig extends BxBaseModTextConfig
 
         $this->CNF = array (
 
+            // database tables
+            'TABLE_ENTRIES' => $aModule['db_prefix'] . 'posts',
+
             // database fields
             'FIELD_ID' => 'id',
             'FIELD_AUTHOR' => 'author',

+ 13 - 5
modules/boonex/persons/classes/BxPersonsDb.php

@@ -14,25 +14,33 @@ bx_import('BxDolModuleDb');
 /*
  * Persons module database queries
  */
-class BxPersonsDb extends BxDolModuleDb {
-
+class BxPersonsDb extends BxDolModuleDb 
+{
     /*
      * Constructor.
      */
-    function __construct(&$oConfig) {
+    public function __construct(&$oConfig) 
+    {
         parent::__construct($oConfig);
     }
 
-    function getContentInfoById ($iContentId) {
+    public function getContentInfoById ($iContentId) 
+    {
         $sQuery = $this->prepare ("SELECT `c`.*, `p`.`account_id`, `p`.`id` AS `profile_id`, `p`.`status` AS `profile_status` FROM `" . $this->_sPrefix . "data` AS `c` INNER JOIN `sys_profiles` AS `p` ON (`p`.`content_id` = `c`.`id` AND `p`.`type` = 'bx_persons') WHERE `c`.`id` = ?", $iContentId);
         return $this->getRow($sQuery);
     }
 
-    function updateContentPictureById($iContentId, $iProfileId, $iPictureId, $sFieldPicture) {
+    public function updateContentPictureById($iContentId, $iProfileId, $iPictureId, $sFieldPicture) 
+    {
         $sQuery = $this->prepare ("UPDATE `" . $this->_sPrefix . "data` SET `" . $sFieldPicture . "` = ? WHERE `id` = ? AND `author` = ?", $iPictureId, $iContentId, $iProfileId);
         return $this->res($sQuery);
     }
 
+    public function searchByTerm($sTerm, $iLimit) 
+    {
+        $sQuery = $this->prepare("SELECT `c`.`id` AS `content_id`, `p`.`account_id`, `p`.`id` AS `profile_id`, `p`.`status` AS `profile_status` FROM `" . $this->_sPrefix . "data` AS `c` INNER JOIN `sys_profiles` AS `p` ON (`p`.`content_id` = `c`.`id` AND `p`.`type` = 'bx_persons') WHERE `p`.`status` = ? AND `c`.`fullname` LIKE ? ORDER BY `added` DESC LIMIT ?", BX_PROFILE_STATUS_ACTIVE, '%' . $sTerm . '%', (int)$iLimit);
+        return $this->getAll($sQuery);
+    }
 }
 
 /** @} */ 

+ 8 - 0
modules/boonex/persons/classes/BxPersonsModule.php

@@ -30,6 +30,14 @@ class BxPersonsModule extends BxDolModule implements iBxDolProfileService {
 
     // ====== SERVICE METHODS
 
+    public function serviceProfilesSearch ($sTerm, $iLimit) {
+        $aRet = array();
+        $a = $this->_oDb->searchByTerm($sTerm, $iLimit);
+        foreach ($a as $r)
+            $aRet[] = array ('label' => $this->serviceProfileName($r['content_id']), 'value' => $r['profile_id']);
+        return $aRet;
+    }
+
     public function serviceProfileUnit ($iContentId) {
         return $this->_serviceTemplateFunc('unit', $iContentId);
     }

+ 2 - 2
studio/classes/BxDolStudioBuilderPage.php

@@ -149,11 +149,11 @@ class BxDolStudioBuilderPage extends BxTemplStudioPage {
         return BxDolStudioUtils::addInArray($aInput, $sKey, $aValues);
     }
 
-    protected function getModuleIcon($sName) {
+    protected function getModuleIcon($sName, $sType = 'menu') {
         if(in_array($sName, array(BX_DOL_STUDIO_MODULE_SYSTEM, BX_DOL_STUDIO_MODULE_CUSTOM, BX_DOL_STUDIO_BP_SKELETONS))) 
             return BxDolStudioTemplate::getInstance()->getIconUrl('mi-bp-' . $sName . '.png');
 
         return BxDolStudioUtils::getModuleIcon($sName);
     }
 }
-/** @} */
+/** @} */

+ 85 - 6
template/scripts/BxBaseServiceProfiles.php

@@ -10,14 +10,69 @@
 /**
  * System service for profiles handling functionality.
  */
-class BxBaseServiceProfiles extends BxDol {
-
-    public function __construct() {
+class BxBaseServiceProfiles extends BxDol 
+{
+    public function __construct() 
+    {
         parent::__construct();
     }
 
-    public function serviceProfilesList ($iAccountId = 0) {
+    public function serviceGetProfilesModules () 
+    {
+        if (getParam('sys_db_cache_enable')) { // get list of profiles  modules from db cache, cache is invalidated when new module is installed
+
+            $oCache = $this->getDbCacheObject ();
+
+            $sKey = $this->genDbCacheKey('profiles_modules_array');
+            $sKeyTs = $this->genDbCacheKey('profiles_modules_ts');
+
+            $mixedRetTs = $oCache->getData($sKeyTs);
+            $mixedRet = $oCache->getData($sKey);
+
+            $iNewestModuleTs = $this->_getLatestModuleTimestamp ();
+
+            if ($mixedRetTs != null && $mixedRet !== null && $mixedRetTs == $iNewestModuleTs) {
+
+                $aModulesArray = $mixedRet;
+
+            } else {
+
+                $aModulesArray = $this->_getProfilesModules ();
+
+                $oCache->setData($sKey, $aModulesArray);
+                $oCache->setData($sKeyTs, $iNewestModuleTs);
+            }
+
+        } else {
+
+            $aModulesArray = $this->_getProfilesModules ();
+
+        }
 
+        return $aModulesArray;
+    }
+
+    public function serviceProfilesSearch ($sTerm, $iLimit = 20) 
+    {
+        // get list of "profiles" modules
+        $aModules = $this->serviceGetProfilesModules();
+
+        // search in each module
+        $a = array();
+        foreach ($aModules as $aModule)
+            $a = array_merge($a, BxDolService::call($aModule['name'], 'profiles_search', array($sTerm, 10)));
+
+        // sort result
+        usort($a, function($r1, $r2) {
+            return strcmp($r1['label'], $r2['label']);
+        });
+
+        // return as array
+        return array_slice($a, 0, $iLimit);
+    }
+
+    public function serviceProfilesList ($iAccountId = 0) 
+    {
         bx_import('BxDolProfileQuery');
         $oProfilesQuery = BxDolProfileQuery::getInstance();
 
@@ -34,10 +89,10 @@ class BxBaseServiceProfiles extends BxDol {
             $s = MsgBox(_t('_sys_txt_empty'));
 
         return $s . '<div class="bx-clear"></div>';
-
     }
 
-    public function serviceAccountProfileSwitcher ($iAccountId = false) {
+    public function serviceAccountProfileSwitcher ($iAccountId = false) 
+    {
         bx_import('BxDolProfileQuery');
         $oProfilesQuery = BxDolProfileQuery::getInstance();
 
@@ -74,6 +129,30 @@ class BxBaseServiceProfiles extends BxDol {
         return $oTemplate->parseHtmlByName('profile_switch_row.html', $aVars);
     }
 
+    protected function _getLatestModuleTimestamp () 
+    {
+        $aRet = array();
+        bx_import('BxDolModuleQuery');
+        $aModules = BxDolModuleQuery::getInstance()->getModulesBy(array('type' => 'modules', 'active' => 1, 'order_by' => '`date` ASC'));
+        if (empty($aModules))
+            return 0;
+        $aModuleNewest = array_pop($aModules);
+        return $aModuleNewest['date'];
+    }
+
+    protected function _getProfilesModules () 
+    {
+        $aRet = array();
+        bx_import('BxDolModuleQuery');
+        $aModules = BxDolModuleQuery::getInstance()->getModulesBy(array('type' => 'modules', 'active' => 1));
+        foreach ($aModules as $aModule) {
+            $oModule = BxDolModule::getInstance($aModule['name']);
+            if ($oModule instanceof iBxDolProfileService)
+                $aRet[] = $aModule;
+        }
+        return $aRet;
+    }
+
 }
 
 /** @} */

+ 4 - 0
template/scripts/BxBaseServices.php

@@ -15,6 +15,10 @@ class BxBaseServices extends BxDol implements iBxDolProfileService {
         parent::__construct();
     }
 
+    public function serviceProfilesSearch ($sTerm, $iLimit) {
+        // TODO: profiles search 
+    }
+
     public function serviceProfileUnit ($iContentId) {
         return $this->_serviceProfileFunc('getUnit', $iContentId);
     }