Explorar o código

Ticket #3073 - Resolve conflict.

AntonLV %!s(int64=5) %!d(string=hai) anos
pai
achega
861bca0057
Modificáronse 100 ficheiros con 2117 adicións e 157 borrados
  1. 10 0
      inc/classes/BxDolForm.php
  2. 19 4
      inc/classes/BxDolGrid.php
  3. 149 0
      inc/classes/BxDolLogs.php
  4. 33 0
      inc/classes/BxDolLogsQuery.php
  5. 137 0
      inc/classes/BxDolLogsStorageFolder.php
  6. 64 0
      inc/classes/BxDolLogsStoragePHPLog.php
  7. 70 0
      inc/classes/BxDolLogsStorageSTDErr.php
  8. 1 1
      inc/classes/BxDolPush.php
  9. 3 0
      inc/classes/BxDolReportQuery.php
  10. 8 1
      inc/classes/BxDolStorage.php
  11. 33 14
      inc/classes/BxDolStorageS3.php
  12. 29 0
      inc/classes/BxDolStorageS3v4.php
  13. 168 0
      inc/classes/BxDolStorageS3v4alt.php
  14. 38 9
      inc/classes/BxDolTemplate.php
  15. 1 3
      inc/classes/BxDolTwilio.php
  16. 1 0
      inc/js/classes/BxDolGrid.js
  17. 21 10
      inc/utils.inc.php
  18. 0 0
      install/sql/system.sql
  19. 9 0
      modules/base/general/classes/BxBaseModGeneralMenuViewActions.php
  20. 6 22
      modules/base/payment/classes/BxBaseModPaymentProvider.php
  21. 19 11
      modules/base/profile/classes/BxBaseModProfileFormsEntryHelper.php
  22. 15 6
      modules/base/profile/classes/BxBaseModProfileGridAdministration.php
  23. 4 1
      modules/base/profile/classes/BxBaseModProfileMenuManageTools.php
  24. 34 0
      modules/base/profile/classes/BxBaseModProfileModule.php
  25. 2 0
      modules/base/profile/classes/BxBaseModProfileTemplate.php
  26. 3 0
      modules/base/text/classes/BxBaseModTextGridAdministration.php
  27. 9 0
      modules/boonex/accounts/classes/BxAccntModule.php
  28. 2 2
      modules/boonex/accounts/install/sql/enable.sql
  29. 1 1
      modules/boonex/accounts/js/main.js
  30. 1 0
      modules/boonex/albums/install/langs/en.xml
  31. 2 0
      modules/boonex/albums/install/sql/enable.sql
  32. 1 0
      modules/boonex/classes/install/langs/en.xml
  33. 2 0
      modules/boonex/classes/install/sql/enable.sql
  34. 3 0
      modules/boonex/courses/install/langs/en.xml
  35. 3 1
      modules/boonex/courses/install/sql/enable.sql
  36. 6 0
      modules/boonex/courses/js/manage_tools.js
  37. 7 9
      modules/boonex/elasticsearch/classes/BxElsApi.php
  38. 2 0
      modules/boonex/elasticsearch/install/langs/en.xml
  39. 4 0
      modules/boonex/elasticsearch/install/sql/install.sql
  40. 2 0
      modules/boonex/elasticsearch/install/sql/uninstall.sql
  41. 11 1
      modules/boonex/english/data/langs/system/en.xml
  42. 3 0
      modules/boonex/events/install/langs/en.xml
  43. 3 1
      modules/boonex/events/install/sql/enable.sql
  44. 6 0
      modules/boonex/events/js/manage_tools.js
  45. 14 8
      modules/boonex/facebook_connect/classes/BxFaceBookConnectModule.php
  46. 7 0
      modules/boonex/facebook_connect/classes/BxFaceBookConnectStudioPage.php
  47. 27 2
      modules/boonex/files/classes/BxFilesConfig.php
  48. 40 0
      modules/boonex/files/classes/BxFilesDb.php
  49. 5 0
      modules/boonex/files/classes/BxFilesFormEntry.php
  50. 7 0
      modules/boonex/files/classes/BxFilesMenuSnippetMeta.php
  51. 132 0
      modules/boonex/files/classes/BxFilesMenuViewActions.php
  52. 124 0
      modules/boonex/files/classes/BxFilesModule.php
  53. 249 1
      modules/boonex/files/classes/BxFilesSearchResult.php
  54. 76 2
      modules/boonex/files/classes/BxFilesTemplate.php
  55. 31 0
      modules/boonex/files/install/langs/en.xml
  56. 1 1
      modules/boonex/files/install/sql/disable.sql
  57. 29 5
      modules/boonex/files/install/sql/enable.sql
  58. 7 0
      modules/boonex/files/install/sql/install.sql
  59. 1 1
      modules/boonex/files/install/sql/uninstall.sql
  60. 48 0
      modules/boonex/files/js/menu_tools.js
  61. 57 0
      modules/boonex/files/js/toolbar_tools.js
  62. 116 2
      modules/boonex/files/template/css/main.css
  63. 13 0
      modules/boonex/files/template/entry-popup.html
  64. 43 0
      modules/boonex/files/template/files_browser_toolbar.html
  65. 17 11
      modules/boonex/files/template/unit_gallery.html
  66. 15 0
      modules/boonex/files/template/unit_simple_row.html
  67. 1 0
      modules/boonex/forum/install/langs/en.xml
  68. 2 0
      modules/boonex/forum/install/sql/enable.sql
  69. 1 0
      modules/boonex/glossary/install/langs/en.xml
  70. 2 0
      modules/boonex/glossary/install/sql/enable.sql
  71. 3 0
      modules/boonex/groups/install/langs/en.xml
  72. 3 1
      modules/boonex/groups/install/sql/enable.sql
  73. 7 0
      modules/boonex/groups/js/manage_tools.js
  74. 1 1
      modules/boonex/invites/js/main.js
  75. 1 0
      modules/boonex/market/install/langs/en.xml
  76. 2 0
      modules/boonex/market/install/sql/enable.sql
  77. 5 2
      modules/boonex/massmailer/classes/BxMassMailerModule.php
  78. 1 1
      modules/boonex/massmailer/install/sql/enable.sql
  79. 3 2
      modules/boonex/oauth2/classes/BxOAuthModule.php
  80. 6 5
      modules/boonex/oauth2/classes/BxOAuthStudioPage.php
  81. 2 0
      modules/boonex/oauth2/install/langs/en.xml
  82. 3 2
      modules/boonex/oauth2/install/sql/enable.sql
  83. 5 1
      modules/boonex/organizations/classes/BxOrgsConfig.php
  84. 1 1
      modules/boonex/organizations/classes/BxOrgsMenuViewActionsAll.php
  85. 9 2
      modules/boonex/organizations/install/langs/en.xml
  86. 5 3
      modules/boonex/organizations/install/sql/enable.sql
  87. 1 1
      modules/boonex/organizations/install/sql/install.sql
  88. 5 0
      modules/boonex/organizations/js/manage_tools.js
  89. 8 0
      modules/boonex/payment/classes/BxPaymentModule.php
  90. 1 1
      modules/boonex/persons/classes/BxPersonsMenuViewActionsAll.php
  91. 4 1
      modules/boonex/persons/install/langs/en.xml
  92. 4 2
      modules/boonex/persons/install/sql/enable.sql
  93. 1 1
      modules/boonex/persons/install/sql/install.sql
  94. 6 0
      modules/boonex/persons/js/manage_tools.js
  95. 1 0
      modules/boonex/photos/install/langs/en.xml
  96. 2 0
      modules/boonex/photos/install/sql/enable.sql
  97. 1 0
      modules/boonex/polls/install/langs/en.xml
  98. 2 0
      modules/boonex/polls/install/sql/enable.sql
  99. 3 0
      modules/boonex/posts/classes/BxPostsConfig.php
  100. 11 0
      modules/boonex/posts/classes/BxPostsFormEntry.php

+ 10 - 0
inc/classes/BxDolForm.php

@@ -1570,6 +1570,16 @@ class BxDolFormCheckerHelper
         }
         return $s ? true : false;
     }
+    static public function checkProfileName($s)
+    {
+        if (!self::checkAvail($s))
+            return false;
+        
+        if (self::checkPreg ($s, '#^@.+$#')){
+            return false;
+        }
+        return true;
+    }
 
     static public function checkLocation($s, $sName, $sMethod = BX_DOL_FORM_METHOD_DEFAULT, $bAll = false)
     {

+ 19 - 4
inc/classes/BxDolGrid.php

@@ -248,6 +248,7 @@ class BxDolGrid extends BxDolFactory implements iBxDolFactoryObject, iBxDolRepla
 
     protected $_aBrowseParams;
     protected $_sDefaultSortingOrder = 'ASC';
+    protected $_iTotalCount = 0;
 
     /**
      * Constructor
@@ -336,7 +337,7 @@ class BxDolGrid extends BxDolFactory implements iBxDolFactoryObject, iBxDolRepla
         $sFunc = '_getData' . $this->_aOptions['source_type'];
         return $this->$sFunc($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);
     }
-
+    
     protected function _getDataArray ($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
     {
         if ($this->_aOptions['source'] && !is_array($this->_aOptions['source'])) {
@@ -389,7 +390,11 @@ class BxDolGrid extends BxDolFactory implements iBxDolFactoryObject, iBxDolRepla
         } else {
             $aSourceOrdered = &$aSource;
         }
-
+        
+        // calculate total records count
+        if ($this->_aOptions['show_total_count'] == 1){
+            $this->_iTotalCount =  count($aSourceOrdered);
+        }
         return array_slice($aSourceOrdered, $iStart, $iPerPage, true);
     }
 
@@ -397,7 +402,6 @@ class BxDolGrid extends BxDolFactory implements iBxDolFactoryObject, iBxDolRepla
    {
         $oDb = BxDolDb::getInstance();
         $sQuery = $this->_aOptions['source'];
-
         if (false === stripos($sQuery, ' WHERE '))
             $sQuery .= " WHERE 1 ";
 
@@ -410,13 +414,24 @@ class BxDolGrid extends BxDolFactory implements iBxDolFactoryObject, iBxDolRepla
         $sOrderByFilter = '';
         $sQuery .= $this->_getDataSqlWhereClause($sFilter, $sOrderByFilter);
 
+        // calculate total records count
+        if ($this->_aOptions['show_total_count'] == 1){
+             $this->_iTotalCount = $this->_getDataSqCounter($sQuery, $sFilter);
+        }
+        
         // add order
         $sQuery .= $this->_getDataSqlOrderClause ($sOrderByFilter, $sOrderField, $sOrderDir);
-
+        
         $sQuery = $sQuery . $oDb->prepareAsString(' LIMIT ?, ?', $iStart, $iPerPage);
         return $oDb->getAll($sQuery);
     }
 
+    protected function _getDataSqCounter($sQuery, $sFilter)
+    {
+        $oDb = BxDolDb::getInstance();
+        return $oDb->getOne("SELECT COUNT(*) " . substr($sQuery, strpos($sQuery, " FROM" )));
+    }
+   
     protected function _getDataSqlWhereClause($sFilter, &$sOrderByFilter)
     {
         if(!$sFilter || (empty($this->_aOptions['filter_fields']) && empty($this->_aOptions['filter_fields_translatable']))) 

+ 149 - 0
inc/classes/BxDolLogs.php

@@ -0,0 +1,149 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+/**
+ * This class unifies the usage of logs.
+ *
+ * Add record to sys_objects_logs table, like you are doing this for Comments or Voting objects:
+ * - object: your logs object name, usually it is in the following format - vendor prefix, underscore, module prefix;
+ * - module: module name
+ * - logs_storage: logs storage, supported storages: 
+ *      - `Auto`: log to the storage currently configured in settings
+ *      - `Folder`: files in `logs` folder
+ *      - `PHPLog`: php log
+ *      - `STDErr`: standard error output, usually written to web server error log
+ * - title: translatable title
+ * - active: 0 or 1
+ * - class_name: user defined class name which is derived from BxDolLogs.
+ * - class_file: the location of the user defined class, leave it empty if class is located in system folders.
+ * 
+ * @section example Example of usage
+ *
+ * Log data to the logs object
+ *
+ * @code
+ *  if ($o = BxDolLogs::getObjectInstance('my_module_logs_object'))
+ *      $o->add($s);
+ * @endcode
+ *
+ * or
+ *
+ * @code
+ *  bx_log('my_module_logs_object', $s);
+ * @endcode
+ *
+ */
+class BxDolLogs extends BxDolFactory implements iBxDolFactoryObject
+{
+	protected $_oDb;
+	protected $_sObject;
+    protected $_aObject;
+    protected $_oLogsStorage;
+
+    /**
+     * Constructor
+     */
+    protected function __construct($aObject, $oLogsStorage)
+    {
+        parent::__construct();
+
+        $this->_aObject = $aObject;
+        $this->_sObject = $aObject['object'];
+
+        $this->_oLogsStorage = $oLogsStorage;
+        $this->_oDb = new BxDolLogsQuery($this->_aObject);
+    }
+
+   /**
+     * Get logs object instance by object name
+     * @param $sObject object name
+     * @return object instance or false on error
+     */
+    public static function getObjectInstance($sObject)
+    {
+        if(isset($GLOBALS['bxDolClasses']['BxDolLogs!' . $sObject]))
+            return $GLOBALS['bxDolClasses']['BxDolLogs!' . $sObject];
+
+        $aObject = BxDolLogsQuery::getLogsObject($sObject);
+        if($aObject && is_array($aObject) && !$aObject['active'])
+            return false;
+        if(!$aObject) {
+            $aObject = array(
+                'object' => $sObject,
+                'module' => 'system',
+                'logs_storage' => getParam('sys_logs_storage_default'),
+            );
+        }
+
+        $sClass = 'BxDolLogs';
+        if(!empty($aObject['class_name'])) {
+            $sClass = $aObject['class_name'];
+            if(!empty($aObject['class_file']))
+                require_once(BX_DIRECTORY_PATH_ROOT . $aObject['class_file']);
+        }        
+
+        $sLogsStorage = (empty($aObject['logs_storage']) ? 'Auto' : $aObject['logs_storage']);
+        if ('Auto' == $sLogsStorage)
+            $sLogsStorage = getParam('sys_logs_storage_default');
+        $sClassLogsStorage = 'BxDolLogsStorage' . $sLogsStorage;
+        $oLogsStorage = $sClassLogsStorage::getInstance();
+
+        $o = new $sClass($aObject, $oLogsStorage);
+        return ($GLOBALS['bxDolClasses']['BxDolLogs!' . $sObject] = $o);
+    }
+
+    /**
+     * Get current logs object name
+     */
+    public function getObjectName()
+    {
+        return $this->_aObject['object'];
+    }
+
+    /**
+     * Log to the current logs storage object
+     * @param $mixed string or array to log
+     * @return true on success or false on error
+     */
+    public function add($mixed)
+    {
+        return $this->_oLogsStorage->add($this, $mixed);
+    }
+
+    /**
+     * Get logs from current logs storage
+     * @param $iLines number of lines from the tail to return
+     * @param $sFilter filter lines by keyword
+     * @return array of strings or false on error
+     */
+    public function get($iLines = 30, $sFilter = '')
+    {
+        if (!$this->_oLogsStorage->isGetAvail())
+            return false;
+        return $this->_oLogsStorage->get($this, $iLines, $sFilter);
+    }
+
+    /**
+     * Check if filtering supported in `get` method
+     */
+    public function isFilterAvail()
+    {
+        return $this->_oLogsStorage->isFilterAvail();
+    }
+
+    /**
+     * Check if `get` method available
+     */
+    public function isGetAvail()
+    {
+        return $this->_oLogsStorage->isGetAvail();
+    }
+}
+
+/** @} */

+ 33 - 0
inc/classes/BxDolLogsQuery.php

@@ -0,0 +1,33 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+class BxDolLogsQuery extends BxDolDb
+{
+
+    public function __construct($aObject = array())
+    {
+        parent::__construct();
+        $this->_aObject = $aObject;
+    }
+
+    static public function getLogsObject($sObject)
+    {
+        $oDb = BxDolDb::getInstance();
+
+        $sQuery = $oDb->prepare("SELECT * FROM `sys_objects_logs` WHERE `object` = ?", $sObject);
+
+        $aObject = $oDb->getRow($sQuery);
+        if(!$aObject || !is_array($aObject))
+            return false;
+
+        return $aObject;
+    }
+}
+
+/** @} */

+ 137 - 0
inc/classes/BxDolLogsStorageFolder.php

@@ -0,0 +1,137 @@
+<?php defined('BX_DOL') or defined('BX_DOL_INSTALL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+class BxDolLogsStorageFolder extends BxDolFactory implements iBxDolSingleton
+{
+    protected function __construct()
+    {
+        if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
+            trigger_error ('Multiple instances are not allowed for the class: ' . get_class($this), E_USER_ERROR);
+
+        parent::__construct();
+    }
+
+    /**
+     * Prevent cloning the instance
+     */
+    public function __clone()
+    {
+        if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
+            trigger_error('Clone is not allowed for the class: ' . get_class($this), E_USER_ERROR);
+    }
+
+    /**
+     * Get singleton instance of the class
+     */
+    public static function getInstance()
+    {
+        if(!isset($GLOBALS['bxDolClasses'][__CLASS__]))
+            $GLOBALS['bxDolClasses'][__CLASS__] = new BxDolLogsStorageFolder();
+
+        return $GLOBALS['bxDolClasses'][__CLASS__];
+    }
+
+    /**
+     * Write to log 
+     * @param $oObject logs object 
+     * @param $mixed string or array to log
+     * @return true on success or false on error
+     */
+    public function add($oObject, $mixed)
+    {
+        if (!$mixed)
+            return true;
+
+        $sFile = BX_DIRECTORY_PATH_LOGS . $oObject->getObjectName() . '.log';
+
+        $s = $this->formatLogString($oObject, $mixed);
+
+        $bNewFile = !file_exists($sFile);
+        $bRet = file_put_contents($sFile, $s, FILE_APPEND) ? true : false;
+        if ($bNewFile && $bRet)
+            chmod($sFile, BX_DOL_FILE_RIGHTS);
+        return $bRet;
+    }
+
+    protected function formatLogString($oObject, $mixed, $bIncludeObjectName = false)
+    {
+        $s = date('M d H:i:s');
+
+        if ($bIncludeObjectName)
+            $s .= ' ' . $oObject->getObjectName();
+
+        $s .= ' [' . (int)bx_get_logged_profile_id() . '] ';
+
+        if (!empty($_SERVER['REQUEST_URI']))
+            $s .= $_SERVER['REQUEST_URI'] . (!empty($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '');
+        elseif (defined('BX_DOL_CRON_EXECUTE'))
+            $s .= 'CRON';
+        else
+            $s .= 'EMPTY';
+
+        $s .= ' ' . trim(is_array($mixed) ? print_r($mixed) : $mixed);
+
+        $s .= "\n";
+
+        return $s;
+    }
+
+    /**
+     * Get logs
+     * @param $oObject logs object
+     * @param $iLines number of lines from the tail
+     * @param $sFilter filter lines which include this phrase
+     * @return array of strings
+     */
+    public function get($oObject, $iLines, $sFilter = false)
+    {
+        $sFile = BX_DIRECTORY_PATH_LOGS . $oObject->getObjectName() . '.log';
+        $iSize = filesize($sFile);
+        if (!($fd = fopen($sFile, 'r+')))
+            return false;
+        $iPos = $iSize;
+        $n = 0;
+        while ($n < $iLines+1 && $iPos > 0) {
+            if (-1 == fseek($fd, $iPos))
+                break;
+            if (false === ($s = fread($fd, 1)))
+                break;
+            if ($s === "\n")
+                ++$n;
+            $iPos--;
+        }
+        $a = array();
+        for ($i = 0; $i < $iLines; $i++) {
+            if (false === ($s = fgets($fd)))
+                break;
+            if (!$sFilter || false !== stripos($s, $sFilter))
+                array_push($a, $s);
+        }
+        fclose($fd);
+        return $a;
+    }
+
+    /**
+     * Check if filtering supported in `get` method
+     */
+    public function isFilterAvail()
+    {
+        return true;
+    }
+
+    /**
+     * Check if `get` method available
+     */
+    public function isGetAvail()
+    {
+        return true;
+    }
+}
+
+/** @} */

+ 64 - 0
inc/classes/BxDolLogsStoragePHPLog.php

@@ -0,0 +1,64 @@
+<?php defined('BX_DOL') or defined('BX_DOL_INSTALL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+class BxDolLogsStoragePHPLog extends BxDolLogsStorageFolder implements iBxDolSingleton
+{
+    /**
+     * Get singleton instance of the class
+     */
+    public static function getInstance()
+    {
+        if(!isset($GLOBALS['bxDolClasses'][__CLASS__]))
+            $GLOBALS['bxDolClasses'][__CLASS__] = new BxDolLogsStoragePHPLog();
+
+        return $GLOBALS['bxDolClasses'][__CLASS__];
+    }
+
+    /**
+     * Write to log 
+     * @param $oObject logs object 
+     * @param $mixed string or array to log
+     * @return true on success or false on error
+     */
+    public function add($oObject, $mixed)
+    {
+        if (!$mixed)
+            return true;
+
+        $s = $this->formatLogString($oObject, $mixed, true);
+
+        return error_log($s, 0);
+    }
+
+    /**
+     * Not supported
+     */
+    public function get($oObject, $iLines, $sFilter = false)
+    {
+        return false;
+    }
+
+    /**
+     * Check if filtering supported in `get` method
+     */
+    public function isFilterAvail()
+    {
+        return false;
+    }
+
+    /**
+     * Check if `get` method available
+     */
+    public function isGetAvail()
+    {
+        return false;
+    }
+}
+
+/** @} */

+ 70 - 0
inc/classes/BxDolLogsStorageSTDErr.php

@@ -0,0 +1,70 @@
+<?php defined('BX_DOL') or defined('BX_DOL_INSTALL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+class BxDolLogsStorageSTDErr extends BxDolLogsStorageFolder implements iBxDolSingleton
+{
+    /**
+     * Get singleton instance of the class
+     */
+    public static function getInstance()
+    {
+        if(!isset($GLOBALS['bxDolClasses'][__CLASS__]))
+            $GLOBALS['bxDolClasses'][__CLASS__] = new BxDolLogsStorageSTDErr();
+
+        return $GLOBALS['bxDolClasses'][__CLASS__];
+    }
+
+    /**
+     * Write to log 
+     * @param $oObject logs object 
+     * @param $mixed string or array to log
+     * @return true on success or false on error
+     */
+    public function add($oObject, $mixed)
+    {
+        if (!$mixed)
+            return true;
+
+        $s = $this->formatLogString($oObject, $mixed, true);
+
+        if (!($fd = fopen('php://stderr', 'w')))
+            return false;
+        if (false === fwrite($fd, $s))
+            return false;
+        fclose($fd);
+
+        return true;
+    }
+
+    /**
+     * Not supported
+     */
+    public function get($oObject, $iLines, $sFilter = false)
+    {
+        return false;
+    }
+
+    /**
+     * Check if filtering supported in `get` method
+     */
+    public function isFilterAvail()
+    {
+        return false;
+    }
+
+    /**
+     * Check if `get` method available
+     */
+    public function isGetAvail()
+    {
+        return false;
+    }
+}
+
+/** @} */

+ 1 - 1
inc/classes/BxDolPush.php

@@ -163,7 +163,7 @@ class BxDolPush extends BxDolFactory implements iBxDolSingleton
         $oResult = @json_decode($sResult, true);
         if(isset($oResult['errors'])){
             foreach($oResult['errors'] as $sError) {  
-                file_put_contents(BX_DIRECTORY_PATH_LOGS . '/bx_push.log', date('m-d-Y H:i:s') . ": " . "Error:" . $sError . " Message:" . json_encode($aMessage) . "\n", FILE_APPEND);            
+                bx_log('sys_push', $sError . " Message:" . json_encode($aMessage));
             }
         }
 

+ 3 - 0
inc/classes/BxDolReportQuery.php

@@ -78,6 +78,9 @@ class BxDolReportQuery extends BxDolObjectQuery
         
         $sQuery = $this->prepare("DELETE FROM `{$this->_sTableTrack}` WHERE `object_id` = ?", $iObjectId);
         $this->query($sQuery);
+        
+        $sQuery = $this->prepare("UPDATE `{$this->_sTriggerTable}` SET `{$this->_sTriggerFieldCount}` = 0 WHERE `{$this->_sTriggerFieldId}` = ?", $iObjectId);
+        $this->query($sQuery);
     }
 }
 

+ 8 - 1
inc/classes/BxDolStorage.php

@@ -44,12 +44,13 @@ define('BX_DOL_STORAGE_ERR_SITE_QUOTA_EXCEEDED', 1007); ///< site quota exceeded
 define('BX_DOL_STORAGE_ERR_ENGINE_ADD', 1008); ///< some other error during file adding occured, related to particular storage engine
 
 define('BX_DOL_STORAGE_ERR_FILE_NOT_FOUND', 2001); ///< file not found
-define('BX_DOL_STORAGE_ERR_UNLINK', 2002); ///< file deletiotion failed
+define('BX_DOL_STORAGE_ERR_UNLINK', 2002); ///< file deletion failed
 
 define('BX_DOL_STORAGE_ERR_DB', 5001); ///< database error
 define('BX_DOL_STORAGE_ERR_FILESYSTEM_PERM', 5002); ///< filesystem permissions error
 define('BX_DOL_STORAGE_ERR_PERMISSION_DENIED', 5003); ///< permission denied
 define('BX_DOL_STORAGE_ERR_ENGINE_GET', 5004); ///< some other error during file getting occured, related to particular storage engine
+define('BX_DOL_STORAGE_ERR_NOT_IMPLEMENTED', 5005); ///< this feature isn't implemented in storage engine
 
 define('BX_DOL_STORAGE_DEFAULT_MIME_TYPE', 'octet/stream'); ///< default mime type if it is not find by extension
 
@@ -317,6 +318,11 @@ abstract class BxDolStorage extends BxDolFactory implements iBxDolFactoryObject
     	return $this->_aObject['object'];
     }
 
+    public function getObjectData()
+    {
+    	return $this->_aObject;
+    }
+
     /**
      * Get error code from the last occured error
      * @return error code
@@ -351,6 +357,7 @@ abstract class BxDolStorage extends BxDolFactory implements iBxDolFactoryObject
             5002 => '_sys_storage_err_filesystem_perm',
             5003 => '_sys_storage_err_permission_denied',
             5004 => '_sys_storage_err_engine_get',
+            5005 => '_sys_storage_err_not_implemented',
         );
         return _t($a[$this->_iErrorCode]);
     }

+ 33 - 14
inc/classes/BxDolStorageS3.php

@@ -10,8 +10,6 @@
 if (!defined('CURL_SSLVERSION_TLSv1'))
     define('CURL_SSLVERSION_TLSv1', 1);
 
-require_once(BX_DIRECTORY_PATH_PLUGINS . 'amazon-s3/S3.php');
-
 /**
  * File storage in Amazon S3.
  * @see BxDolStorage
@@ -21,6 +19,7 @@ class BxDolStorageS3 extends BxDolStorage
     protected $_s3;
     protected $_sBucket;
     protected $_sDomain;
+    protected $_sEndpoint;
     protected $_bSSL;
     protected $_bReducedRedundancy;
 
@@ -31,14 +30,25 @@ class BxDolStorageS3 extends BxDolStorage
     {
         parent::__construct($aObject);
 
-        $sAccessKey = getParam('sys_storage_s3_access_key');
-        $sSecretKey = getParam('sys_storage_s3_secret_key');
-        $this->_s3 = new S3($sAccessKey, $sSecretKey);
+        $this->_bSSL = (isset($this->_aParams['ssl']) && $this->_aParams['ssl']) || (!isset($this->_aParams['ssl']) && strncmp(BX_DOL_URL_ROOT, 'https://', 8) === 0) ? true : false;
+
+        $this->_sEndpoint = getParam('sys_storage_s3_endpoint');
         $this->_sBucket = getParam('sys_storage_s3_bucket');
         $this->_sDomain = getParam('sys_storage_s3_domain');
-
-        $this->_bSSL = (isset($this->_aParams['ssl']) && $this->_aParams['ssl']) || (!isset($this->_aParams['ssl']) && strncmp(BX_DOL_URL_ROOT, 'https://', 8) === 0) ? true : false;
         $this->_bReducedRedundancy = isset($this->_aParams['reduced_redundancy']) && $this->_aParams['reduced_redundancy'] ? true : false;
+
+        $this->init($aObject);
+    }
+
+    protected function init ($aObject)
+    {
+        require_once(BX_DIRECTORY_PATH_PLUGINS . 'amazon-s3/S3.php');
+        $this->_s3 = new S3(
+            getParam('sys_storage_s3_access_key'), 
+            getParam('sys_storage_s3_secret_key'), 
+            $this->_bSSL, 
+            $this->_sEndpoint ? $this->_sEndpoint : 's3.amazonaws.com'
+        );
     }
 
     /**
@@ -163,14 +173,11 @@ class BxDolStorageS3 extends BxDolStorage
 
     // ----------------
 
-    protected function addFileToEngine($sTmpFile, $sLocalId, $sName, $isPrivate, $iProfileId)
+    protected function generateHeaders($sFileName, $isPrivate, $sMimeType = '')
     {
-        $sMimeType = $this->getMimeTypeByFileName($sName);
-        $sExt = $this->getFileExt($sName);
-        $sPath = $this->genPath($sLocalId, $this->_aObject['levels']);
-        $sRemoteNamePath = $sPath . $sLocalId . ($sExt ? '.' . $sExt : '');
+        if (!$sMimeType)
+            $sMimeType = $this->getMimeTypeByFileName($sFileName);
 
-        $aMetaHeaders = array();
         $aRequestHeaders = array (
             "Content-Type"  => $sMimeType,
         );
@@ -179,6 +186,18 @@ class BxDolStorageS3 extends BxDolStorage
                 "Cache-Control" => "max-age=" . ($isPrivate && $this->_iCacheControl > $this->_aObject['token_life'] ? $this->_aObject['token_life'] : $this->_iCacheControl),
             ));
         }
+    
+        return $aRequestHeaders;
+    }
+
+    protected function addFileToEngine($sTmpFile, $sLocalId, $sName, $isPrivate, $iProfileId)
+    {
+        $sMimeType = $this->getMimeTypeByFileName($sName);
+        $sExt = $this->getFileExt($sName);
+        $sPath = $this->genPath($sLocalId, $this->_aObject['levels']);
+        $sRemoteNamePath = $sPath . $sLocalId . ($sExt ? '.' . $sExt : '');
+        $aRequestHeaders = $this->generateHeaders($sName, $isPrivate);
+        $aMetaHeaders = array();
 
         $sStorageClass = $this->_bReducedRedundancy ? S3::STORAGE_CLASS_RRS : S3::STORAGE_CLASS_STANDARD;
         $sACL = $isPrivate ? S3::ACL_AUTHENTICATED_READ : S3::ACL_PUBLIC_READ;
@@ -216,7 +235,7 @@ class BxDolStorageS3 extends BxDolStorage
     protected function getObjectBaseUrl ($isPrivate = false)
     {
         $sProto = $this->_bSSL ? 'https://' : 'http://';
-        return $sProto . $this->_sBucket . '.s3.amazonaws.com/' . $this->getObjectBaseDir($isPrivate);
+        return $sProto . $this->_sBucket . '.' . ($this->_sEndpoint ? $this->_sEndpoint : 's3.amazonaws.com'). '/' . $this->getObjectBaseDir($isPrivate);
     }
 }
 

+ 29 - 0
inc/classes/BxDolStorageS3v4.php

@@ -0,0 +1,29 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+/**
+ * File storage in Amazon S3 with signature v4 authorisation.
+ * @see BxDolStorage
+ */
+class BxDolStorageS3v4 extends BxDolStorageS3
+{
+    protected function init ($aObject)
+    {
+        require_once(BX_DIRECTORY_PATH_PLUGINS . 'amazon-s3/S3v4.php');
+        $this->_s3 = new S3v4\S3(
+            getParam('sys_storage_s3_access_key'), 
+            getParam('sys_storage_s3_secret_key'), 
+            $this->_bSSL, 
+            $this->_sEndpoint ? $this->_sEndpoint : 's3.amazonaws.com',
+            getParam('sys_storage_s3_region')
+        );
+    }
+}
+
+/** @} */

+ 168 - 0
inc/classes/BxDolStorageS3v4alt.php

@@ -0,0 +1,168 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+if (!defined('CURL_SSLVERSION_TLSv1'))
+    define('CURL_SSLVERSION_TLSv1', 1);
+
+if (!defined('BX_DOL_STORAGE_S3V4_MULTIPART_UPLOAD'))
+    define('BX_DOL_STORAGE_S3V4_MULTIPART_UPLOAD', 5*1024*1024);
+
+define('AKEEBAENGINE', 1);
+
+/**
+ * Alternatove file storage implementation for Amazon S3 
+ * compatible storge with Signature v4 support.
+ * @see BxDolStorage
+ */
+class BxDolStorageS3v4alt extends BxDolStorageS3
+{
+    protected function init ($aObject)
+    {
+        $oConfiguration = new Akeeba\Engine\Postproc\Connector\S3v4\Configuration(
+            getParam('sys_storage_s3_access_key'),
+            getParam('sys_storage_s3_secret_key'),
+            getParam('sys_storage_s3_sig_ver'),
+            getParam('sys_storage_s3_region')
+        );
+        if ($this->_sEndpoint)
+            $oConfiguration->setEndpoint($this->_sEndpoint);
+
+        $this->_s3 = new Akeeba\Engine\Postproc\Connector\S3v4\Connector($oConfiguration);
+    }
+
+    /**
+     * Get file url.
+     * @param $iFileId file
+     * @return file url
+     */
+    public function getFileUrlById($iFileId)
+    {
+        $aFile = $this->_oDb->getFileById($iFileId);
+        if (!$aFile)
+            return false;
+
+        if ($aFile['private']) {
+            $sFileLocation = $this->getObjectBaseDir($aFile['private']) . $aFile['path'];
+            $sRet = $this->_s3->getAuthenticatedURL($this->_sBucket, $sFileLocation, $this->_aObject['token_life'], $this->_bSSL);
+        } 
+        else {
+            $sRet = $this->getObjectBaseUrl($aFile['private']) . $aFile['path'];
+        }
+    
+        if ('s3.wasabisys.com' === $this->_sEndpoint)
+            $sRet = str_replace('//s3.', '//' . $this->_sBucket . '.s3.', $sRet);
+
+        return $sRet;
+    }
+
+    public function setFilePrivate($iFileId, $isPrivate = true)
+    {
+        // since in S3v4 lib this feature not implemented then we need to get file and upload it back with new ACL
+
+        $aFile = $this->_oDb->getFileById($iFileId);
+        if (!$aFile)
+            return false;
+
+        $sFileLocation = $this->getObjectBaseDir($aFile['private']) . $aFile['path'];
+        
+        $sTmpFile = tempnam(BX_DIRECTORY_PATH_TMP, $this->_aObject['object']);
+        if (!$sTmpFile) {
+            $this->setErrorCode(BX_DOL_STORAGE_ERR_FILESYSTEM_PERM);
+            return false;
+        }
+
+        try {
+            $this->_s3->getObject($this->_sBucket, $sFileLocation, $sTmpFile);
+        } catch (Exception $e) {
+            @unlink($sTmpFile);
+            $this->setErrorCode(BX_DOL_STORAGE_ERR_UNLINK);
+            return false;
+        }
+
+        $sACL = $isPrivate ? Akeeba\Engine\Postproc\Connector\S3v4\Acl::ACL_AUTHENTICATED_READ : Akeeba\Engine\Postproc\Connector\S3v4\Acl::ACL_PUBLIC_READ;
+        $aRequestHeaders = $this->generateHeaders('', $isPrivate, $aFile['mime_type']);
+        if (!$this->_upload($sTmpFile, $sFileLocation, $sACL, $aRequestHeaders))
+            return false;
+
+        return BxDolStorage::setFilePrivate($iFileId, $isPrivate);
+    }
+    
+    // ----------------
+
+    protected function addFileToEngine($sTmpFile, $sLocalId, $sName, $isPrivate, $iProfileId)
+    {
+        $sExt = $this->getFileExt($sName);
+        $sPath = $this->genPath($sLocalId, $this->_aObject['levels']);
+        $sRemoteNamePath = $sPath . $sLocalId . ($sExt ? '.' . $sExt : '');
+        $aRequestHeaders = $this->generateHeaders($sName, $isPrivate);
+        $sACL = $isPrivate ? Akeeba\Engine\Postproc\Connector\S3v4\Acl::ACL_AUTHENTICATED_READ : Akeeba\Engine\Postproc\Connector\S3v4\Acl::ACL_PUBLIC_READ;
+
+        return $this->_upload($sTmpFile, $this->getObjectBaseDir($isPrivate) . $sRemoteNamePath, $sACL, $aRequestHeaders);
+    }
+
+    protected function _upload($sInputFile, $sUri, $sACL, $aRequestHeaders)
+    {
+        try {
+            $oInputFile = Akeeba\Engine\Postproc\Connector\S3v4\Input::createFromFile($sInputFile);
+            if (filesize($sInputFile) < BX_DOL_STORAGE_S3V4_MULTIPART_UPLOAD) {
+                $this->_s3->putObject($oInputFile, $this->_sBucket, $sUri, $sACL, $aRequestHeaders);
+            }
+            else {
+                $sUploadSessionId = $this->_s3->startMultipart($oInputFile, $this->_sBucket, $sUri, $sACL, $aRequestHeaders);
+
+                $sETags = array();
+                $sETag = null;
+                $iPartNumber = 0;
+
+                do
+                {
+                    // IMPORTANT: You MUST create the input afresh before each uploadMultipart call
+                    $oInput = Akeeba\Engine\Postproc\Connector\S3v4\Input::createFromFile($sInputFile);
+                    $oInput->setUploadID($sUploadSessionId);
+                    $oInput->setPartNumber(++$iPartNumber);
+                    
+                    $sETag = $this->_s3->uploadMultipart($oInput, $this->_sBucket, $sUri);
+
+                    if (!is_null($sETag))
+                        $sETags[] = $sETag;
+                }
+                while (!is_null($sETag));
+
+                // IMPORTANT: You MUST create the input afresh before finalising the multipart upload
+                $oInput = Akeeba\Engine\Postproc\Connector\S3v4\Input::createFromFile($sInputFile);
+                $oInput->setUploadID($sUploadSessionId);
+                $oInput->setEtags($sETags);
+
+                $this->_s3->finalizeMultipart($oInput, $this->_sBucket, $sUri);
+            }
+
+        } catch (Exception $e) {
+            $this->setErrorCode(BX_DOL_STORAGE_ERR_ENGINE_ADD);
+            return false;
+        }
+
+        return true;
+    }
+
+    protected function deleteFileFromEngine($sFilePath, $isPrivate)
+    {
+        $sFileLocation = $this->getObjectBaseDir($isPrivate) . $sFilePath;
+
+        try {
+            $this->_s3->deleteObject($this->_sBucket, $sFileLocation);
+        } catch (Exception $e) {
+            $this->setErrorCode(BX_DOL_STORAGE_ERR_UNLINK);
+            return false;
+        }
+
+        return true;
+    }
+}
+
+/** @} */

+ 38 - 9
inc/classes/BxDolTemplate.php

@@ -469,11 +469,23 @@ class BxDolTemplate extends BxDolFactory implements iBxDolSingleton
         $this->aPageSnapshot = $this->aPage;
     }
 
+    public function collectingInject($aCss, $aJs)
+    {
+        $a = array('css' => 'aCss', 'js' => 'aJs');
+        foreach ($a as $s => $sVar) {
+            if (empty($$sVar))
+                continue;
+            $sKey = $s . '_compiled';
+            foreach ($$sVar as $r)
+                $this->aPage[$sKey][] = $r;
+        }
+    }
+
     /**
      * Get difference for non-system css and js files from previously remembered state as ready HTML code, 
      * additionally filter out css and js from $aExcludeCss and $aExcludeJs arrays
      */
-    public function collectingEndGetCode($aExcludeCss = array(), $aExcludeJs = array())
+    public function collectingEndGetCode($aExcludeCss = array(), $aExcludeJs = array(), $sFormat = 'html')
     {
         $aPageSave = $this->aPage; // save current state to restore later
 
@@ -500,15 +512,23 @@ class BxDolTemplate extends BxDolFactory implements iBxDolSingleton
         $this->aPage['js_compiled'] = array_filter($this->aPage['js_compiled'], $fFilterJs);
 
         // return js/css
-        $sRet = '';
-        $sRet .= $this->includeFiles('css');
-        $sRet .= $this->includeFiles('js');
+        $mixedRet = '';
+        if ('html' == $sFormat) {
+            $mixedRet .= $this->includeFiles('css');
+            $mixedRet .= $this->includeFiles('js');
+        }
+        else {
+            $mixedRet = array(
+                'css' => $this->aPage['css_compiled'],
+                'js' => $this->aPage['js_compiled'],
+            );
+        }
 
         // restore original state
         $this->aPageSnapshot = array();
         $this->aPage = $aPageSave; 
 
-        return $sRet;
+        return $mixedRet;
     }
 
     public function getClassName()
@@ -1103,8 +1123,17 @@ class BxDolTemplate extends BxDolFactory implements iBxDolSingleton
                 <meta name="geo.position" content="' . $this->aPage['location']['lat'] . ';' . $this->aPage['location']['lng'] . '" />
                 <meta name="geo.region" content="' . bx_html_attribute($this->aPage['location']['country']) . '" />';
 
-
-        // facebook / twitter
+        // set cover image as meta[image] value
+		if (empty($this->aPage['image'])){
+			$oPage = BxDolPage::getObjectInstanceByURI();
+			$aCover = $oPage->getPageCoverImage();
+			if ($aCover){
+				$oCover = BxDolCover::getInstance($this);
+				$this->aPage['image'] =  $oCover->getCoverImageUrl($aCover);
+			}
+		}	
+		
+		// facebook / twitter
         $bPageImage = !empty($this->aPage['image']);
         $sRet .= '<meta name="twitter:card" content="' . ($bPageImage ? 'summary_large_image' : 'summary') . '" />';
         if ($bPageImage)
@@ -2074,7 +2103,7 @@ class BxDolTemplate extends BxDolFactory implements iBxDolSingleton
         		 * Is mainly needed for CSS files which are gotten from LESS compiler.
         		 */
         		$sContent = preg_replace_callback(
-	                "'@import\s+[\'|\"]*\s*" . str_replace("/", "\/", BX_DOL_URL_ROOT) . "([a-zA-Z0-9\.\/_-]+)\s*[\'|\"]*\s*;'", function ($aMatches)  use($oTemplate, $sPath, $aIncluded) {
+	                "'@import\s+[\'|\"]*\s*" . str_replace("/", "\/", BX_DOL_URL_ROOT) . "([a-zA-Z0-9\.\/_-]+)\s*[\'|\"]*\s*;'", function ($aMatches)  use($oTemplate, $sPath, &$aIncluded) {
 	                	return $oTemplate->_compileCss(realpath(BX_DIRECTORY_PATH_ROOT . $aMatches[1]), $aIncluded);
 	                }, $sContent);
 
@@ -2083,7 +2112,7 @@ class BxDolTemplate extends BxDolFactory implements iBxDolSingleton
 				 * Is mainly needed for default CSS files.
 				 */
 	            $sContent = preg_replace_callback(
-	                "'@import\s+url\s*\(\s*[\'|\"]*\s*([a-zA-Z0-9\.\/_-]+)\s*[\'|\"]*\s*\)\s*;'", function ($aMatches)  use($oTemplate, $sPath, $aIncluded) {
+	                "'@import\s+url\s*\(\s*[\'|\"]*\s*([a-zA-Z0-9\.\/_-]+)\s*[\'|\"]*\s*\)\s*;'", function ($aMatches)  use($oTemplate, $sPath, &$aIncluded) {
 	                	return $oTemplate->_compileCss(realpath($sPath . dirname($aMatches[1])) . DIRECTORY_SEPARATOR . basename($aMatches[1]), $aIncluded);
 	                }, $sContent);
 

+ 1 - 3
inc/classes/BxDolTwilio.php

@@ -70,9 +70,7 @@ class BxDolTwilio extends BxDolFactory implements iBxDolSingleton
     
     private function writeLog($sString)
 	{
-		$sFile = BX_DIRECTORY_PATH_LOGS . '/bx_twilio.log';   
-		file_put_contents($sFile, date('m-d-Y H:i:s').": ", FILE_APPEND);            
-		file_put_contents($sFile, $sString."\n", FILE_APPEND);
+        bx_log('sys_twilio', $sString);
 	}
 }
 

+ 1 - 0
inc/js/classes/BxDolGrid.js

@@ -246,6 +246,7 @@ BxDolGrid.prototype.processJson = function (oData, sAction, isDisableLoading) {
             $('#' + $this._sIdContainer).html($(oData.grid).find('#' + $this._sIdContainer).html());
             var sFooter = $(oData.grid).find('.bx-grid-footer').size() ? $(oData.grid).find('.bx-grid-footer').html() : '';
             $('#' + $this._sIdWrapper).find('.bx-grid-footer').html(sFooter);
+            $('#' + $this._sIdWrapper).find('.bx-grid-header-controls-counter-value').html(oData.total_count);
             $this._onDataReloaded(true);
         }
     	if (oData && undefined != oData.blink) {

+ 21 - 10
inc/utils.inc.php

@@ -547,16 +547,9 @@ function echoDbg( $what, $desc = '' )
     echo "</pre>\n";
 }
 
-function echoDbgLog($mWhat, $sDesc = '', $sFileName = 'debug.log')
+function echoDbgLog($mWhat, $sDesc = 'unused', $sFileName = 'unused')
 {
-    $sCont =
-        '--- ' . date('r') . ' (' . BX_DOL_START . ") ---\n" .
-        $sDesc . "\n" .
-        print_r($mWhat, true) . "\n\n\n";
-
-    $rFile = fopen(BX_DIRECTORY_PATH_LOGS . $sFileName, 'a');
-    fwrite($rFile, $sCont);
-    fclose($rFile);
+    bx_log('sys_debug', $mWhat);
 }
 
 function dbgTiming($sStartMicrotime)
@@ -786,6 +779,11 @@ function bx_autoload($sClassName)
 {
     if (0 === strncmp($sClassName, 'BxDol', 5) || 0 === strncmp($sClassName, 'BxBase', 6) || 0 === strncmp($sClassName, 'BxTempl', 7))
         bx_import($sClassName);
+    else {
+        if (0 === strpos($sClassName, 'Akeeba\Engine\Postproc\Connector\S3v4')) {
+            require_once(BX_DIRECTORY_PATH_PLUGINS . 'akeeba-s3/src' . str_replace('\\', '/', substr($sClassName, 37)) . '.php');
+        }
+    }
 }
 
 /**
@@ -1336,7 +1334,7 @@ function bx_append_url_params ($sUrl, $mixedParams)
     if (!$mixedParams)
         return $sUrl;
 
-    $sParams = false == strpos($sUrl, '?') ? '?' : '&';
+    $sParams = false === strpos($sUrl, '?') ? '?' : '&';
 
     if (is_array($mixedParams)) {
         foreach($mixedParams as $sKey => $sValue) {
@@ -2035,4 +2033,17 @@ function bx_get_device_pixel_ratio()
     return $iRatio;
 }
 
+/**
+ * Log to logs object
+ * @param $sObject - logs object
+ * @param $mixed - string or array to log
+ */
+function bx_log($sObject, $mixed)
+{
+    if ($o = BxDolLogs::getObjectInstance($sObject))
+        return $o->add($mixed);
+    else
+        return false;
+}
+
 /** @} */

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
install/sql/system.sql


+ 9 - 0
modules/base/general/classes/BxBaseModGeneralMenuViewActions.php

@@ -446,6 +446,15 @@ class BxBaseModGeneralMenuViewActions extends BxTemplMenuCustom
         return $this->_getMenuItemDefault($aItem);
     }
 
+    protected function _getMenuItemByNameActionDelete($aItem)
+    {
+        $oProfile = $this->_oModule->getProfileObject(($this->_iContentId));
+        if (!$this->_oModule->isAllowDeleteOrDisable(bx_get_logged_profile_id(), $oProfile->id()))
+            return false;
+        
+        return $this->_getMenuItemByNameActionsMore($aItem);
+    }
+    
     protected function _getMenuItemByNameActionsMore($aItem)
     {
         $CNF = &$this->_oModule->_oConfig->CNF;

+ 6 - 22
modules/base/payment/classes/BxBaseModPaymentProvider.php

@@ -34,8 +34,6 @@ class BxBaseModPaymentProvider extends BxDol
     protected $_bUseSsl;
     protected $_bRedirectOnResult;
 
-    protected $_sLogFile;
-
     /**
      * Is needed to translate status received from Payment Provider 
      * to internal one. 
@@ -58,8 +56,6 @@ class BxBaseModPaymentProvider extends BxDol
         $this->_sCaption = _t($aConfig['caption']);
         $this->_sPrefix = $aConfig['option_prefix'];
 
-        $this->_sLogFile = BX_DIRECTORY_PATH_LOGS . 'bx_pp_' . $this->_sName . '.log';
-
         $this->_aSbsStatuses = array();
 
         $this->_iVendor = 0;
@@ -188,24 +184,12 @@ class BxBaseModPaymentProvider extends BxDol
 	 */
 	protected function log($sContents)
 	{
-		try {
-			if($this->_sLogFile != '')
-				$file = $this->_sLogFile;
-	    	else 
-				$file = dirname(__FILE__) . '/bx_pp_' . $this->_sName . '.log';
-	
-			file_put_contents($file, date('m-d H:i:s').": ", FILE_APPEND);
-	
-			if (is_array($sContents))
-				$sContents = var_export($sContents, true);	
-			else if (is_object($sContents))
-				$sContents = json_encode($sContents);
-	
-			file_put_contents($file, $sContents."\n", FILE_APPEND);
-	  	} 
-	  	catch (Exception $e) {
-			echo 'Error: ' . $e->getMessage();
-	  	}
+		if (is_array($sContents))
+			$sContents = var_export($sContents, true);	
+		else if (is_object($sContents))
+			$sContents = json_encode($sContents);
+		
+		bx_log('sys_payments', '[' . $this->_sName . '] ' . $sContents);
 	}
 }
 

+ 19 - 11
modules/base/profile/classes/BxBaseModProfileFormsEntryHelper.php

@@ -14,7 +14,7 @@
  */
 class BxBaseModProfileFormsEntryHelper extends BxBaseModGeneralFormsEntryHelper
 {
-    protected $_bAutoApproval = false;
+    protected $_sAutoApproval = false;
 
     public function __construct($oModule)
     {
@@ -22,19 +22,27 @@ class BxBaseModProfileFormsEntryHelper extends BxBaseModGeneralFormsEntryHelper
 
         $CNF = &$this->_oModule->_oConfig->CNF;
 
-        $bAutoApproval = isset($CNF['PARAM_AUTOAPPROVAL']) ? (bool)getParam($CNF['PARAM_AUTOAPPROVAL']) : true;
+        $sAutoApproval = isset($CNF['PARAM_AUTOAPPROVAL']) ? getParam($CNF['PARAM_AUTOAPPROVAL']) : BX_DOL_PROFILE_ACTIVATE_ALWAYS;
         $bAdministrator = BxDolAcl::getInstance()->isMemberLevelInSet(array(MEMBERSHIP_ID_ADMINISTRATOR));
-        $this->setAutoApproval(isAdmin() || $bAdministrator || $bAutoApproval);
+        if (isAdmin() || $bAdministrator)
+            $sAutoApproval = BX_DOL_PROFILE_ACTIVATE_ALWAYS;
+        $this->setAutoApproval($sAutoApproval);
     }
 
-    public function isAutoApproval()
+    public function isAutoApproval($sAction = BX_DOL_PROFILE_ACTIVATE_ALWAYS)
     {
-        return $this->_bAutoApproval;
+        if ($this->_sAutoApproval == BX_DOL_PROFILE_ACTIVATE_ALWAYS || $this->_sAutoApproval == $sAction)
+            return true;
+        
+        return false;
     }
-
-    public function setAutoApproval($b)
+    
+    public function setAutoApproval($mValue)
     {
-        return ($this->_bAutoApproval = $b);
+        if ($mValue === true)
+            $mValue = BX_DOL_PROFILE_ACTIVATE_ALWAYS;
+        
+        return ($this->_sAutoApproval = $mValue);
     }
 
     protected function _getProfileAndContentData ($iContentId)
@@ -93,7 +101,7 @@ class BxBaseModProfileFormsEntryHelper extends BxBaseModGeneralFormsEntryHelper
         list ($oProfile, $aContentInfo) = $this->_getProfileAndContentData($iContentId);
 
         $sStatus = $oProfile->getStatus();
-        if (!$this->isAutoApproval() && BX_PROFILE_STATUS_ACTIVE == $sStatus)
+        if (!$this->isAutoApproval(BX_DOL_PROFILE_ACTIVATE_EDIT) && BX_PROFILE_STATUS_ACTIVE == $sStatus)
             $aTrackTextFieldsChanges = array ();
     }
 
@@ -111,7 +119,7 @@ class BxBaseModProfileFormsEntryHelper extends BxBaseModGeneralFormsEntryHelper
 
         // change profile to 'pending' only if profile is 'active'
         $sStatus = $oProfile->getStatus();
-        if (!$this->isAutoApproval() && BX_PROFILE_STATUS_ACTIVE == $sStatus && !empty($aTrackTextFieldsChanges['changed_fields']))
+        if (!$this->isAutoApproval(BX_DOL_PROFILE_ACTIVATE_EDIT) && BX_PROFILE_STATUS_ACTIVE == $sStatus && !empty($aTrackTextFieldsChanges['changed_fields']))
             $oProfile->disapprove(BX_PROFILE_ACTION_AUTO, 0, $this->_oModule->serviceActAsProfile());
 
         // process uploaded files
@@ -147,7 +155,7 @@ class BxBaseModProfileFormsEntryHelper extends BxBaseModGeneralFormsEntryHelper
 
         // approve profile if auto-approval is enabled and profile status is 'pending'
         $sStatus = $oProfile->getStatus();
-        if ($sStatus == BX_PROFILE_STATUS_PENDING && $this->isAutoApproval())
+        if ($sStatus == BX_PROFILE_STATUS_PENDING && $this->isAutoApproval(BX_DOL_PROFILE_ACTIVATE_ADD))
             $oProfile->approve(BX_PROFILE_ACTION_AUTO, 0, $this->_oModule->serviceActAsProfile() && $this->_oModule->serviceIsEnableProfileActivationLetter());
 
         // set created profile some default membership

+ 15 - 6
modules/base/profile/classes/BxBaseModProfileGridAdministration.php

@@ -167,6 +167,15 @@ class BxBaseModProfileGridAdministration extends BxBaseModGeneralGridAdministrat
         return  $this->_getFilterSelectOne($this->_sFilter1Name, $this->_sFilter1Value, $this->_aFilter1Values) . $this->_getSearchInput();
     }
 
+    protected function _getCellSwitcher ($mixedValue, $sKey, $aField, $aRow)
+    {
+        if (isset($aRow['profile_id']))
+            if (!$this->_oModule->isAllowDeleteOrDisable(bx_get_logged_profile_id(), $aRow['profile_id']))
+                return parent::_getCellDefault('', $sKey, $aField, $aRow);    
+        
+        return parent::_getCellSwitcher ($mixedValue, $sKey, $aField, $aRow);
+    }
+    
     protected function _getCellFullname($mixedValue, $sKey, $aField, $aRow)
     {
     	$oProfile = $this->_getProfileObject($aRow['id']);
@@ -201,11 +210,7 @@ class BxBaseModProfileGridAdministration extends BxBaseModGeneralGridAdministrat
 
     protected function _getProfileObject($iId)
     {
-    	$oProfile = BxDolProfile::getInstanceByContentAndType((int)$iId, $this->_oModule->_oConfig->getName());
-        if (!$oProfile) 
-            $oProfile = BxDolProfileUndefined::getInstance();
-
-        return $oProfile;
+        return $this->_oModule->getProfileObject($iId);
     }
 
     protected function _getProfileId($iId)
@@ -215,11 +220,15 @@ class BxBaseModProfileGridAdministration extends BxBaseModGeneralGridAdministrat
 
 	protected function _doDelete($iId, $aParams = array())
     {
+        $oProfile = $this->_getProfileObject($iId);
+        
+        if (!$this->_oModule->isAllowDeleteOrDisable(bx_get_logged_profile_id(), $oProfile->id()))
+            return false;
+        
     	if($this->_oModule->checkMyself($iId))
     		return false;
 
     	if(isset($aParams['with_content']) && $aParams['with_content'] === true) {
-    		$oProfile = $this->_getProfileObject($iId);
 
     		if($oProfile instanceof BxDolProfileUndefined)
     			return false;

+ 4 - 1
modules/base/profile/classes/BxBaseModProfileMenuManageTools.php

@@ -39,7 +39,10 @@ class BxBaseModProfileMenuManageTools extends BxBaseModGeneralMenuManageTools
             case 'delete-with-content':
             	if($this->_oModule->checkMyself($this->_iContentId))
             		return false;
-
+                $oProfile = $this->_oModule->getProfileObject(($this->_iContentId));
+                if (!$this->_oModule->isAllowDeleteOrDisable(bx_get_logged_profile_id(), $oProfile->id()))
+                    return false;
+                
                 $sCheckFuncName = 'checkAllowedDelete';
                 break;
         }

+ 34 - 0
modules/base/profile/classes/BxBaseModProfileModule.php

@@ -13,6 +13,11 @@ define('BX_DOL_PROFILE_REDIRECT_PROFILE', 'profile');
 define('BX_DOL_PROFILE_REDIRECT_LAST', 'last');
 define('BX_DOL_PROFILE_REDIRECT_CUSTOM', 'custom');
 
+define('BX_DOL_PROFILE_ACTIVATE_ALWAYS', 'on');
+define('BX_DOL_PROFILE_ACTIVATE_NEVER', 'off');
+define('BX_DOL_PROFILE_ACTIVATE_ADD', 'add');
+define('BX_DOL_PROFILE_ACTIVATE_EDIT', 'edit');
+
 /**
  * Base class for profile modules.
  */
@@ -112,6 +117,18 @@ class BxBaseModProfileModule extends BxBaseModGeneralModule implements iBxDolCon
 
         return $aResult;
     }
+    
+    public function serviceGetOptionsActivation()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aResult = array();
+        $aChoices = array(BX_DOL_PROFILE_ACTIVATE_ALWAYS, BX_DOL_PROFILE_ACTIVATE_NEVER, BX_DOL_PROFILE_ACTIVATE_ADD, BX_DOL_PROFILE_ACTIVATE_EDIT);
+        foreach($aChoices as $sChoice) 
+            $aResult[] = array('key' => $sChoice, 'value' => _t($CNF['T']['option_activation_' . $sChoice]));
+
+        return $aResult;
+    }
 
     public function serviceGetThumb ($iContentId, $sTranscoder = '') 
     {
@@ -1627,6 +1644,23 @@ class BxBaseModProfileModule extends BxBaseModGeneralModule implements iBxDolCon
 
 		return $oProfile->id() == $iLogged;
     }
+    
+    public function isAllowDeleteOrDisable($iActorProfileId, $iTargetProfileId)
+    {
+        if (BxDolAcl::getInstance()->isMemberLevelInSet(array(MEMBERSHIP_ID_MODERATOR), $iActorProfileId) && BxDolAcl::getInstance()->isMemberLevelInSet(array(MEMBERSHIP_ID_MODERATOR, MEMBERSHIP_ID_ADMINISTRATOR), $iTargetProfileId))
+            return false;
+        
+        return true;    
+    }
+    
+    public function getProfileObject($iContentId)
+    {
+    	$oProfile = BxDolProfile::getInstanceByContentAndType((int)$iContentId, $this->_oConfig->getName());
+        if (!$oProfile) 
+            $oProfile = BxDolProfileUndefined::getInstance();
+
+        return $oProfile;
+    }
 
     protected function _serviceCheckAllowedContactForProfile($aDataEntry, $isPerformAction = false, $iProfileId = false)
     {

+ 2 - 0
modules/base/profile/classes/BxBaseModProfileTemplate.php

@@ -260,6 +260,8 @@ class BxBaseModProfileTemplate extends BxBaseModGeneralTemplate
 
         if(!$sUrlCover)
             $sUrlCover = $this->getImageUrl('cover.svg');
+		else
+			BxDolTemplate::getInstance()->addPageMetaImage($sUrlCover);
         
         $sUrlCoverChange = BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_EDIT_COVER'] . '&id=' . $aData[$CNF['FIELD_ID']]);
 

+ 3 - 0
modules/base/text/classes/BxBaseModTextGridAdministration.php

@@ -74,6 +74,9 @@ class BxBaseModTextGridAdministration extends BxBaseModGeneralGridAdministration
         if((int)$aField['chars_limit'] > 0)
             $sTitle = strmaxtextlen($sTitle, (int)$aField['chars_limit']);
 
+        if ($sTitle == '')
+            $sTitle = _t('_sys_txt_no_title');
+        
         return parent::_getCellDefault($this->_getEntryLink($sTitle, $aRow), $sKey, $aField, $aRow);
     }
 

+ 9 - 0
modules/boonex/accounts/classes/BxAccntModule.php

@@ -117,6 +117,15 @@ class BxAccntModule extends BxBaseModGeneralModule
 
     	return CHECK_ACTION_RESULT_ALLOWED;
     }
+    
+    public function isAllowDeleteOrDisable($iActorProfileId, $iTargetProfileId)
+    {
+        if (BxDolAcl::getInstance()->isMemberLevelInSet(array(MEMBERSHIP_ID_MODERATOR), $iActorProfileId) && BxDolAcl::getInstance()->isMemberLevelInSet(array(MEMBERSHIP_ID_MODERATOR, MEMBERSHIP_ID_ADMINISTRATOR), $iTargetProfileId))
+            return false;
+        
+        return true;    
+    }
+    
 }
 
 /** @} */

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

@@ -58,8 +58,8 @@ INSERT INTO `sys_acl_matrix` (`IDLevel`, `IDAction`) VALUES
 
 
 -- GRIDS: administration
-INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
-('bx_accounts_administration', 'Sql', 'SELECT `ta`.*, `tp`.`status` AS `status` FROM `sys_accounts` AS `ta` LEFT JOIN `sys_profiles` AS `tp` ON `ta`.`id`=`tp`.`account_id` AND `tp`.`type`=''system'' WHERE 1 ', 'sys_accounts', 'id', 'logged', 'status', '', 20, NULL, 'start', '', 'name,email,ip,phone', '', 'like', 'email_confirmed,logged,added', '', 192, 'BxAccntGridAdministration', 'modules/boonex/accounts/classes/BxAccntGridAdministration.php');
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `show_total_count`, `override_class_name`, `override_class_file`) VALUES
+('bx_accounts_administration', 'Sql', 'SELECT `ta`.*, `tp`.`status` AS `status` FROM `sys_accounts` AS `ta` LEFT JOIN `sys_profiles` AS `tp` ON `ta`.`id`=`tp`.`account_id` AND `tp`.`type`=''system'' WHERE 1 ', 'sys_accounts', 'id', 'logged', 'status', '', 20, NULL, 'start', '', 'name,email,ip,phone', '', 'like', 'email_confirmed,logged,added', '', 192, 1, 'BxAccntGridAdministration', 'modules/boonex/accounts/classes/BxAccntGridAdministration.php');
 
 INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
 ('bx_accounts_administration', 'checkbox', '_sys_select', '2%', 0, '', '', 1),

+ 1 - 1
modules/boonex/accounts/js/main.js

@@ -9,7 +9,7 @@ function BxAccntMain(oOptions) {
 BxAccntMain.prototype.copyToClipboard = function(oElement) {
     var $this = this;
 
-    oClipboard = new Clipboard('#' + this._aHtmlIds['password_button'], {
+    oClipboard = new ClipboardJS('#' + this._aHtmlIds['password_button'], {
 	    target: function(oTrigger) {
 	        return $('#' + $this._aHtmlIds['password_text']).get(0);
 	    }

+ 1 - 0
modules/boonex/albums/install/langs/en.xml

@@ -242,6 +242,7 @@
     <string name="_bx_albums_grid_column_title_adm_author"><![CDATA[Author]]></string>
     <string name="_bx_albums_grid_action_title_adm_edit"><![CDATA[Edit]]></string>
     <string name="_bx_albums_grid_action_title_adm_delete"><![CDATA[Delete]]></string>
+    <string name="_bx_albums_grid_action_title_adm_clear_reports"><![CDATA[Сlear reports]]></string>
     <string name="_bx_albums_grid_action_title_adm_more_actions"><![CDATA[More Actions]]></string>
     <string name="_bx_albums_grid_action_title_adm_audit_content"><![CDATA[Audit actions]]></string>
     <string name="_bx_albums_grid_action_err_delete"><![CDATA[Cannot delete the selected album(s).]]></string>

+ 2 - 0
modules/boonex/albums/install/sql/enable.sql

@@ -514,10 +514,12 @@ INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable
 
 INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
 ('bx_albums_administration', 'bulk', 'delete', '_bx_albums_grid_action_title_adm_delete', '', 0, 1, 1),
+('bx_albums_administration', 'bulk', 'clear_reports', '_bx_albums_grid_action_title_adm_clear_reports', '', 0, 1, 2),
 ('bx_albums_administration', 'single', 'edit', '_bx_albums_grid_action_title_adm_edit', 'pencil-alt', 1, 0, 1),
 ('bx_albums_administration', 'single', 'delete', '_bx_albums_grid_action_title_adm_delete', 'remove', 1, 1, 2),
 ('bx_albums_administration', 'single', 'settings', '_bx_albums_grid_action_title_adm_more_actions', 'cog', 1, 0, 3),
 ('bx_albums_administration', 'single', 'audit_content', '_bx_albums_grid_action_title_adm_audit_content', 'search', 1, 0, 4),
+('bx_albums_administration', 'single', 'clear_reports', '_bx_albums_grid_action_title_adm_clear_reports', 'eraser', 1, 0, 5),
 ('bx_albums_common', 'bulk', 'delete', '_bx_albums_grid_action_title_adm_delete', '', 0, 1, 1),
 ('bx_albums_common', 'single', 'edit', '_bx_albums_grid_action_title_adm_edit', 'pencil-alt', 1, 0, 1),
 ('bx_albums_common', 'single', 'delete', '_bx_albums_grid_action_title_adm_delete', 'remove', 1, 1, 2),

+ 1 - 0
modules/boonex/classes/install/langs/en.xml

@@ -210,6 +210,7 @@
 	<string name="_bx_classes_grid_column_title_adm_active"><![CDATA[Active]]></string>
     <string name="_bx_classes_grid_column_title_adm_title"><![CDATA[Title]]></string>
     <string name="_bx_classes_grid_action_title_adm_audit_content"><![CDATA[Audit actions]]></string>
+    <string name="_bx_classes_grid_action_title_adm_clear_reports"><![CDATA[Сlear reports]]></string>
     <string name="_bx_classes_grid_column_title_adm_added"><![CDATA[Added]]></string>
     <string name="_bx_classes_grid_column_title_adm_author"><![CDATA[Author]]></string>>
     <string name="_bx_classes_grid_action_title_adm_add"><![CDATA[Add]]></string>

+ 2 - 0
modules/boonex/classes/install/sql/enable.sql

@@ -340,10 +340,12 @@ INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable
 
 INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
 ('bx_classes_administration', 'bulk', 'delete', '_bx_classes_grid_action_title_adm_delete', '', 0, 1, 1),
+('bx_classes_administration', 'bulk', 'clear_reports', '_bx_classes_grid_action_title_adm_clear_reports', '', 0, 1, 2),
 ('bx_classes_administration', 'single', 'edit', '_bx_classes_grid_action_title_adm_edit', 'pencil-alt', 1, 0, 1),
 ('bx_classes_administration', 'single', 'delete', '_bx_classes_grid_action_title_adm_delete', 'remove', 1, 1, 2),
 ('bx_classes_administration', 'single', 'settings', '_bx_classes_grid_action_title_adm_more_actions', 'cog', 1, 0, 3),
 ('bx_classes_administration', 'single', 'audit_content', '_bx_classes_grid_action_title_adm_audit_content', 'search', 1, 0, 4),
+('bx_classes_administration', 'single', 'clear_reports', '_bx_classes_grid_action_title_adm_clear_reports', 'eraser', 1, 0, 5),
 ('bx_classes_common', 'bulk', 'delete', '_bx_classes_grid_action_title_adm_delete', '', 0, 1, 1),
 ('bx_classes_common', 'single', 'edit', '_bx_classes_grid_action_title_adm_edit', 'pencil-alt', 1, 0, 1),
 ('bx_classes_common', 'single', 'delete', '_bx_classes_grid_action_title_adm_delete', 'remove', 1, 1, 2),

+ 3 - 0
modules/boonex/courses/install/langs/en.xml

@@ -169,6 +169,8 @@
     <string name="_bx_courses_menu_item_title_delete"><![CDATA[Delete]]></string>
     <string name="_bx_courses_menu_item_title_system_delete_with_content"><![CDATA[Delete With Content]]></string>
     <string name="_bx_courses_menu_item_title_delete_with_content"><![CDATA[Delete With Content]]></string>
+    <string name="_bx_courses_menu_item_title_system_clear_reports"><![CDATA[Clear reports]]></string>
+    <string name="_bx_courses_menu_item_title_clear_reports"><![CDATA[Clear reports]]></string>
     <string name="_bx_courses_menu_item_title_system_edit_cover"><![CDATA[Edit Cover]]></string>
     <string name="_bx_courses_menu_item_title_edit_cover"><![CDATA[Edit Cover]]></string>
     <string name="_bx_courses_menu_item_title_system_edit_profile"><![CDATA[Edit Course]]></string>
@@ -263,6 +265,7 @@
     <string name="_bx_courses_grid_column_title_adm_account"><![CDATA[Account]]></string>
     <string name="_bx_courses_grid_action_title_adm_audit_context"><![CDATA[Audit actions in context]]></string>
     <string name="_bx_courses_grid_action_title_adm_audit_content"><![CDATA[Audit actions with context]]></string>
+    <string name="_bx_courses_grid_action_title_adm_clear_reports"><![CDATA[Сlear reports]]></string>
     <string name="_bx_courses_grid_action_title_adm_delete"><![CDATA[Delete]]></string>
     <string name="_bx_courses_grid_action_title_adm_delete_with_content"><![CDATA[Delete With Content]]></string>
     <string name="_bx_courses_grid_action_title_adm_more_actions"><![CDATA[More Actions]]></string>

+ 3 - 1
modules/boonex/courses/install/sql/enable.sql

@@ -344,7 +344,8 @@ INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES
 
 INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
 ('bx_courses_menu_manage_tools', 'bx_courses', 'delete', '_bx_courses_menu_item_title_system_delete', '_bx_courses_menu_item_title_delete', 'javascript:void(0)', 'javascript:{js_object}.onClickDelete({content_id});', '_self', 'far trash-alt', '', 2147483647, 1, 0, 1),
-('bx_courses_menu_manage_tools', 'bx_courses', 'delete-with-content', '_bx_courses_menu_item_title_system_delete_with_content', '_bx_courses_menu_item_title_delete_with_content', 'javascript:void(0)', 'javascript:{js_object}.onClickDeleteWithContent({content_id});', '_self', 'far trash-alt', '', 2147483647, 1, 0, 2);
+('bx_courses_menu_manage_tools', 'bx_courses', 'delete-with-content', '_bx_courses_menu_item_title_system_delete_with_content', '_bx_courses_menu_item_title_delete_with_content', 'javascript:void(0)', 'javascript:{js_object}.onClickDeleteWithContent({content_id});', '_self', 'far trash-alt', '', 2147483647, 1, 0, 2),
+('bx_courses_menu_manage_tools', 'bx_courses', 'clear-reports', '_bx_courses_menu_item_title_system_clear_reports', '_bx_courses_menu_item_title_clear_reports', 'javascript:void(0)', 'javascript:{js_object}.onClickClearReports({content_id});', '_self', 'eraser', '', 2147483647, 1, 0, 3);
 
 -- MENU: dashboard manage tools
 SET @iManageMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name`='sys_account_dashboard_manage_tools' LIMIT 1);
@@ -505,6 +506,7 @@ INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable
 INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
 ('bx_courses_administration', 'bulk', 'delete', '_bx_courses_grid_action_title_adm_delete', '', 0, 1, 2),
 ('bx_courses_administration', 'bulk', 'delete_with_content', '_bx_courses_grid_action_title_adm_delete_with_content', '', 0, 1, 3),
+('bx_courses_administration', 'bulk', 'clear_reports', '_bx_classes_grid_action_title_adm_clear_reports', '', 0, 1, 4),
 ('bx_courses_administration', 'single', 'settings', '_bx_courses_grid_action_title_adm_more_actions', 'cog', 1, 0, 2),
 ('bx_courses_administration', 'single', 'audit_content', '_bx_courses_grid_action_title_adm_audit_content', 'search', 1, 0, 3),
 ('bx_courses_administration', 'single', 'audit_context', '_bx_courses_grid_action_title_adm_audit_context', 'search-location', 1, 0, 4),

+ 6 - 0
modules/boonex/courses/js/manage_tools.js

@@ -59,4 +59,10 @@ BxCoursesManageTools.prototype.onClickDeleteWithContent = function(iContentId) {
     glGrids[this._sObjNameGrid].actionWithId(iContentId, 'delete_with_content', {}, '', false, 1);
 };
 
+BxCoursesManageTools.prototype.onClickClearReports = function (iContentId) {
+    $('.bx-popup-applied:visible').dolPopupHide();
+
+    glGrids[this._sObjNameGrid].actionWithId(iContentId, 'clear_reports', {}, '', false, 1);
+};
+
 /** @} */

+ 7 - 9
modules/boonex/elasticsearch/classes/BxElsApi.php

@@ -238,15 +238,13 @@ class BxElsApi extends BxDol
     {
         if (!defined('BX_ELASTICSEARCH_LOG') || !constant('BX_ELASTICSEARCH_LOG'))
             return;
-        $fn = BX_DIRECTORY_PATH_ROOT . "logs/elasticsearch.log";
-        $f = @fopen ($fn, 'a');
-        if (!$f)
-            return;
-        if (is_array($mixed))
-            fwrite ($f, date(DATE_RFC822) . "\n" . print_r($mixed, true) . "\n");
-        else
-            fwrite ($f, date(DATE_RFC822) . "\t" . $mixed . "\n");
-        fclose ($f);
+        
+		if (is_array($mixed))
+			$mixed = var_export($mixed, true);	
+		else if (is_object($mixed))
+			$mixed = json_encode($mixed);
+		
+		bx_log('bx_elasticsearch', $mixed);
     }
 }
 

+ 2 - 0
modules/boonex/elasticsearch/install/langs/en.xml

@@ -14,6 +14,8 @@
     <string name="_bx_elasticsearch_form_manage_input_do_submit"><![CDATA[Index]]></string> 
 
     <string name="_bx_elasticsearch_lmi_cpt_manage"><![CDATA[Manage]]></string>
+  
+    <string name="_bx_elasticsearch_log"><![CDATA[ElasticSearch]]></string>
     
     <string name="_bx_elasticsearch_msg_index_planned"><![CDATA[Data indexing was planned.]]></string>
 </resources>

+ 4 - 0
modules/boonex/elasticsearch/install/sql/install.sql

@@ -16,6 +16,10 @@ INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_fo
 ('bx_elasticsearch_manage_index', 'type', 2147483647, 1, 2),
 ('bx_elasticsearch_manage_index', 'do_submit', 2147483647, 1, 3);
 
+-- Logs Objects
+INSERT INTO `sys_objects_logs` (`object`, `module`, `logs_storage`, `title`, `active`, `class_name`, `class_file`) VALUES
+('bx_elasticsearch', 'bx_elasticsearch', 'Auto', '_bx_elasticsearch_log', 1, '', '');
+
 
 -- Studio page and widget
 INSERT INTO `sys_std_pages`(`index`, `name`, `header`, `caption`, `icon`) VALUES

+ 2 - 0
modules/boonex/elasticsearch/install/sql/uninstall.sql

@@ -5,6 +5,8 @@ DELETE FROM `sys_form_displays` WHERE `module` = 'bx_elasticsearch';
 DELETE FROM `sys_form_inputs` WHERE `module` = 'bx_elasticsearch';
 DELETE FROM `sys_form_display_inputs` WHERE `display_name` IN('bx_elasticsearch_manage_index');
 
+-- Logs Objects
+DELETE FROM `sys_objects_logs` WHERE `module` = 'bx_elasticsearch';
 
 -- Studio page and widget
 DELETE FROM `tp`, `tw`, `tpw`

+ 11 - 1
modules/boonex/english/data/langs/system/en.xml

@@ -1546,6 +1546,10 @@
 	<string name="_adm_stg_cpt_option_sys_storage_s3_bucket"><![CDATA[AWS bucket]]></string>
 	<string name="_adm_stg_cpt_option_sys_storage_s3_domain"><![CDATA[AWS custom domain (require custom DNS setup)]]></string>
 	<string name="_adm_stg_cpt_option_sys_storage_s3_secret_key"><![CDATA[AWS secret key]]></string>
+	<string name="_adm_stg_cpt_option_sys_storage_s3_endpoint"><![CDATA[Endpoint (specify if other than Amazon, such as storage.googleapis.com, s3.wasabisys.com)]]></string>
+	<string name="_adm_stg_cpt_option_sys_storage_s3_sig_ver"><![CDATA[Signature version (only S3v4 storage supports v4)]]></string>
+	<string name="_adm_stg_cpt_option_sys_storage_s3_region"><![CDATA[Region (for signature v4 only)]]></string>
+
 	<string name="_adm_stg_cpt_option_sys_template_cache_image_enable"><![CDATA[Enable cache for images]]></string>
 	<string name="_adm_stg_cpt_option_sys_template_cache_image_max_size"><![CDATA[Cached image max size (in Kb)]]></string>
         <string name="_adm_stg_cpt_option_sys_template_cache_image_max_size_err"><![CDATA[Use non-empty value less or equal to 10kb. Big values make no sense.]]></string>
@@ -1572,6 +1576,7 @@
 	<string name="_adm_stg_cpt_option_sys_hide_post_to_context_for_privacy"><![CDATA[Hide post to context for modules in Privacy field]]></string>
     <string name="_adm_stg_cpt_option_sys_search_keyword_min_len"><![CDATA[Keyword min length in site search]]></string>
     <string name="_adm_stg_cpt_option_sys_profile_bot"><![CDATA[System bot profile]]></string>
+    <string name="_adm_stg_cpt_option_sys_logs_storage_default"><![CDATA[Default logs storage]]></string>
 	<string name="_adm_stg_cpt_type_system"><![CDATA[System]]></string>
 	<string name="_adm_stg_scs_save"><![CDATA[Settings were successfully saved]]></string>
 	<string name="_adm_stg_err_save_error_message"><![CDATA[Error in '{0}': {1}]]></string>
@@ -2230,7 +2235,7 @@ If it is not enabled then please consider implement this optimization, since it
 	<string name="_sys_form_account_input_email_uniq_error_loggedin"><![CDATA[This email is already used by someone.]]></string>
   <string name="_sys_form_account_input_phone_uniq_error_loggedin"><![CDATA[This phone number is already used by someone.]]></string>
 	<string name="_sys_form_account_input_name"><![CDATA[Account name]]></string>
-	<string name="_sys_form_account_input_name_error"><![CDATA[This is a required field.]]></string>
+	<string name="_sys_form_account_input_name_error"><![CDATA[Account name is mandatory and and cannot start with @]]></string>
 	<string name="_sys_form_account_input_password"><![CDATA[Create a password]]></string>
 	<string name="_sys_form_account_input_password_confirm"><![CDATA[Confirm a password]]></string>
 	<string name="_sys_form_account_input_password_confirm_error"><![CDATA[Password confirmation failed]]></string>
@@ -2470,6 +2475,7 @@ If it is not enabled then please consider implement this optimization, since it
 	<string name="_sys_grid_confirmation"><![CDATA[Are you sure?]]></string>
 	<string name="_sys_grid_delete_failed"><![CDATA[Delete failed]]></string>
   <string name="_sys_grid_search"><![CDATA[Search...]]></string>
+  <string name="_sys_grid_total_count"><![CDATA[Сount: {0}]]></string>
   <string name="_sys_grid_txt_account_manager"><![CDATA[Redirect to manage account.]]></string>
 	<string name="_sys_inst_conf_desc"><![CDATA[{0}{1}<br />Example: {2}]]></string>
 	<string name="_sys_inst_conf_desc_admin_email"><![CDATA[Admin user e-mail address.]]></string>
@@ -2566,6 +2572,10 @@ If it is not enabled then please consider implement this optimization, since it
 	<string name="_sys_location_determining"><![CDATA[Determining location...]]></string>
 	<string name="_sys_location_geolocation_not_supported_by_browser"><![CDATA[Geolocation is not supported by this browser]]></string>
 	<string name="_sys_location_undefined"><![CDATA[Location off]]></string>
+	<string name="_sys_log_debug"><![CDATA[System: debug]]></string>
+    <string name="_sys_log_twilio"><![CDATA[System: Twilio]]></string>
+    <string name="_sys_log_push"><![CDATA[System: PUSH]]></string>
+	<string name="_sys_log_payments"><![CDATA[System: Payments]]></string>
     <string name="_sys_macros_malformed"><![CDATA[Error: malformed macros]]></string>
     <string name="_sys_macros_unsafe"><![CDATA[Error: unsafe macros]]></string>
     <string name="_sys_macros_output_not_displayable"><![CDATA[Error: output isn't displayable]]></string>

+ 3 - 0
modules/boonex/events/install/langs/en.xml

@@ -220,6 +220,8 @@
     <string name="_bx_events_menu_item_title_delete"><![CDATA[Delete]]></string>
     <string name="_bx_events_menu_item_title_system_delete_with_content"><![CDATA[Delete With Content]]></string>
     <string name="_bx_events_menu_item_title_delete_with_content"><![CDATA[Delete With Content]]></string>
+    <string name="_bx_events_menu_item_title_system_clear_reports"><![CDATA[Clear reports]]></string>
+    <string name="_bx_events_menu_item_title_clear_reports"><![CDATA[Clear reports]]></string>
     <string name="_bx_events_menu_item_title_system_edit_cover"><![CDATA[Edit Cover]]></string>
     <string name="_bx_events_menu_item_title_edit_cover"><![CDATA[Edit Cover]]></string>
     <string name="_bx_events_menu_item_title_system_edit_profile"><![CDATA[Edit Event]]></string>
@@ -320,6 +322,7 @@
     <string name="_bx_events_grid_action_title_adm_more_actions"><![CDATA[More Actions]]></string>
   <string name="_bx_events_grid_action_title_adm_audit_content"><![CDATA[Audit actions with content]]></string>
   <string name="_bx_events_grid_action_title_adm_audit_context"><![CDATA[Audit actions in context]]></string>
+  <string name="_bx_events_grid_action_title_adm_clear_reports"><![CDATA[Сlear reports]]></string>
 	<string name="_bx_events_grid_action_err_delete"><![CDATA[Cannot delete the selected event(s).]]></string>
 	<string name="_bx_events_grid_txt_account_manager"><![CDATA[Redirect to manage account.]]></string>
 

+ 3 - 1
modules/boonex/events/install/sql/enable.sql

@@ -411,7 +411,8 @@ INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES
 
 INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
 ('bx_events_menu_manage_tools', 'bx_events', 'delete', '_bx_events_menu_item_title_system_delete', '_bx_events_menu_item_title_delete', 'javascript:void(0)', 'javascript:{js_object}.onClickDelete({content_id});', '_self', 'far trash-alt', '', 2147483647, 1, 0, 1),
-('bx_events_menu_manage_tools', 'bx_events', 'delete-with-content', '_bx_events_menu_item_title_system_delete_with_content', '_bx_events_menu_item_title_delete_with_content', 'javascript:void(0)', 'javascript:{js_object}.onClickDeleteWithContent({content_id});', '_self', 'far trash-alt', '', 2147483647, 1, 0, 2);
+('bx_events_menu_manage_tools', 'bx_events', 'delete-with-content', '_bx_events_menu_item_title_system_delete_with_content', '_bx_events_menu_item_title_delete_with_content', 'javascript:void(0)', 'javascript:{js_object}.onClickDeleteWithContent({content_id});', '_self', 'far trash-alt', '', 2147483647, 1, 0, 2),
+('bx_events_menu_manage_tools', 'bx_events', 'clear-reports', '_bx_cevents_menu_item_title_system_clear_reports', '_bx_events_menu_item_title_clear_reports', 'javascript:void(0)', 'javascript:{js_object}.onClickClearReports({content_id});', '_self', 'eraser', '', 2147483647, 1, 0, 3);
 
 -- MENU: dashboard manage tools
 SET @iManageMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name`='sys_account_dashboard_manage_tools' LIMIT 1);
@@ -570,6 +571,7 @@ INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable
 INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
 ('bx_events_administration', 'bulk', 'delete', '_bx_events_grid_action_title_adm_delete', '', 0, 1, 2),
 ('bx_events_administration', 'bulk', 'delete_with_content', '_bx_events_grid_action_title_adm_delete_with_content', '', 0, 1, 3),
+('bx_events_administration', 'bulk', 'clear_reports', '_bx_events_grid_action_title_adm_clear_reports', '', 0, 1, 3),
 ('bx_events_administration', 'single', 'settings', '_bx_events_grid_action_title_adm_more_actions', 'cog', 1, 0, 2),
 ('bx_events_administration', 'single', 'audit_content', '_bx_events_grid_action_title_adm_audit_content', 'search', 1, 0, 3),
 ('bx_events_administration', 'single', 'audit_context', '_bx_events_grid_action_title_adm_audit_context', 'search-location', 1, 0, 4),

+ 6 - 0
modules/boonex/events/js/manage_tools.js

@@ -59,4 +59,10 @@ BxEventsManageTools.prototype.onClickDeleteWithContent = function(iContentId) {
 	glGrids[this._sObjNameGrid].actionWithId(iContentId, 'delete_with_content', {}, '', false, 1);
 };
 
+BxEventsManageTools.prototype.onClickClearReports = function (iContentId) {
+    $('.bx-popup-applied:visible').dolPopupHide();
+
+    glGrids[this._sObjNameGrid].actionWithId(iContentId, 'clear_reports', {}, '', false, 1);
+};
+
 /** @} */

+ 14 - 8
modules/boonex/facebook_connect/classes/BxFaceBookConnectModule.php

@@ -36,14 +36,20 @@ class BxFaceBookConnectModule extends BxBaseModConnectModule
         // Create our Application instance.
         $this -> oFacebook = null;
 
-        if ($this -> _oConfig -> mApiID) {
+        if ($this -> _oConfig -> mApiID && $this -> _oConfig -> mApiSecret) {
             if (!isset($_SESSION))
                 session_start();
-            $this -> oFacebook = new Facebook\Facebook(array(
-                'app_id'  => $this -> _oConfig -> mApiID,
-                'app_secret' => $this -> _oConfig -> mApiSecret,
-                'default_graph_version' => 'v2.4',  
-            ));
+
+            try {
+                $this -> oFacebook = new Facebook\Facebook(array(
+                    'app_id'  => $this -> _oConfig -> mApiID,
+                    'app_secret' => $this -> _oConfig -> mApiSecret,
+                    'default_graph_version' => 'v2.4',  
+                ));
+            }
+            catch (Exception $e) {
+                $this->_setLastError($e->getMessage());
+            }
         }
     }
 
@@ -153,9 +159,9 @@ class BxFaceBookConnectModule extends BxBaseModConnectModule
         $this -> _oTemplate -> dislayPageError();
     }
 
-    function serviceLastError()
+    function serviceLastError($bShowEmpty = true)
     {
-        return MsgBox($this->sLastError ? $this->sLastError : _t('_Empty'));
+        return $this->sLastError ? MsgBox($this->sLastError) : ($bShowEmpty ? MsgBox(_t('_Empty')): '');
     }
 
     /**

+ 7 - 0
modules/boonex/facebook_connect/classes/BxFaceBookConnectStudioPage.php

@@ -21,6 +21,13 @@ class BxFaceBookConnectStudioPage extends BxTemplStudioModule
         );
     }
     
+    function getSettings ()
+    {
+        $s = parent::getSettings ();
+        $oModule = BxDolModule::getInstance('bx_facebook');         
+        return $oModule->serviceLastError(false) . $s;
+    }
+
     function getHelp ()
     {
         $oModule = BxDolModule::getInstance('bx_facebook'); 

+ 27 - 2
modules/boonex/files/classes/BxFilesConfig.php

@@ -30,6 +30,7 @@ class BxFilesConfig extends BxBaseModTextConfig
             'TABLE_ENTRIES' => $aModule['db_prefix'] . 'main',
             'TABLE_ENTRIES_FULLTEXT' => 'search_fields',
             'TABLE_FILES' => $aModule['db_prefix'] . 'files',
+            'TABLE_BOOKMARKS' => $aModule['db_prefix'] . 'bookmarks',
 
             // database fields
             'FIELD_ID' => 'id',
@@ -52,6 +53,9 @@ class BxFilesConfig extends BxBaseModTextConfig
             'FIELD_LOCATION_PREFIX' => 'location',
             'FIELD_LABELS' => 'labels',
             'FIELDS_WITH_KEYWORDS' => 'auto', // can be 'auto', array of fields or comma separated string of field names, works only when OBJECT_METATAGS is specified
+            'FIELD_BOOKMARKS_ID' => 'object_id',
+            'FIELD_BOOKMARKS_PROFILE' => 'profile_id',
+            'FIELD_MIME_TYPE' => 'mime_type',
 			
             // some params
             'PARAM_PER_PAGE_FOR_FAVORITES_LISTS' => 'bx_files_per_page_for_favorites_lists',
@@ -77,6 +81,7 @@ class BxFilesConfig extends BxBaseModTextConfig
             'PARAM_CHARS_SUMMARY_PLAIN' => '',
             'PARAM_NUM_RSS' => 'bx_files_rss_num',
             'PARAM_SEARCHABLE_FIELDS' => 'bx_files_searchable_fields',
+            'PARAM_LINK_TO_PREVIEW' => 'bx_files_show_link_to_preview',
 
             // objects
             'OBJECT_STORAGE' => 'bx_files_files',
@@ -146,11 +151,13 @@ class BxFilesConfig extends BxBaseModTextConfig
         );
 
         $this->_aJsClasses = array(
-        	'manage_tools' => 'BxFilesManageTools'
+        	'manage_tools' => 'BxFilesManageTools',
+            'toolbar_tools' => 'BxFilesBrowserTools',
         );
 
         $this->_aJsObjects = array(
-        	'manage_tools' => 'oBxFilesManageTools'
+        	'manage_tools' => 'oBxFilesManageTools',
+            'toolbar_tools' => 'oBxFilesBrowserTools',
         );
 
         $this->_aGridObjects = array(
@@ -159,6 +166,24 @@ class BxFilesConfig extends BxBaseModTextConfig
         	
         );
     }
+
+    public function getJsClass($mixedType)
+    {
+        if (is_array($mixedType)) {
+            return parent::getJsClass($mixedType['type']);
+        } else {
+            return parent::getJsClass($mixedType);
+        }
+    }
+
+    public function getJsObject($mixedType)
+    {
+        if (is_array($mixedType)) {
+            return parent::getJsObject($mixedType['type']).$mixedType['uniq'];
+        } else {
+            return parent::getJsObject($mixedType);
+        }
+    }
 }
 
 /** @} */

+ 40 - 0
modules/boonex/files/classes/BxFilesDb.php

@@ -34,6 +34,46 @@ class BxFilesDb extends BxBaseModFilesDb
         $sQuery = $this->prepare ("SELECT * FROM `" . $this->_oConfig->CNF['TABLE_ENTRIES'] . "` WHERE `data_processed` = 0 ORDER BY `added` ASC LIMIT ?", $iLimit);
         return $this->getAll($sQuery);
     }
+
+    public function bookmarkFile($iContentId, $iProfileId) {
+        if ($this->isFileBookmarked($iContentId, $iProfileId)) {
+            $this->query("DELETE FROM `" . $this->_oConfig->CNF['TABLE_BOOKMARKS'] . "` WHERE `object_id` = :id AND `profile_id` = :profile_id", [
+                'id' => $iContentId,
+                'profile_id' => $iProfileId
+            ]);
+        } else {
+            $this->query("INSERT INTO `" . $this->_oConfig->CNF['TABLE_BOOKMARKS'] . "` (`object_id`, `profile_id`) VALUES (:id, :profile_id)", [
+                'id' => $iContentId,
+                'profile_id' => $iProfileId
+            ]);
+        }
+    }
+
+    public function deleteFileBookmarks($iContentId) {
+        $this->query("DELETE FROM `" . $this->_oConfig->CNF['TABLE_BOOKMARKS'] . "` WHERE `object_id` = :id", [
+            'id' => $iContentId,
+        ]);
+    }
+
+    public function deleteProfileBookmarks($iProfileId) {
+        $this->query("DELETE FROM `" . $this->_oConfig->CNF['TABLE_BOOKMARKS'] . "` WHERE `profile_id` = :profile_id", [
+            'profile_id' => $iProfileId,
+        ]);
+    }
+
+    public function isFileBookmarked($iContentId, $iProfileId) {
+        return $this->getOne("SELECT COUNT(*) FROM `" . $this->_oConfig->CNF['TABLE_BOOKMARKS'] . "` WHERE `object_id` = :id AND `profile_id` = :profile_id", [
+            'id' => $iContentId,
+            'profile_id' => $iProfileId
+        ]);
+    }
+
+    public function updateEntryTitle($iContentId, $sNewTitle) {
+        $this->query("UPDATE `" . $this->_oConfig->CNF['TABLE_ENTRIES'] . "` SET `" . $this->_oConfig->CNF['FIELD_TITLE'] . "` = :title WHERE `" . $this->_oConfig->CNF['FIELD_ID'] . "` = :id", [
+            'id' => $iContentId,
+            'title' => $sNewTitle,
+        ]);
+    }
 }
 
 /** @} */

+ 5 - 0
modules/boonex/files/classes/BxFilesFormEntry.php

@@ -25,6 +25,11 @@ class BxFilesFormEntry extends BxBaseModFilesFormEntry
         parent::_associalFileWithContent($oStorage, $iFileId, $iProfileId, $iContentId, $sPictureField);
         $this->_oModule->_oDb->updateFileId($iContentId, $iFileId);
     }
+
+    public function delete ($iContentId, $aContentInfo = array()) {
+        $this->_oModule->_oDb->deleteFileBookmarks($iContentId);
+        return parent::delete ($iContentId, $aContentInfo);
+    }
 }
 
 /** @} */

+ 7 - 0
modules/boonex/files/classes/BxFilesMenuSnippetMeta.php

@@ -17,6 +17,13 @@ class BxFilesMenuSnippetMeta extends BxBaseModTextMenuSnippetMeta
 
         parent::__construct($aObject, $oTemplate);
     }
+
+    protected function _getMenuItemSize($aItem)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+        $aFile = $this->_oModule->getContentFile($this->_aContentInfo);
+        return $this->getUnitMetaItemText(_t_format_size($aFile['size']));
+    }
 }
 
 /** @} */

+ 132 - 0
modules/boonex/files/classes/BxFilesMenuViewActions.php

@@ -37,4 +37,136 @@ class BxFilesMenuViewActions extends BxBaseModTextMenuViewActions
     }
 }
 
+class BxFilesMenuViewActionsInline extends BxFilesMenuViewActions {
+    protected $_iMenuItemCounter;
+    protected $_sJsObjectMenuTools;
+    protected $_sBookmarkIcon;
+    protected $_bAllowEditOptions;
+
+    public function __construct($aObject, $oTemplate = false)
+    {
+        parent::__construct($aObject, $oTemplate);
+
+        $this->_bShowTitle = false;
+        $this->_iMenuItemCounter = 0;
+
+        $this->_iMoreAutoItemsStatic = 2;
+        $this->_bMoreAutoItemsStaticOnly = 1;
+
+        $this->_sJsObjectMenuTools = 'oMenuTools' . bx_gen_method_name($this->_sObject);
+
+        $this->addMarkers([
+            'js_object' => $this->_sJsObjectMenuTools,
+        ]);
+
+        $this->_sBookmarkIcon = 'far star';
+    }
+
+    public function setContentId($iContentId)
+    {
+        $iRandNumber = mt_rand();
+
+        $this->_iContentId = $iContentId;
+        $this->_sJsObjectMoreAuto = 'oMenuMoreAuto' . bx_gen_method_name($this->_sObject) . $this->_iContentId . $iRandNumber;
+
+        $sPrefix = str_replace('_', '-', $this->_sObject);
+        $this->_aHtmlIds = [
+            'main' => $sPrefix . $this->_iContentId . $iRandNumber,
+            'more_auto_popup' => $sPrefix . '-ma-popup' . $this->_iContentId . $iRandNumber,
+        ];
+
+        //erase cached data to force it to generate it again since this is going to be a menu for a new entry
+        $this->_oMenuAction = false;
+        $this->_oMenuActions = false;
+        $this->_oMenuActionsMore = false;
+        $this->_oMenuSocialSharing = false;
+        $this->_bMoreAuto = null;
+        unset($this->_aObject['menu_items']);
+
+        parent::setContentId($iContentId);
+    }
+
+    public function setBookmarked($bBookmarked) {
+        $this->_sBookmarkIcon = $bBookmarked ? 'fas star' : 'far star';
+    }
+
+    public function setAllowEditOptions($bAllow) {
+        $this->_bAllowEditOptions = $bAllow;
+    }
+
+    protected function _getMenuItem ($aItem) {
+        //first two items suppose to appear in a row without a title, the rest must go to more-auto along with a title
+        $this->_bShowTitle = $this->_iMenuItemCounter > $this->_iMoreAutoItemsStatic;
+
+        if ($aItem['name'] == 'bookmark')
+            $aItem['icon'] = $this->_sBookmarkIcon;
+
+        $bRes = parent::_getMenuItem ($aItem);
+
+        if ($bRes) $this->_iMenuItemCounter++;
+        return $bRes;
+    }
+
+    public function _getMenuItemBookmark($aItem) {
+        if (!isLogged()) return false;
+        return true;
+    }
+
+    public function _getMenuItemDeleteFileQuick($aItem) {
+        if (!$this->_bAllowEditOptions) return false;
+        return true;
+    }
+
+    public function _getMenuItemMoveTo($aItem) {
+        if (!$this->_bAllowEditOptions) return false;
+        return true;
+    }
+
+    public function _getMenuItemEditTitle($aItem) {
+        if (!$this->_bAllowEditOptions) return false;
+        return true;
+    }
+
+    public function getMenuItems () {
+        //reset the counter here because at the very first call the getMenuItems calls twice (the first is in BxBaseMenuMoreAuto::_isMoreAuto
+        $this->_iMenuItemCounter = 1;
+        return parent::getMenuItems();
+    }
+
+    protected function _getJsCodeMoreAuto()
+    {
+        $sJsObject = $this->_getJsObjectMoreAuto();
+        $aJsParams = array(
+            'sObject' => $this->_sObject,
+            'iItemsStatic' => $this->_iMoreAutoItemsStatic,
+            'bItemsStaticOnly' => $this->_bMoreAutoItemsStaticOnly ? 1 : 0,
+            'aHtmlIds' => $this->_getHtmlIds()
+        );
+
+        return $this->_oTemplate->_wrapInTagJsCode("if(!" . $sJsObject . ") {var " . $sJsObject . " = new BxDolMenuMoreAuto(" . json_encode($aJsParams) . "); " . $sJsObject . ".init();} else {" . $sJsObject . ".init();}");
+    }
+
+    protected function _addJsCss()
+    {
+        parent::_addJsCss();
+        $this->_oTemplate->addJs('menu_tools.js');
+    }
+
+    protected function _getTemplateVars() {
+        $aResult = parent::_getTemplateVars();
+
+        $sJsObject = $this->_getJsObjectMenuTools();
+        $aJsParams = [
+            'sActionUrl' => BX_DOL_URL_ROOT.$this->_oTemplate->getModule()->_oConfig->getBaseUri(),
+        ];
+        $aResult['js_code'] .= $this->_oTemplate->_wrapInTagJsCode("if(!" . $sJsObject . ") {var " . $sJsObject . " = new BxFilesMenuTools(" . json_encode($aJsParams) . ");}");
+
+        return $aResult;
+    }
+
+    protected function _getJsObjectMenuTools() {
+        return $this->_sJsObjectMenuTools;
+    }
+}
+
 /** @} */

+ 124 - 0
modules/boonex/files/classes/BxFilesModule.php

@@ -198,6 +198,130 @@ class BxFilesModule extends BxBaseModTextModule
             array('id' => $aFile['id'], 'url' => $sUrl, 'src' => $sPhotoThumb),
         );
     }
+
+    public function actionBookmark($iContentId) {
+        if (!$this->isLogged()) return;
+
+        $aData = $this->_oDb->getContentInfoById((int)$iContentId);
+        if (!$aData) return;
+
+        $this->_oDb->bookmarkFile((int)$iContentId, bx_get_logged_profile_id());
+    }
+
+    public function actionEntryPreview($iContentId) {
+        $aData = $this->_oDb->getContentInfoById((int)$iContentId);
+
+        $sJsCss = '';
+
+        $sPreviewCode = $this->_serviceTemplateFunc ('entryFilePreview', $iContentId);
+
+        // various init scripts could be there, which fail to work properly
+        // because of a fact that it is inside a popup which is at the moment is hidden
+        // so we need a way to postpone that initialization
+        $aScripts = [];
+        $sPreviewCode = preg_replace_callback('/<script>(.*)<\/script>/s', function($aMatch) use (&$aScripts) {
+            $aScripts[] = ['code' => $aMatch[1]];
+            return '';
+        }, $sPreviewCode);
+
+        // if there is a script then most likely it is a text + codemirror initialization, so include it here.
+        if ($aScripts) {
+            $sJsCss .= $this->_oTemplate->addJs('codemirror/codemirror-ext.min.js', true);
+            $sJsCss .= $this->_oTemplate->addCss(BX_DIRECTORY_PATH_PLUGINS_PUBLIC . 'codemirror/|codemirror.css', true);
+        }
+
+        $sJsCss .= $this->_oTemplate->addCss('file_handler.css', true);
+
+        $sPopupCode = $this->_oTemplate->parseHtmlByName('entry-popup.html', [
+            'content' => $sPreviewCode,
+            'bx_repeat:init_scripts' => $aScripts,
+        ]);
+
+        echoJson([
+            'popup' => [
+                'html' => PopupBox('bx-files-popup', bx_process_output($aData['title']), $sJsCss.$sPopupCode),
+                'options' => [
+                    'onShow' => 'bx_files_popup_init()',
+                ],
+            ],
+        ]);
+    }
+
+    public function actionEntryInfo($iContentId) {
+        $aData = $this->_oDb->getContentInfoById((int)$iContentId);
+
+        echoJson([
+            'popup' => [
+                'html' => PopupBox('bx-files-popup', bx_process_output($aData['title']), $this->_oTemplate->entryInfoPopup($aData)),
+            ],
+        ]);
+    }
+
+    public function actionEntryEditTitle($iContentId) {
+        $iContentId = intval($iContentId);
+        $aData = $this->_oDb->getContentInfoById($iContentId);
+
+        $CNF = &$this->_oConfig->CNF;
+
+        if (!$aData) {
+            echoJson(['message' => _t('_bx_files_txt_error')]);
+            exit;
+        }
+
+        if ($aData[$CNF['FIELD_AUTHOR']] != bx_get_logged_profile_id()) {
+            echoJson(['message' => _t('_bx_files_txt_permission_denied')]);
+            exit;
+        }
+
+        if (isset($_POST['title'])) {
+            if ($sNewTitle = bx_get('title')) {
+                $this->_oDb->updateEntryTitle($iContentId, $sNewTitle);
+                $aData = $this->_oDb->getContentInfoById($iContentId);
+                echo bx_process_output($aData[$CNF['FIELD_TITLE']]);
+            }
+            exit;
+        }
+
+        $sActionUrl = BX_DOL_URL_ROOT.$this->_oConfig->getBaseUri();
+        echoJson([
+            'eval' => "bx_prompt('"._t('_bx_files_form_entry_input_title')."', '".bx_js_string($aData[$CNF['FIELD_TITLE']])."', function(val) {                
+                $.post('{$sActionUrl}entry_edit_title/{$iContentId}', {title: val.getValue()}, function(sTitle) {
+                    if (sTitle)
+                        $('.bx-file-entry[file_id=".$aData[$CNF['FIELD_ID']]."] .bx-files-filename a').html(sTitle);
+                });                
+            })",
+        ]);
+    }
+
+    public function actionEntryDelete($iContentId) {
+        $iContentId = intval($iContentId);
+        $aData = $this->_oDb->getContentInfoById($iContentId);
+
+        $CNF = &$this->_oConfig->CNF;
+
+        if (!$aData) {
+            echoJson(['message' => _t('_bx_files_txt_error')]);
+            exit;
+        }
+
+        if ($aData[$CNF['FIELD_AUTHOR']] != bx_get_logged_profile_id()) {
+            echoJson(['message' => _t('_bx_files_txt_permission_denied')]);
+            exit;
+        }
+
+        $this->serviceDeleteEntity($iContentId);
+
+        echoJson([
+            'eval' => "
+                $('.bx-file-entry[file_id={$iContentId}]').fadeOut();
+            ",
+        ]);
+    }
+
+    public function serviceDeleteEntitiesByAuthor ($iProfileId) {
+        $this->_oDb->deleteProfileBookmarks($iProfileId);
+        return parent::serviceDeleteEntitiesByAuthor ($iProfileId);
+    }
 }
 
 /** @} */

+ 249 - 1
modules/boonex/files/classes/BxFilesSearchResult.php

@@ -11,10 +11,49 @@
 
 class BxFilesSearchResult extends BxBaseModTextSearchResult
 {
+    protected $sOrderParamName = 'order';
+    protected $sBookmarksParamName = 'bookmarks';
+    protected $sSortingParamName = 'sorting';
+    protected $sCurrentView;
+    protected $bFileManagerMode = false;
+    protected $iFileManagerUploadTo = 0;
+
     function __construct($sMode = '', $aParams = array())
     {
+        $aParams['unit_views'] = [
+            'extended' => 'unit.html',
+            'gallery' => 'unit_gallery.html',
+            'full' => 'unit_full.html',
+            'showcase' => 'unit_showcase.html',
+            'table' => 'unit_simple_row.html',
+        ];
+
+        //Enble filemanager (toolbar, table files layout, dynamic menus, folders support) only for author/context blocks.
+        if (($sMode == 'author' || $sMode == 'context')) {
+            $this->bFileManagerMode = true;
+            $this->setUnitParams(['show_inline_menu' => true]);
+        }
+
         parent::__construct($sMode, $aParams);
 
+        $this->sCurrentView = array_flip($aParams['unit_views'])[$this->sUnitTemplate];
+
+        $this->aGetParams = array_merge($this->aGetParams, [
+            $this->sOrderParamName,
+            $this->sBookmarksParamName,
+            $this->sSortingParamName,
+            $this->sUnitViewParamName,
+        ]);
+
+
+        $this->aSortingOptions = [
+            'name',
+            'date',
+            'type',
+            'author',
+        ];
+
+
         $this->aCurrent = array(
             'name' => 'bx_files',
             'module_name' => 'bx_files',
@@ -121,8 +160,217 @@ class BxFilesSearchResult extends BxBaseModTextSearchResult
         }
 
         $this->processReplaceableMarkers($oProfileAuthor);
-
         $this->addConditionsForPrivateContent($CNF, $oProfileAuthor);
+
+        if ($this->bFileManagerMode) {
+            if (bx_get($this->sBookmarksParamName))
+                $this->addConditionsForBookmarks($CNF);
+
+            $this->aCurrent['sorting'] = 'name';
+            if (bx_get($this->sSortingParamName)) $this->aCurrent['sorting'] = bx_get($this->sSortingParamName);
+
+            if ($this->aCurrent['sorting'] == 'type')
+                $this->addConditionsForMimeType($CNF);
+
+            $this->removeContainerClass('bx-def-margin-sec-lefttopright-neg');
+
+            $this->iFileManagerUploadTo = $oProfileAuthor->id();
+        }
+    }
+
+    protected function addConditionsForBookmarks(&$CNF) {
+        if(empty($this->aCurrent['join']) || !is_array($this->aCurrent['join'])) $this->aCurrent['join'] = [];
+
+        $this->aCurrent['join'] = array_merge($this->aCurrent['join'], [
+            'bookmarks' => [
+                'type' => 'INNER',
+                'table' => $CNF['TABLE_BOOKMARKS'],
+                'mainTable' => $CNF['TABLE_ENTRIES'],
+                'mainField' => $CNF['FIELD_ID'],
+                'onField' => $CNF['FIELD_BOOKMARKS_ID'],
+                'joinFields' => array($CNF['FIELD_BOOKMARKS_PROFILE']),
+            ],
+        ]);
+
+        $this->aCurrent['restriction']['bookmarks'] = [
+            'value' => bx_get_logged_profile_id(),
+            'field' => $CNF['FIELD_BOOKMARKS_PROFILE'],
+            'operator' => '=',
+            'table' => $CNF['TABLE_BOOKMARKS'],
+        ];
+    }
+
+    protected function addConditionsForMimeType(&$CNF) {
+        if(empty($this->aCurrent['join']) || !is_array($this->aCurrent['join'])) $this->aCurrent['join'] = [];
+
+        $this->aCurrent['join'] = array_merge($this->aCurrent['join'], [
+            'files' => [
+                'type' => 'INNER',
+                'table' => $CNF['TABLE_FILES'],
+                'mainTable' => $CNF['TABLE_ENTRIES'],
+                'mainField' => $CNF['FIELD_ID'],
+                'onField' => $CNF['FIELD_ID'],
+                'joinFields' => array($CNF['FIELD_MIME_TYPE']),
+            ],
+        ]);
+    }
+
+    public function addCustomParts() {
+        if ($this->_bLiveSearch) return;
+
+        if (!$this->bFileManagerMode) return;
+
+        return $this->showBrowseToolbar([
+            'aRequestParams' => [
+                'unit_view_param' => $this->sUnitViewParamName,
+                'bookmarks_param' => $this->sBookmarksParamName,
+                'sorting_param' => $this->sSortingParamName,
+                'keyword_param' => 'keyword',
+            ],
+
+            'layout' => $this->sCurrentView,
+            'bookmarks' => intval(bx_get($this->sBookmarksParamName)),
+            'sorting' => $this->aCurrent['sorting'],
+            'sorting_options' => $this->aSortingOptions,
+            'keyword' => bx_get('keyword') ? bx_get('keyword') : '',
+        ]);
+    }
+
+    function processing ()
+    {
+        $sCode = parent::processing();
+        if (!$this->aCurrent['paginate']['num'] && bx_is_dynamic_request() && $this->bFileManagerMode) {
+            //to show toolbar in case of empty results
+            $sCode = $this->displaySearchBox($this->addCustomParts().MsgBox(_t('_Empty')));
+        }
+        return $sCode;
+    }
+
+    function showPagination($bAdmin = false, $bChangePage = true, $bPageReload = true)
+    {
+        $aAdditionalParams = [
+            $this->sUnitViewParamName => $this->sCurrentView,
+            $this->sBookmarksParamName => intval(bx_get($this->sBookmarksParamName)),
+            $this->sSortingParamName => $this->aCurrent['sorting'],
+        ];
+        $sPageUrl = $this->getCurrentUrl($aAdditionalParams, false);
+        $sOnClick = $this->getCurrentOnclick($aAdditionalParams, false);
+
+        $oPaginate = new BxTemplPaginate([
+            'page_url' => $sPageUrl,
+            'on_change_page' => $sOnClick,
+            'num' => $this->aCurrent['paginate']['num'],
+            'per_page' => $this->aCurrent['paginate']['perPage'],
+            'start' => $this->aCurrent['paginate']['start'],
+        ]);
+
+        return $sOnClick ? $oPaginate->getSimplePaginate() : $oPaginate->getPaginate();
+    }
+
+    function getAlterOrder() {
+        if (in_array($this->aCurrent['sorting'], $this->aSortingOptions)) {
+            $CNF = &$this->oModule->_oConfig->CNF;
+
+            $aSql = array();
+            switch ($this->aCurrent['sorting']) {
+                case 'name':
+                    $aSql['order'] = ' ORDER BY `'.$CNF['TABLE_ENTRIES'].'`.`'.$CNF['FIELD_TITLE'].'` ASC';
+                    break;
+                case 'date':
+                    $aSql['order'] = ' ORDER BY `'.$CNF['TABLE_ENTRIES'].'`.`'.$CNF['FIELD_ID'].'` DESC';
+                    break;
+                case 'author':
+                    $aSql['order'] = ' ORDER BY `'.$CNF['TABLE_ENTRIES'].'`.`'.$CNF['FIELD_AUTHOR'].'` ASC, `'.$CNF['TABLE_ENTRIES'].'`.`'.$CNF['FIELD_TITLE'].'` ASC';
+                    break;
+                case 'type':
+                    $aSql['order'] = ' ORDER BY `'.$CNF['TABLE_FILES'].'`.`'.$CNF['FIELD_MIME_TYPE'].'` ASC, `'.$CNF['TABLE_ENTRIES'].'`.`'.$CNF['FIELD_TITLE'].'` ASC';
+                    break;
+            }
+            return $aSql;
+        } else {
+            return parent::getAlterOrder();
+        }
+
+    }
+
+    public function showBrowseToolbar($aParams) {
+        $aSortingOptions = [];
+        if (isset($aParams['sorting_options'])) {
+            foreach ($aParams['sorting_options'] as $sSorting) {
+                $aSortingOptions[$sSorting] = _t('_bx_files_toolbar_sorting_' . $sSorting);
+            }
+        }
+
+        $sUniqueIdent = mt_rand();
+        $aParams['unique_ident'] = $sUniqueIdent;
+
+        $this->oModule->_oTemplate->addJs('toolbar_tools.js');
+
+        $sJsCode = $this->oModule->_oTemplate->getJsCode(['type' => 'toolbar_tools', 'uniq' => $sUniqueIdent], $aParams);
+        $sJsObject = $this->oModule->_oConfig->getJsObject(['type' => 'toolbar_tools', 'uniq' => $sUniqueIdent]);
+
+        $oForm = new BxTemplFormView([], $this->oModule->_oTemplate);
+        $aInputSorting = [
+            'type' => 'select',
+            'name' => 'sorting',
+            'value' => $aParams['sorting'],
+            'values' => $aSortingOptions,
+            'attrs' => [
+                'onchange' => $sJsObject.'.setSorting(this.value);',
+            ],
+        ];
+
+        $aInputFilter = [
+            'type' => 'text',
+            'name' => 'keyword',
+            'value' => $aParams['keyword'],
+            'attrs' => [
+                'placeholder' => _t('_sys_search_placeholder'),
+                'onkeyup' => $sJsObject.'.onChangeFilter(this);',
+                'onpaste' => $sJsObject.'.onChangeFilter(this);',
+            ],
+        ];
+
+        return $this->oModule->_oTemplate->parseHtmlByName('files_browser_toolbar.html', [
+            'js_object' => $sJsObject,
+            'js_code' => $sJsCode,
+            'unique_ident' => $sUniqueIdent,
+            'sorting_dropdown' => $oForm->genRowStandard($aInputSorting),
+            'filter_box' => $oForm->genRowStandard($aInputFilter),
+            'bx_if:upload_visible' => [
+                'condition' => $this->oModule->serviceIsAllowedAddContentToProfile($this->iFileManagerUploadTo),
+                'content' => [],
+            ],
+            'bx_if:bookmarks_visible' => [
+                'condition' => isLogged(),
+                'content' => [
+                    'js_object' => $sJsObject,
+                    'bookmark_enabled' => $aParams['bookmarks'] ? 'fas' : 'far',
+                ],
+            ],
+            'bx_if:table_layout_btn' => [
+                'condition' => $aParams['layout'] == 'table',
+                'content' => [
+                    'js_object' => $sJsObject,
+                    'unit_view_param' => $aParams['aRequestParams']['unit_view_param'],
+                ],
+            ],
+            'bx_if:gallery_layout_btn' => [
+                'condition' => $aParams['layout'] == 'gallery',
+                'content' => [
+                    'js_object' => $sJsObject,
+                    'unit_view_param' => $aParams['aRequestParams']['unit_view_param'],
+                ],
+            ],
+            'bx_if:select_all_checkbox' => [
+                'condition' => $aParams['layout'] == 'table',
+                'content' => [
+                    'js_object' => $sJsObject,
+                    'unique_ident' => $sUniqueIdent,
+                ],
+            ],
+            'bx_repeat:sorting_options' => $aSortingOptions,
+        ]);
     }
 }
 

+ 76 - 2
modules/boonex/files/classes/BxFilesTemplate.php

@@ -53,8 +53,31 @@ class BxFilesTemplate extends BxBaseModTextTemplate
         $aParams['template_name'] = $sTemplateName;
         $aVars = $this->getUnit($aData, $aParams);
         $aVars['icon'] = $oStorage ? $oStorage->getFontIconNameByFileName($aFile['file_name']) : 'far file';
-        if($sTemplateName == 'unit_gallery.html') 
-            $aVars['bx_if:no_thumb']['content']['icon'] = $aVars['icon'];
+
+        $aVars['bx_if:no_thumb']['content']['icon'] = $aVars['icon'];
+
+
+        $aVars['bx_if:inline_menu']['condition'] = isset($aParams['show_inline_menu']) && $aParams['show_inline_menu'];
+        $aVars['bx_if:inline_menu']['content'] = [];
+        if ($aVars['bx_if:inline_menu']['condition']) {
+            bx_import('BxTemplMenu');
+            $oMenu = BxTemplMenu::getObjectInstance('bx_files_view_inline', $this);
+            if ($oMenu) {
+                $oMenu->setContentId($aData[$CNF['FIELD_ID']]);
+                $oMenu->setBookmarked($this->_oDb->isFileBookmarked($aData[$CNF['FIELD_ID']], bx_get_logged_profile_id()));
+                $oMenu->setAllowEditOptions($aData[$CNF['FIELD_AUTHOR']] == bx_get_logged_profile_id());
+                $aVars['bx_if:inline_menu']['content']['menu'] = $oMenu->getCode();
+            }
+        }
+
+        $sAltContentUrlHandler = '';
+        if ($oModule->_oDb->getParam($CNF['PARAM_LINK_TO_PREVIEW'])) {
+            $sAltContentUrlHandler = '$.get(\'' . BX_DOL_URL_ROOT . $oModule->_oConfig->getBaseUri() . 'entry_preview/' . $aData[$CNF['FIELD_ID']] . '\', processJsonData, \'json\'); return false;';
+        }
+        $aVars['alt_content_url_handler'] = $sAltContentUrlHandler;
+        $aVars['bx_if:no_thumb']['content']['alt_content_url_handler'] = $sAltContentUrlHandler;
+        $aVars['bx_if:thumb']['content']['alt_content_url_handler'] = $sAltContentUrlHandler;
+
 
         return $this->parseHtmlByName($sTemplateName, $aVars);
     }
@@ -82,6 +105,57 @@ class BxFilesTemplate extends BxBaseModTextTemplate
         return $this->parseHtmlByName($sTemplateName, $this->getTmplVarsText($aData));
     }
 
+    public function entryInfoPopup($aData) {
+        $CNF = &$this->getModule()->_oConfig->CNF;
+
+        $aFile = $this->getModule()->getContentFile($aData);
+
+        $oAuthor = BxDolProfile::getInstance($aData[$CNF['FIELD_AUTHOR']]);
+        if (!$oAuthor) $oAuthor = BxDolProfileUndefined::getInstance();
+
+        $aForm = [
+            'params' => [
+                'view_mode' => true,
+            ],
+            'inputs' => [
+                'size' => [
+                    'caption' => _t('_bx_files_entry_info_size'),
+                    'type' => 'value',
+                    'value' => _t_format_size($aFile['size']),
+                ],
+                'date' => [
+                    'caption' => _t('_bx_files_entry_info_date'),
+                    'type' => 'date_time',
+                    'value' => $aData[$CNF['FIELD_ADDED']],
+                ],
+                'author' => [
+                    'caption' => _t('_bx_files_entry_info_author'),
+                    'type' => 'textarea',
+                    'html' => 1,
+                    'value' => '<a href="'.$oAuthor->getUrl().'">'.$oAuthor->getDisplayName().'</a>',
+                ],
+            ],
+        ];
+
+        if ($aData[$CNF['FIELD_ALLOW_VIEW_TO']] < 0) {
+            $oProfile = BxDolProfile::getInstance(-$aData[$CNF['FIELD_ALLOW_VIEW_TO']]);
+            if ($oProfile) {
+                $aForm['inputs']['context'] = [
+                    'caption' => _t('_bx_files_entry_info_context'),
+                    'type' => 'textarea',
+                    'html' => 1,
+                    'value' => '<a href="' . $oProfile->getUrl() . '">' . $oProfile->getDisplayName() . '</a>',
+                ];
+            }
+        }
+
+        $oForm = new BxTemplFormView($aForm);
+        return $this->parseHtmlByName('entry-popup.html', [
+            'content' => $oForm->getCode(),
+            'bx_repeat:init_scripts' => [],
+        ]);
+    }
+
     public function getTmplVarsText($aData)
     {
         $aVars = parent::getTmplVarsText($aData);

+ 31 - 0
modules/boonex/files/install/langs/en.xml

@@ -205,6 +205,7 @@
     <string name="_bx_files_grid_column_title_adm_author"><![CDATA[Author]]></string>
     <string name="_bx_files_grid_action_title_adm_edit"><![CDATA[Edit]]></string>
     <string name="_bx_files_grid_action_title_adm_delete"><![CDATA[Delete]]></string>
+    <string name="_bx_files_grid_action_title_adm_clear_reports"><![CDATA[Clear reports]]></string>
     <string name="_bx_files_grid_action_title_adm_more_actions"><![CDATA[More Actions]]></string>
     <string name="_bx_files_grid_action_title_adm_audit_content"><![CDATA[Audit actions]]></string>
   <string name="_bx_files_grid_action_err_delete"><![CDATA[Cannot delete the selected file(s).]]></string>
@@ -242,4 +243,34 @@
 
     <string name="_bx_files_privacy_public"><![CDATA[Public]]></string>
     <string name="_bx_files_privacy_participants"><![CDATA[Participants]]></string>
+
+    <string name="_bx_files_menu_title_view_inline"><![CDATA[File inline menu]]></string>
+    <string name="_bx_files_menu_set_title_view_inline"><![CDATA[File inline menu]]></string>
+    <string name="_bx_files_menu_item_title_system_move_to_entry"><![CDATA[Move to...]]></string>
+    <string name="_bx_files_menu_item_title_move_to_entry"><![CDATA[Move to...]]></string>
+    <string name="_bx_files_menu_item_title_system_info_entry"><![CDATA[Info]]></string>
+    <string name="_bx_files_menu_item_title_info_entry"><![CDATA[Info]]></string>
+    <string name="_bx_files_menu_item_title_system_bookmark"><![CDATA[Bookmark]]></string>
+    <string name="_bx_files_menu_item_title_bookmark"><![CDATA[Bookmark]]></string>
+
+    <string name="_bx_files_menu_item_title_system_sm_size"><![CDATA[Size]]></string>
+    <string name="_bx_files_menu_item_title_sm_size"><![CDATA[Size]]></string>
+
+    <string name="_bx_files_option_link_to_preview"><![CDATA[Link file title to a preview popup instead of a file view page in context blocks]]></string>
+
+    <string name="_bx_files_entry_info_size"><![CDATA[Size]]></string>
+    <string name="_bx_files_entry_info_date"><![CDATA[Date]]></string>
+    <string name="_bx_files_entry_info_author"><![CDATA[By]]></string>
+    <string name="_bx_files_entry_info_context"><![CDATA[Uploaded to]]></string>
+
+    <string name="_bx_files_toolbar_view_table"><![CDATA[Table View]]></string>
+    <string name="_bx_files_toolbar_view_gallery"><![CDATA[Gallery View]]></string>
+    <string name="_bx_files_toolbar_view_bookmarks"><![CDATA[Bookmarked files]]></string>
+    <string name="_bx_files_toolbar_sorting_name"><![CDATA[Order by Name]]></string>
+    <string name="_bx_files_toolbar_sorting_date"><![CDATA[Order by Date]]></string>
+    <string name="_bx_files_toolbar_sorting_type"><![CDATA[Order by Type]]></string>
+    <string name="_bx_files_toolbar_sorting_author"><![CDATA[Order by Author]]></string>
+
+    <string name="_bx_files_txt_error"><![CDATA[File not found]]></string>
+    <string name="_bx_files_txt_permission_denied"><![CDATA[Permission denied]]></string>
 </resources>

+ 1 - 1
modules/boonex/files/install/sql/disable.sql

@@ -13,7 +13,7 @@ DELETE FROM `sys_pages_blocks` WHERE `module` = 'bx_files' OR `object` IN('bx_fi
 -- MENU
 DELETE FROM `sys_objects_menu` WHERE `module` = 'bx_files';
 DELETE FROM `sys_menu_sets` WHERE `module` = 'bx_files';
-DELETE FROM `sys_menu_items` WHERE `module` = 'bx_files' OR `set_name` IN('bx_files_view', 'bx_files_view_actions', 'bx_files_submenu', 'bx_files_view_submenu', 'bx_files_snippet_meta', 'bx_files_my', 'bx_files_menu_manage_tools');
+DELETE FROM `sys_menu_items` WHERE `module` = 'bx_files' OR `set_name` IN('bx_files_view', 'bx_files_view_actions', 'bx_files_submenu', 'bx_files_view_submenu', 'bx_files_snippet_meta', 'bx_files_my', 'bx_files_menu_manage_tools', 'bx_files_view_inline');
 
 -- PRIVACY 
 DELETE FROM `sys_objects_privacy` WHERE `object` IN ('bx_files_allow_view_to', 'bx_files_allow_view_favorite_list');

+ 29 - 5
modules/boonex/files/install/sql/enable.sql

@@ -12,6 +12,7 @@ SET @iCategId = LAST_INSERT_ID();
 
 INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `check`, `check_error`, `extra`, `order`) VALUES
 ('bx_files_summary_chars', '700', @iCategId, '_bx_files_option_summary_chars', 'digit', '', '', '', 1),
+('bx_files_show_link_to_preview', '', @iCategId, '_bx_files_option_link_to_preview', 'checkbox', '', '', '', 5),
 ('bx_files_per_page_browse', '12', @iCategId, '_bx_files_option_per_page_browse', 'digit', '', '', '', 10),
 ('bx_files_per_page_profile', '6', @iCategId, '_bx_files_option_per_page_profile', 'digit', '', '', '', 12),
 ('bx_files_per_page_browse_showcase', '32', @iCategId, '_sys_option_per_page_browse_showcase', 'digit', '', '', '', 15),
@@ -250,6 +251,26 @@ INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `titl
 ('bx_files_view_actions', 'bx_files', 'more-auto', '_sys_menu_item_title_system_va_more_auto', '_sys_menu_item_title_va_more_auto', 'javascript:void(0)', '', '', 'ellipsis-v', '', '', 0, 2147483647, 1, 0, 9999);
 
 
+-- MENU: actions menu for view inline entry
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_files_view_inline', '_bx_files_menu_title_view_inline', 'bx_files_view_inline', 'bx_files', 9, 0, 1, 'BxFilesMenuViewActionsInline', 'modules/boonex/files/classes/BxFilesMenuViewActions.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES
+('bx_files_view_inline', 'bx_files', '_bx_files_menu_set_title_view_inline', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES
+('bx_files_view_inline', 'bx_files', 'bookmark', '_bx_files_menu_item_title_system_bookmark', '_bx_files_menu_item_title_bookmark', '', '{js_object}.bookmark({content_id}, this); return false;', '', 'far star', '', 2147483647, 1, 0, 1),
+('bx_files_view_inline', 'bx_files', 'download-file', '_bx_files_menu_item_title_system_download_file', '_bx_files_menu_item_title_download_file', 'modules/?r=files/download/{file_download_token}/{content_id}.{file_ext}', '', '_blank', 'download', '', 2147483647, 1, 0, 2),
+('bx_files_view_inline', 'bx_files', 'delete-file-quick', '_bx_files_menu_item_title_system_delete_entry', '_bx_files_menu_item_title_delete_entry', 'page.php?i=delete-file&id={content_id}', '{js_object}.delete({content_id}); return false;', '', 'remove', '', 2147483647, 1, 0, 3),
+('bx_files_view_inline', 'bx_files', 'move-to', '_bx_files_menu_item_title_system_move_to_entry', '_bx_files_menu_item_title_move_to_entry', '', 'bx_alert(\'TODO\');return false;', '', 'file-export', '', 2147483647, 1, 0, 4),
+('bx_files_view_inline', 'bx_files', 'edit-title', '_bx_files_menu_item_title_system_edit_entry', '_bx_files_menu_item_title_edit_entry', 'page.php?i=edit-file&id={content_id}', '{js_object}.edit({content_id}); return false;', '', 'pencil-alt', '', 2147483647, 1, 0, 5),
+('bx_files_view_inline', 'bx_files', 'preview', '_bx_files_menu_item_title_system_info_entry', '_bx_files_menu_item_title_info_entry', '', '{js_object}.info({content_id}); return false;', '', 'info', '', 2147483647, 1, 0, 6),
+('bx_files_view_inline', 'bx_files', 'report', '_sys_menu_item_title_system_va_report', '', '', '', '', '', '', 2147483647, 1, 0, 7),
+('bx_files_view_inline', 'bx_files', 'more-auto', '_sys_menu_item_title_system_va_more_auto', '_sys_menu_item_title_va_more_auto', 'javascript:void(0)', '', '', 'ellipsis-v', '', 2147483647, 1, 0, 9999);
+
+
+
 -- MENU: actions menu for my entries
 
 INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
@@ -298,11 +319,12 @@ INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES
 
 INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `editable`, `order`) VALUES 
 ('bx_files_snippet_meta', 'bx_files', 'date', '_sys_menu_item_title_system_sm_date', '_sys_menu_item_title_sm_date', '', '', '', '', '', 2147483647, 1, 0, 1, 1),
-('bx_files_snippet_meta', 'bx_files', 'author', '_sys_menu_item_title_system_sm_author', '_sys_menu_item_title_sm_author', '', '', '', '', '', 2147483647, 1, 0, 1, 2),
-('bx_files_snippet_meta', 'bx_files', 'category', '_sys_menu_item_title_system_sm_category', '_sys_menu_item_title_sm_category', '', '', '', '', '', 2147483647, 0, 0, 1, 3),
-('bx_files_snippet_meta', 'bx_files', 'tags', '_sys_menu_item_title_system_sm_tags', '_sys_menu_item_title_sm_tags', '', '', '', '', '', 2147483647, 0, 0, 1, 4),
-('bx_files_snippet_meta', 'bx_files', 'views', '_sys_menu_item_title_system_sm_views', '_sys_menu_item_title_sm_views', '', '', '', '', '', 2147483647, 0, 0, 1, 5),
-('bx_files_snippet_meta', 'bx_files', 'comments', '_sys_menu_item_title_system_sm_comments', '_sys_menu_item_title_sm_comments', '', '', '', '', '', 2147483647, 0, 0, 1, 6);
+('bx_files_snippet_meta', 'bx_files', 'size', '_bx_files_menu_item_title_system_sm_size', '_bx_files_menu_item_title_sm_size', '', '', '', '', '', 2147483647, 1, 0, 1, 2),
+('bx_files_snippet_meta', 'bx_files', 'author', '_sys_menu_item_title_system_sm_author', '_sys_menu_item_title_sm_author', '', '', '', '', '', 2147483647, 1, 0, 1, 3),
+('bx_files_snippet_meta', 'bx_files', 'category', '_sys_menu_item_title_system_sm_category', '_sys_menu_item_title_sm_category', '', '', '', '', '', 2147483647, 0, 0, 1, 4),
+('bx_files_snippet_meta', 'bx_files', 'tags', '_sys_menu_item_title_system_sm_tags', '_sys_menu_item_title_sm_tags', '', '', '', '', '', 2147483647, 0, 0, 1, 5),
+('bx_files_snippet_meta', 'bx_files', 'views', '_sys_menu_item_title_system_sm_views', '_sys_menu_item_title_sm_views', '', '', '', '', '', 2147483647, 0, 0, 1, 6),
+('bx_files_snippet_meta', 'bx_files', 'comments', '_sys_menu_item_title_system_sm_comments', '_sys_menu_item_title_sm_comments', '', '', '', '', '', 2147483647, 0, 0, 1, 7);
 
 -- MENU: profile stats
 SET @iNotifMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name` = 'sys_profile_stats' AND `active` = 1 LIMIT 1);
@@ -441,10 +463,12 @@ INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable
 
 INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
 ('bx_files_administration', 'bulk', 'delete', '_bx_files_grid_action_title_adm_delete', '', 0, 1, 1),
+('bx_files_administration', 'bulk', 'clear_reports', '_bx_files_grid_action_title_adm_clear_reports', '', 0, 1, 2),
 ('bx_files_administration', 'single', 'edit', '_bx_files_grid_action_title_adm_edit', 'pencil-alt', 1, 0, 1),
 ('bx_files_administration', 'single', 'delete', '_bx_files_grid_action_title_adm_delete', 'remove', 1, 1, 2),
 ('bx_files_administration', 'single', 'settings', '_bx_files_grid_action_title_adm_more_actions', 'cog', 1, 0, 3),
 ('bx_files_administration', 'single', 'audit_content', '_bx_files_grid_action_title_adm_audit_content', 'search', 1, 0, 4),
+('bx_files_administration', 'single', 'clear_reports', '_bx_files_grid_action_title_adm_clear_reports', 'eraser', 1, 0, 5),
 ('bx_files_common', 'bulk', 'delete', '_bx_files_grid_action_title_adm_delete', '', 0, 1, 1),
 ('bx_files_common', 'single', 'edit', '_bx_files_grid_action_title_adm_edit', 'pencil-alt', 1, 0, 1),
 ('bx_files_common', 'single', 'delete', '_bx_files_grid_action_title_adm_delete', 'remove', 1, 1, 2),

+ 7 - 0
modules/boonex/files/install/sql/install.sql

@@ -87,6 +87,13 @@ CREATE TABLE IF NOT EXISTS `bx_files_cmts` (
   FULLTEXT KEY `search_fields` (`cmt_text`)
 );
 
+-- TABLE: bookmarks
+CREATE TABLE IF NOT EXISTS `bx_files_bookmarks` (
+  `object_id` int(11) NOT NULL default '0',
+  `profile_id` int(11) NOT NULL default '0',
+  PRIMARY KEY (`object_id`, `profile_id`)
+);
+
 -- TABLE: votes
 CREATE TABLE IF NOT EXISTS `bx_files_votes` (
   `object_id` int(11) NOT NULL default '0',

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

@@ -1,6 +1,6 @@
 
 -- TABLES
-DROP TABLE IF EXISTS `bx_files_main`, `bx_files_files`, `bx_files_photos_resized`, `bx_files_cmts`, `bx_files_votes`, `bx_files_votes_track`, `bx_files_reactions`, `bx_files_reactions_track`, `bx_files_views_track`, `bx_files_meta_keywords`, `bx_files_meta_mentions`, `bx_files_reports`, `bx_files_reports_track`, `bx_files_favorites_track`, `bx_files_favorites_lists`, `bx_files_scores`, `bx_files_scores_track`;
+DROP TABLE IF EXISTS `bx_files_main`, `bx_files_files`, `bx_files_photos_resized`, `bx_files_cmts`, `bx_files_votes`, `bx_files_votes_track`, `bx_files_reactions`, `bx_files_reactions_track`, `bx_files_views_track`, `bx_files_meta_keywords`, `bx_files_meta_mentions`, `bx_files_reports`, `bx_files_reports_track`, `bx_files_favorites_track`, `bx_files_favorites_lists`, `bx_files_scores`, `bx_files_scores_track`, `bx_files_bookmarks`;
 
 -- STORAGES & TRANSCODERS
 DELETE FROM `sys_objects_storage` WHERE `object` = 'bx_files_files' OR `object` = 'bx_files_photos_resized';

+ 48 - 0
modules/boonex/files/js/menu_tools.js

@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Files Files
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+function BxFilesMenuTools(oOptions) {
+	this._sActionsUrl = oOptions.sActionUrl;
+}
+
+BxFilesMenuTools.prototype.bookmark = function(iContentId, oMenuItem) {
+	$.post(this._sActionsUrl+'bookmark/'+iContentId);
+
+	if ($('i', oMenuItem).hasClass('far')) {
+		$('i', oMenuItem).removeClass('far').addClass('fas');
+	} else {
+		$('i', oMenuItem).removeClass('fas').addClass('far');
+	}
+};
+
+BxFilesMenuTools.prototype.preview = function(iContentId) {
+	$('.bx-popup-wrapper:visible').dolPopupHide();
+	$.get(this._sActionsUrl+'entry_preview/'+iContentId, processJsonData, 'json');
+};
+
+BxFilesMenuTools.prototype.info = function(iContentId) {
+	$('.bx-popup-wrapper:visible').dolPopupHide();
+	$.get(this._sActionsUrl+'entry_info/'+iContentId, processJsonData, 'json');
+};
+
+BxFilesMenuTools.prototype.edit = function(iContentId) {
+	$('.bx-popup-wrapper:visible').dolPopupHide();
+	$.get(this._sActionsUrl+'entry_edit_title/'+iContentId, processJsonData, 'json');
+};
+
+BxFilesMenuTools.prototype.delete = function(iContentId) {
+	$('.bx-popup-wrapper:visible').dolPopupHide();
+	var $this = this;
+	bx_confirm(_t('_Are_you_sure'), function(){
+		$.get($this._sActionsUrl+'entry_delete/'+iContentId, processJsonData, 'json');
+	});
+};
+
+/** @} */

+ 57 - 0
modules/boonex/files/js/toolbar_tools.js

@@ -0,0 +1,57 @@
+function BxFilesBrowserTools(oOptions) {
+    this._sView = oOptions.layout;
+    this._bBookmarks = oOptions.bookmarks;
+    this._sSorting = oOptions.sorting;
+    this._sKeyword = oOptions.keyword;
+    this._sUniq = oOptions.unique_ident;
+    this._aRequestParams = oOptions.aRequestParams;
+    this._iSearchTimeoutId = 0;
+
+    this._oToolbarObject = $('#bx-files-browser-toolbar-' + this._sUniq).get(0);
+}
+
+BxFilesBrowserTools.prototype.reloadBlock = function() {
+    loadDynamicBlockAutoPaginate(this._oToolbarObject, '0', '0', this.getRequestUrl());
+}
+
+BxFilesBrowserTools.prototype.onChangeFilter = function(oFilter) {
+	var $this = this;
+
+	clearTimeout($this._iSearchTimeoutId);
+    $this._iSearchTimeoutId = setTimeout(function () {
+        $this._sKeyword = $(oFilter).val();
+        $this.reloadBlock();
+    }, 500);
+};
+
+BxFilesBrowserTools.prototype.setView = function(val) {
+    this._sView = val;
+    this.reloadBlock();
+}
+
+BxFilesBrowserTools.prototype.toggleBookmarks = function() {
+    this._bBookmarks = this._bBookmarks ? 0 : 1;
+    this.reloadBlock();
+}
+
+BxFilesBrowserTools.prototype.setSorting = function(val) {
+    this._sSorting = val;
+    this.reloadBlock();
+}
+
+BxFilesBrowserTools.prototype.getRequestUrl = function() {
+    return  this._aRequestParams.unit_view_param + '=' + this._sView + '&' +
+            this._aRequestParams.bookmarks_param + '=' + this._bBookmarks + '&' +
+            this._aRequestParams.sorting_param + '=' + this._sSorting + '&' +
+            this._aRequestParams.keyword_param + '=' + this._sKeyword;
+}
+
+BxFilesBrowserTools.prototype.onSelectAll = function(el) {
+    $(el).
+        closest('.bx-files-text-unit-simple-row').
+        siblings().
+        find('input[type=checkbox]').
+        attr('checked', $(el).attr('checked') == 'checked').change(function(){
+            $(el).attr('checked', false);
+        });
+};

+ 116 - 2
modules/boonex/files/template/css/main.css

@@ -9,12 +9,58 @@
     top:0;
     width:100%;
     position:absolute;
-    font-size:8rem; 
-    line-height:11.25rem;
+    font-size:10rem;
+    line-height:12.25rem;
     text-align:center;
     overflow:hidden;
 }
 
+.bx-files-text-unit-gallery .bx-files-filename,
+.bx-files-text-unit-simple-row .bx-files-filename{
+    overflow: hidden;
+    text-overflow: ellipsis;
+    line-height: 2rem;
+    margin: 0px;
+    font-weight: bold;
+    white-space: nowrap;
+}
+
+.bx-files-text-unit-gallery .bx-base-general-unit-meta ul.bx-menu-custom.bx-menu-hor {
+    align-items: center;
+    -webkit-align-items: center;
+    justify-content: center;
+    -webkit-justify-content: center;
+}
+
+.bx-files-text-unit-gallery .bx-files-inline-menu ul.bx-menu-object-bx_files_view_inline li.bx-menu-item span.bx-base-general-entity-action {
+    margin: 0px;
+    vertical-align: top;
+}
+.bx-files-inline-menu ul.bx-menu-object-bx_files_view_inline li.bx-menu-item {
+    white-space: nowrap;
+}
+.bx-files-text-unit-gallery .bx-menu-custom.bx-menu-hor.bx-menu-more-auto {
+    justify-content: center;
+}
+.bx-files-text-unit-gallery {
+    overflow: visible!important;
+}
+.bx-files-text-unit-gallery .bx-base-text-unit-thumb,
+.bx-files-text-unit-gallery .bx-base-text-unit-no-thumb,
+.bx-files-text-unit-gallery .bx-base-text-unit-text {
+    overflow: hidden;
+}
+.bx-media-desktop .bx-files-text-unit-gallery {
+    max-width: 33.25%;
+}
+.bx-media-tablet2 .bx-files-text-unit-gallery {
+    max-width: 49.8%;
+}
+.bx-media-phone2 .bx-files-text-unit-gallery {
+    max-width: 100%;
+}
+
+
 .bx-files-text-unit .bx-base-text-unit-text {
     position:relative;
 }
@@ -37,4 +83,72 @@
 .bx-files-entity-preview .bx-msg-box-container {
     margin: 0;
     padding: 0;
+}
+
+.bx-files-border-thick {
+    border-width: 3px;
+}
+
+/*--- Files Browser Table layout ---*/
+.bx-files-text-unit-simple-row {
+    text-align: left;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+}
+.bx-files-text-unit-simple-row .bx-files-fileinfo {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    display: flex;
+    align-items: center;
+    -webkit-align-items: center;
+}
+.bx-files-text-unit-simple-row .bx-files-fileinfo-text {
+    display: inline-block;
+    overflow: hidden;
+}
+.bx-files-text-unit-simple-row .bx-files-inline-menu ul.bx-menu-object-bx_files_view_inline {
+    float: right;
+}
+.bx-files-text-unit-simple-row .bx-files-inline-menu ul.bx-menu-object-bx_files_view_inline li.bx-menu-item span.bx-base-general-entity-action {
+    margin: 0px;
+    vertical-align: top;
+}
+.bx-files-text-unit-simple-row .bx-files-fileinfo i {
+    font-size: 2.3rem;
+}
+.bx-files-text-unit-simple-row .bx-base-general-unit-meta ul {
+    margin: 0px!important;
+    margin-top: 0.2rem;
+}
+.bx-files-text-unit-simple-row .bx-files-filename {
+    line-height: unset;
+}
+
+
+/*--- Toolbar ---*/
+.bx-files-browser-toolbar {
+    width: 100%;
+    box-sizing: border-box;
+    text-align: left;
+    padding-top: 0px;
+}
+.bx-files-browser-toolbar .bx-form-element-wrapper {
+    display: inline-block;
+    margin: 0px;
+}
+.bx-files-browser-toolbar .bx-btn {
+    float: none;
+    display: inline-block;
+}
+.bx-files-toolbar-controls-group {
+    display: inline-block;
+    margin-right: 10px;
+    white-space: nowrap;
+    margin-bottom: 1rem;
+}
+.bx-files-toolbar-spacer {
+    width: 100%;
+    margin-bottom: 0.5rem;
 }

+ 13 - 0
modules/boonex/files/template/entry-popup.html

@@ -0,0 +1,13 @@
+<div class="bx-def-margin-bottom">
+    __content__
+</div>
+<button class="bx-btn" onclick="$('#bx-files-popup').dolPopupHide();" style="float: none;"><bx_text:_sys_close /></button>
+
+<script>
+    function bx_files_popup_init() {
+        <bx_repeat:init_scripts>
+            __code__
+        </bx_repeat:init_scripts>
+        $(window).resize();
+    }
+</script>

+ 43 - 0
modules/boonex/files/template/files_browser_toolbar.html

@@ -0,0 +1,43 @@
+<div class="bx-def-padding-sec-leftright bx-def-border-bottom bx-files-border-thick bx-files-browser-toolbar bx-clearfix" id="bx-files-browser-toolbar-__unique_ident__">
+    <bx_if:upload_visible>
+    <div class="bx-files-toolbar-controls-group">
+        <button class="bx-btn" onclick="bx_alert('TODO'); return false;"><i class="sys-icon upload"></i>&nbsp;Upload</button>
+        <button class="bx-btn" onclick="bx_alert('TODO'); return false;"><i class="sys-icon plus"></i>&nbsp;Add Folder</button>
+    </div>
+    </bx_if:upload_visible>
+
+    <div class="bx-files-toolbar-controls-group">
+        <bx_if:table_layout_btn>
+            <button class="bx-btn bx-btn-disabled" title="<bx_text:_bx_files_toolbar_view_table />"><i class="sys-icon list"></i></button>
+            <button class="bx-btn" onclick="__js_object__.setView('gallery');" title="<bx_text:_bx_files_toolbar_view_gallery />"><i class="sys-icon th-large"></i></button>
+        </bx_if:table_layout_btn>
+        <bx_if:gallery_layout_btn>
+            <button class="bx-btn" onclick="__js_object__.setView('table');"  title="<bx_text:_bx_files_toolbar_view_table />"><i class="sys-icon list"></i></button>
+            <button class="bx-btn bx-btn-disabled" title="<bx_text:_bx_files_toolbar_view_gallery />"><i class="sys-icon th-large"></i></button>
+        </bx_if:gallery_layout_btn>
+    </div>
+
+    <bx_if:bookmarks_visible>
+    <div class="bx-files-toolbar-controls-group">
+        <button class="bx-btn" onclick="__js_object__.toggleBookmarks();" title="<bx_text:_bx_files_toolbar_view_bookmarks />"><i class="sys-icon __bookmark_enabled__ star"></i></button>
+    </div>
+    </bx_if:bookmarks_visible>
+
+    <div class="bx-files-toolbar-controls-group">
+        __sorting_dropdown__
+        __filter_box__
+    </div>
+</div>
+
+<div class="bx-files-toolbar-spacer"></div>
+
+<bx_if:select_all_checkbox>
+<div class="bx-def-padding-sec bx-files-text-unit-simple-row">
+    <div>
+        <input type="checkbox" class="bx-def-font-inputs bx-form-input-checkbox bx-def-margin-right" id="bx-files-browser-select-all-__unique_ident__" onchange="__js_object__.onSelectAll(this);" />
+        <label for="bx-files-browser-select-all-__unique_ident__"><bx_text:_Select_all /></label>
+    </div>
+</div>
+</bx_if:select_all_checkbox>
+
+__js_code__

+ 17 - 11
modules/boonex/files/template/unit_gallery.html

@@ -1,13 +1,7 @@
-<div class="__class__ bx-files-text-unit-gallery bx-base-text-unit bx-base-text-unit-gallery bx-def-gal-unit-size bx-def-padding-sec bx-def-margin-sec-bottom">
+<div class="__class__ bx-files-text-unit-gallery bx-base-text-unit bx-base-text-unit-gallery bx-def-gal-unit-size bx-def-padding-sec bx-def-margin-sec-bottom bx-file-entry" file_id="__id__">
 	<div class="bx-base-text-unit-cnt bx-def-box bx-def-round-corners bx-def-color-bg-box bx-def-box-sizing">
-		<div class="bx-base-text-unit-text bx-def-padding-leftright bx-def-padding-sec-topbottom bx-def-font-align-center bx-def-box-sizing">
-			<h2><a href="__content_url__">__title__</a></h2>
-		    <div class="bx-base-text-unit-info">
-                <bx_if:meta><div class="bx-base-general-unit-meta">__meta__</div></bx_if:meta>
-            </div>
-		</div>
-	    <bx_if:thumb>
-	        <a href="__content_url__" title="__summary_attr__">
+		<bx_if:thumb>
+	        <a href="__content_url__" title="__summary_attr__" onclick="__alt_content_url_handler__">
 	            <div class="bx-base-text-unit-thumb bx-def-gal-div-size bx-def-round-corners bx-def-font-middle" style="background-image:url(__gallery_url__)">
 	                <div class="bx-base-text-unit-strecher">__strecher__</div>
 	            </div>
@@ -15,12 +9,24 @@
 	    </bx_if:thumb>
 	    <bx_if:no_thumb>
 	        <div class="bx-def-gal-img-column">
-                <a href="__content_url__">
+                <a href="__content_url__" onclick="__alt_content_url_handler__">
     	            <div class="bx-base-text-unit-no-thumb bx-def-gal-div-size bx-def-font-middle bx-def-font-grayed bx-def-padding bx-def-box-sizing">
-                        <i class="sys-icon __icon__ "></i>
+                        <i class="sys-icon __icon__ sys-colored col-gray"></i>
     	            </div>
                 </a>
 	        </div>
 	    </bx_if:no_thumb>
+		<div class="bx-base-text-unit-text bx-def-padding-leftright bx-def-padding-sec-topbottom bx-def-font-align-center bx-def-box-sizing">
+			<div class="bx-files-filename bx-def-font-large"><a href="__content_url__" onclick="__alt_content_url_handler__">__title__</a></div>
+		    <div class="bx-base-text-unit-info">
+                <bx_if:meta><div class="bx-base-general-unit-meta">__meta__</div></bx_if:meta>
+            </div>
+		</div>
+
+		<bx_if:inline_menu>
+		<div class="bx-files-inline-menu bx-clearfix bx-def-margin-bottom">
+			__menu__
+		</div>
+		</bx_if:inline_menu>
 	</div>
 </div>

+ 15 - 0
modules/boonex/files/template/unit_simple_row.html

@@ -0,0 +1,15 @@
+<div class="__class__ bx-def-padding-sec bx-def-border-top bx-files-border-thick bx-files-text-unit-simple-row bx-file-entry" file_id="__id__">
+    <div class="bx-files-fileinfo">
+        <input type="checkbox" class="bx-def-font-inputs bx-form-input-checkbox bx-def-margin-right" name="file[]" value="__id__" />
+        <i class="sys-icon __icon__ sys-colored bx-def-font-large bx-def-padding-sec-right col-gray"></i>
+        <div class="bx-files-fileinfo-text">
+            <div class="bx-files-filename"><a href="__content_url__" onclick="__alt_content_url_handler__">__title__</a></div>
+            <bx_if:meta><div class="bx-base-general-unit-meta">__meta__</div></bx_if:meta>
+        </div>
+    </div>
+    <bx_if:inline_menu>
+    <div class="bx-files-inline-menu bx-clearfix">
+        __menu__
+    </div>
+    </bx_if:inline_menu>
+</div>

+ 1 - 0
modules/boonex/forum/install/langs/en.xml

@@ -308,6 +308,7 @@
     <string name="_bx_forum_grid_column_title_visible_for_levels"><![CDATA[Visible For]]></string>
     <string name="_bx_forum_grid_action_title_adm_edit"><![CDATA[Edit]]></string>
     <string name="_bx_forum_grid_action_title_adm_delete"><![CDATA[Delete]]></string>
+    <string name="_bx_forum_grid_action_title_adm_clear_reports"><![CDATA[Clear reports]]></string>
     <string name="_bx_forum_grid_action_title_adm_more_actions"><![CDATA[More Actions]]></string>
     <string name="_bx_forum_grid_action_title_adm_audit_content"><![CDATA[Audit actions]]></string>
     <string name="_bx_forum_grid_action_title_add"><![CDATA[New discussion]]></string>

+ 2 - 0
modules/boonex/forum/install/sql/enable.sql

@@ -429,10 +429,12 @@ INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable
 
 INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
 ('bx_forum_administration', 'bulk', 'delete', '_bx_forum_grid_action_title_adm_delete', '', 0, 1, 1),
+('bx_forum_administration', 'bulk', 'clear_reports', '_bx_forum_grid_action_title_adm_clear_reports', '', 0, 1, 2),
 ('bx_forum_administration', 'single', 'edit', '_bx_forum_grid_action_title_adm_edit', 'pencil-alt', 1, 0, 1),
 ('bx_forum_administration', 'single', 'delete', '_bx_forum_grid_action_title_adm_delete', 'remove', 1, 1, 2),
 ('bx_forum_administration', 'single', 'settings', '_bx_forum_grid_action_title_adm_more_actions', 'cog', 1, 0, 3),
 ('bx_forum_administration', 'single', 'audit_content', '_bx_forum_grid_action_title_adm_audit_content', 'search', 1, 0, 4),
+('bx_forum_administration', 'single', 'clear_reports', '_bx_forum_grid_action_title_adm_clear_reports', 'eraser', 1, 0, 5),
 
 ('bx_forum_common', 'bulk', 'delete', '_bx_forum_grid_action_title_adm_delete', '', 0, 1, 1),
 ('bx_forum_common', 'single', 'edit', '_bx_forum_grid_action_title_adm_edit', 'pencil-alt', 1, 0, 1),

+ 1 - 0
modules/boonex/glossary/install/langs/en.xml

@@ -203,6 +203,7 @@
     <string name="_bx_glossary_grid_column_title_adm_author"><![CDATA[Author]]></string>
     <string name="_bx_glossary_grid_action_title_adm_edit"><![CDATA[Edit]]></string>
     <string name="_bx_glossary_grid_action_title_adm_delete"><![CDATA[Delete]]></string>
+    <string name="_bx_glossary_grid_action_title_adm_clear_reports"><![CDATA[Clear reports]]></string>
     <string name="_bx_glossary_grid_action_title_adm_more_actions"><![CDATA[More Actions]]></string>
     <string name="_bx_glossary_grid_action_title_adm_audit_content"><![CDATA[Audit actions]]></string>  
     <string name="_bx_glossary_grid_action_err_delete"><![CDATA[Cannot delete the selected term(s).]]></string>

+ 2 - 0
modules/boonex/glossary/install/sql/enable.sql

@@ -449,10 +449,12 @@ INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable
 
 INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
 ('bx_glossary_administration', 'bulk', 'delete', '_bx_glossary_grid_action_title_adm_delete', '', 0, 1, 1),
+('bx_glossary_administration', 'bulk', 'clear_reports', '_bx_glossary_grid_action_title_adm_clear_reports', '', 0, 1, 2),
 ('bx_glossary_administration', 'single', 'edit', '_bx_glossary_grid_action_title_adm_edit', 'pencil-alt', 1, 0, 1),
 ('bx_glossary_administration', 'single', 'delete', '_bx_glossary_grid_action_title_adm_delete', 'remove', 1, 1, 2),
 ('bx_glossary_administration', 'single', 'settings', '_bx_glossary_grid_action_title_adm_more_actions', 'cog', 1, 0, 3),
 ('bx_glossary_administration', 'single', 'audit_content', '_bx_glossary_grid_action_title_adm_audit_content', 'search', 1, 0, 4),
+('bx_glossary_administration', 'single', 'clear_reports', '_bx_glossary_grid_action_title_adm_clear_reports', 'eraser', 1, 0, 5),
 ('bx_glossary_common', 'bulk', 'delete', '_bx_glossary_grid_action_title_adm_delete', '', 0, 1, 1),
 ('bx_glossary_common', 'single', 'edit', '_bx_glossary_grid_action_title_adm_edit', 'pencil-alt', 1, 0, 1),
 ('bx_glossary_common', 'single', 'delete', '_bx_glossary_grid_action_title_adm_delete', 'remove', 1, 1, 2),

+ 3 - 0
modules/boonex/groups/install/langs/en.xml

@@ -211,6 +211,8 @@
     <string name="_bx_groups_menu_item_title_delete"><![CDATA[Delete]]></string>
     <string name="_bx_groups_menu_item_title_system_delete_with_content"><![CDATA[Delete With Content]]></string>
     <string name="_bx_groups_menu_item_title_delete_with_content"><![CDATA[Delete With Content]]></string>
+    <string name="_bx_groups_menu_item_title_system_clear_reports"><![CDATA[Clear reports]]></string>
+    <string name="_bx_groups_menu_item_title_clear_reports"><![CDATA[Clear reports]]></string>
     <string name="_bx_groups_menu_item_title_system_edit_cover"><![CDATA[Edit Cover]]></string>
     <string name="_bx_groups_menu_item_title_edit_cover"><![CDATA[Edit Cover]]></string>
     <string name="_bx_groups_menu_item_title_system_edit_pricing"><![CDATA[Edit Pricing]]></string>
@@ -306,6 +308,7 @@
     <string name="_bx_groups_grid_action_title_adm_audit_content"><![CDATA[Audit actions with context]]></string>
     <string name="_bx_groups_grid_action_title_adm_delete"><![CDATA[Delete]]></string>
     <string name="_bx_groups_grid_action_title_adm_delete_with_content"><![CDATA[Delete With Content]]></string>
+    <string name="_bx_groups_grid_action_title_adm_clear_reports"><![CDATA[Clear reports]]></string>
     <string name="_bx_groups_grid_action_title_adm_more_actions"><![CDATA[More Actions]]></string>
     <string name="_bx_groups_grid_action_err_delete"><![CDATA[Cannot delete the selected group(s).]]></string>
     <string name="_bx_groups_grid_txt_account_manager"><![CDATA[Redirect to manage account.]]></string>

+ 3 - 1
modules/boonex/groups/install/sql/enable.sql

@@ -390,7 +390,8 @@ INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES
 
 INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
 ('bx_groups_menu_manage_tools', 'bx_groups', 'delete', '_bx_groups_menu_item_title_system_delete', '_bx_groups_menu_item_title_delete', 'javascript:void(0)', 'javascript:{js_object}.onClickDelete({content_id});', '_self', 'far trash-alt', '', 2147483647, 1, 0, 1),
-('bx_groups_menu_manage_tools', 'bx_groups', 'delete-with-content', '_bx_groups_menu_item_title_system_delete_with_content', '_bx_groups_menu_item_title_delete_with_content', 'javascript:void(0)', 'javascript:{js_object}.onClickDeleteWithContent({content_id});', '_self', 'far trash-alt', '', 2147483647, 1, 0, 2);
+('bx_groups_menu_manage_tools', 'bx_groups', 'delete-with-content', '_bx_groups_menu_item_title_system_delete_with_content', '_bx_groups_menu_item_title_delete_with_content', 'javascript:void(0)', 'javascript:{js_object}.onClickDeleteWithContent({content_id});', '_self', 'far trash-alt', '', 2147483647, 1, 0, 2),
+('bx_groups_menu_manage_tools', 'bx_groups', 'clear-reports', '_bx_groups_menu_item_title_system_clear_reports', '_bx_groups_menu_item_title_clear_reports', 'javascript:void(0)', 'javascript:{js_object}.onClickClearReports({content_id});', '_self', 'eraser', '', 2147483647, 1, 0, 3);
 
 -- MENU: dashboard manage tools
 SET @iManageMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name`='sys_account_dashboard_manage_tools' LIMIT 1);
@@ -553,6 +554,7 @@ INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable
 INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
 ('bx_groups_administration', 'bulk', 'delete', '_bx_groups_grid_action_title_adm_delete', '', 0, 1, 2),
 ('bx_groups_administration', 'bulk', 'delete_with_content', '_bx_groups_grid_action_title_adm_delete_with_content', '', 0, 1, 3),
+('bx_groups_administration', 'bulk', 'clear_reports', '_bx_groups_grid_action_title_adm_clear_reports', '', 0, 1, 4),
 ('bx_groups_administration', 'single', 'settings', '_bx_groups_grid_action_title_adm_more_actions', 'cog', 1, 0, 2),
 ('bx_groups_administration', 'single', 'audit_content', '_bx_groups_grid_action_title_adm_audit_content', 'search', 1, 0, 3),
 ('bx_groups_administration', 'single', 'audit_context', '_bx_groups_grid_action_title_adm_audit_context', 'search-location', 1, 0, 4),

+ 7 - 0
modules/boonex/groups/js/manage_tools.js

@@ -59,4 +59,11 @@ BxGroupsManageTools.prototype.onClickDeleteWithContent = function(iContentId) {
 	glGrids[this._sObjNameGrid].actionWithId(iContentId, 'delete_with_content', {}, '', false, 1);
 };
 
+BxGroupsManageTools.prototype.onClickClearReports = function (iContentId) {
+    $('.bx-popup-applied:visible').dolPopupHide();
+
+    glGrids[this._sObjNameGrid].actionWithId(iContentId, 'clear_reports', {}, '', false, 1);
+};
+
+
 /** @} */

+ 1 - 1
modules/boonex/invites/js/main.js

@@ -62,7 +62,7 @@ BxInvMain.prototype.showLinkPopup = function(oElement) {
         				opacity: .7
                     },
                     onShow: function () {
-                    	oClipboard = new Clipboard('#' + $this._aHtmlIds['link_popup'] + ' .bx-btn[name = "clipboard"]', {
+                    	oClipboard = new ClipboardJS('#' + $this._aHtmlIds['link_popup'] + ' .bx-btn[name = "clipboard"]', {
                     	    target: function(oTrigger) {
                     	        return $('#' + sPopupId).find('[name = "link"]').get(0);
                     	    }

+ 1 - 0
modules/boonex/market/install/langs/en.xml

@@ -343,6 +343,7 @@
     <string name="_bx_market_grid_action_title_adm_delete"><![CDATA[Delete]]></string>
     <string name="_bx_market_grid_action_title_adm_more_actions"><![CDATA[More Actions]]></string>
     <string name="_bx_market_grid_action_title_adm_audit_content"><![CDATA[Audit actions]]></string>
+    <string name="_bx_market_grid_action_title_adm_clear_reports"><![CDATA[Clear reports]]></string>
     <string name="_bx_market_grid_action_err_delete"><![CDATA[Cannot delete the selected product(s).]]></string>
     <string name="_bx_market_grid_txt_account_manager"><![CDATA[Redirect to manage account.]]></string>
 

+ 2 - 0
modules/boonex/market/install/sql/enable.sql

@@ -611,10 +611,12 @@ INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable
 
 INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
 ('bx_market_administration', 'bulk', 'delete', '_bx_market_grid_action_title_adm_delete', '', 0, 1, 1),
+('bx_market_administration', 'bulk', 'clear_reports', '_bx_market_grid_action_title_adm_clear_reports', '', 0, 1, 2),
 ('bx_market_administration', 'single', 'edit', '_bx_market_grid_action_title_adm_edit', 'pencil-alt', 1, 0, 1),
 ('bx_market_administration', 'single', 'delete', '_bx_market_grid_action_title_adm_delete', 'remove', 1, 1, 2),
 ('bx_market_administration', 'single', 'settings', '_bx_market_grid_action_title_adm_more_actions', 'cog', 1, 0, 3),
 ('bx_market_administration', 'single', 'audit_content', '_bx_market_grid_action_title_adm_audit_content', 'search', 1, 0, 4),
+('bx_market_administration', 'single', 'clear_reports', '_bx_market_grid_action_title_adm_clear_reports', 'eraser', 1, 0, 5),
 ('bx_market_common', 'bulk', 'delete', '_bx_market_grid_action_title_adm_delete', '', 0, 1, 1),
 ('bx_market_common', 'single', 'edit', '_bx_market_grid_action_title_adm_edit', 'pencil-alt', 1, 0, 1),
 ('bx_market_common', 'single', 'delete', '_bx_market_grid_action_title_adm_delete', 'remove', 1, 1, 2),

+ 5 - 2
modules/boonex/massmailer/classes/BxMassMailerModule.php

@@ -58,7 +58,7 @@ class BxMassMailerModule extends BxBaseModGeneralModule
     
     public function actionGetReportsData($sReportName, $sReportType, $sDateFrom, $sDateTo, $sSegment)
     {
-        if(!BxDolAcl::getInstance()->isMemberLevelInSet(128))
+        if($this->checkAllowed() !== CHECK_ACTION_RESULT_ALLOWED)
             return '';
         
         header('Content-Type: application/json');
@@ -341,6 +341,9 @@ class BxMassMailerModule extends BxBaseModGeneralModule
      */
     public function serviceTotalSubscribers ()
     {
+        if($this->checkAllowed() !== CHECK_ACTION_RESULT_ALLOWED)
+            return '';
+        
         return $this->_oTemplate->getTotalSubscribers();
     }
     
@@ -537,7 +540,7 @@ class BxMassMailerModule extends BxBaseModGeneralModule
         return $this->_oDb->getAccountsByTerms($sTerms);
     }
     
-    private function checkAllowed($isPerformAction = false)
+    public function checkAllowed($isPerformAction = false)
     {
         $aCheck = checkActionModule($this->_iProfileId, 'use massmailer', $this->getName(), $isPerformAction);
         if ($aCheck[CHECK_ACTION_RESULT] !== CHECK_ACTION_RESULT_ALLOWED)

+ 1 - 1
modules/boonex/massmailer/install/sql/enable.sql

@@ -70,7 +70,7 @@ INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `tit
 
 -- GRIDS: campaigns administration
 INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
-('bx_massmailer_campaigns', 'Sql', 'SELECT * FROM `bx_massmailer_campaigns` WHERE 1 ', 'bx_massmailer_campaigns', 'id', 'added', '', '', 20, NULL, 'start', '', 'title', '', 'like', '', '', 128, 'BxMassMailerGridCampaigns', 'modules/boonex/massmailer/classes/BxMassMailerGridCampaigns.php');
+('bx_massmailer_campaigns', 'Sql', 'SELECT * FROM `bx_massmailer_campaigns` WHERE 1 ', 'bx_massmailer_campaigns', 'id', 'added', '', '', 20, NULL, 'start', '', 'title', '', 'like', '', '', 2147483647, 'BxMassMailerGridCampaigns', 'modules/boonex/massmailer/classes/BxMassMailerGridCampaigns.php');
 
 INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
 ('bx_massmailer_campaigns', 'checkbox', '_sys_select', '2%', 0, '', '', 1),

+ 3 - 2
modules/boonex/oauth2/classes/BxOAuthModule.php

@@ -131,9 +131,10 @@ class BxOAuthModule extends BxDolModule
         );
         $this->_oStorage = new BxOAuthUserCredentialsStorage(BxDolDb::getLink(), $aConfig);
 
+        $iRefreshTokenLifetime = (int)getParam('bx_oauth2_refresh_token_lifetime');
         $this->_oServer = new OAuth2\Server($this->_oStorage, array(
             'require_exact_redirect_uri' => false,
-            'refresh_token_lifetime' => 7776000, // set lifetime to 90 days
+            'refresh_token_lifetime' => $iRefreshTokenLifetime ? $iRefreshTokenLifetime : 7779000, // set lifetime to 90 days
         ));
 
         // add grand types
@@ -153,7 +154,7 @@ class BxOAuthModule extends BxDolModule
         // Add the "Refresh Token" grant type
         if (in_array('refresh_token', $aGrantTypes))
             $this->_oServer->addGrantType(new OAuth2\GrantType\RefreshToken($this->_oStorage, array(
-                'always_issue_new_refresh_token' => true,
+                'always_issue_new_refresh_token' => getParam('bx_oauth2_always_issue_new_refresh_token') ? true : false,
                 // 'unset_refresh_token_after_use' => false,
             )));
     }    

+ 6 - 5
modules/boonex/oauth2/classes/BxOAuthStudioPage.php

@@ -13,19 +13,20 @@ class BxOAuthStudioPage extends BxTemplStudioModule
 {
     protected $oModule;
 
-    function __construct($sModule, $mixedPageName, $sPage = "")
+    function __construct($sModule = "", $sPage = "")
     {
-        parent::__construct($sModule, $mixedPageName, $sPage);
+        parent::__construct($sModule, $sPage);
 
         $this->oModule = BxDolModule::getInstance('bx_oauth');
 
         $this->aMenuItems = array(
-            'settings' => array('name' => 'settings', 'icon' => 'cogs', 'title' => '_adm_lmi_cpt_settings'),
-            'help' => array('name' => 'help', 'icon' => 'question', 'title' => '_bx_oauth_help'),
+            array('name' => 'settings', 'icon' => 'cogs', 'title' => '_adm_lmi_cpt_settings'),
+            array('name' => 'keys', 'icon' => 'key', 'title' => '_adm_lmi_cpt_keys'),
+            array('name' => 'help', 'icon' => 'question', 'title' => '_bx_oauth_help'),
         );
     }
 
-    function getSettings ()
+    function getKeys ()
     {
     	$this->oModule->_oTemplate->addStudioJs(array('BxDolGrid.js'));
     	$this->oModule->_oTemplate->addStudioCss(array('grid.css'));

+ 2 - 0
modules/boonex/oauth2/install/langs/en.xml

@@ -21,4 +21,6 @@ Specify the following "Main UNA Site URL" on the client sites:<br />
 
 	<string name="_bx_oauth_adm_stg_cpt_type"><![CDATA[OAuth2 Server]]></string>
 	<string name="_bx_oauth_adm_stg_cpt_category_general"><![CDATA[General]]></string>
+	<string name="_bx_oauth_adm_stg_opt_refresh_token_lifetime"><![CDATA[Refresh token lifetime (in seconds)]]></string>
+	<string name="_bx_oauth_adm_stg_opt_always_issue_new_refresh_token"><![CDATA[Always issue new refresh token]]></string>
 </resources>

+ 3 - 2
modules/boonex/oauth2/install/sql/enable.sql

@@ -28,8 +28,9 @@ INSERT INTO `sys_options_categories` (`type_id`, `name`, `caption`, `order` )
 VALUES (@iTypeId, 'bx_oauth_general', '_bx_oauth_adm_stg_cpt_category_general', 1);
 SET @iCategId = LAST_INSERT_ID();
 
--- INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `check`, `check_error`, `order`, `extra`) VALUES
--- ('bx_oauth2_on', '', @iCategId, 'Enable OAuth2 Server', 'checkbox', '', '', '10', '');
+INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `check`, `check_error`, `order`, `extra`) VALUES
+('bx_oauth2_refresh_token_lifetime', '7776000', @iCategId, '_bx_oauth_adm_stg_opt_refresh_token_lifetime', 'digit', '', '', '10', ''),
+('bx_oauth2_always_issue_new_refresh_token', 'on', @iCategId, '_bx_oauth_adm_stg_opt_always_issue_new_refresh_token', 'checkbox', '', '', '20', '');
 
 -- Pages
 

+ 5 - 1
modules/boonex/organizations/classes/BxOrgsConfig.php

@@ -248,7 +248,11 @@ class BxOrgsConfig extends BxBaseModGroupsConfig
             	'txt_browse_favorites' => '_bx_orgs_page_title_browse_favorites',
             	'option_redirect_aadd_profile' => '_bx_orgs_option_redirect_aadd_profile',
             	'option_redirect_aadd_last' => '_bx_orgs_option_redirect_aadd_last',
-            	'option_redirect_aadd_custom' => '_bx_orgs_option_redirect_aadd_custom'
+            	'option_redirect_aadd_custom' => '_bx_orgs_option_redirect_aadd_custom',
+                'option_activation_on' => '_bx_orgs_option_activation_on',
+            	'option_activation_off' => '_bx_orgs_option_activation_off',
+            	'option_activation_add' => '_bx_orgs_option_activation_add',
+                'option_activation_edit' => '_bx_orgs_option_activation_edit'
             ),
         ));
 

+ 1 - 1
modules/boonex/organizations/classes/BxOrgsMenuViewActionsAll.php

@@ -38,7 +38,7 @@ class BxOrgsMenuViewActionsAll extends BxBaseModGroupsMenuViewActionsAll
 
     protected function _getMenuItemDeleteOrganizationProfile($aItem)
     {
-        return $this->_getMenuItemByNameActionsMore($aItem);
+        return $this->_getMenuItemByNameActionDelete($aItem);
     }
 
     protected function _getMenuItemDeleteOrganizationAccount($aItem)

+ 9 - 2
modules/boonex/organizations/install/langs/en.xml

@@ -4,7 +4,7 @@
     <string name="_bx_organizations"><![CDATA[Organizations]]></string>
     <string name="_bx_orgs_cmts"><![CDATA[Organizations' Comments]]></string>
 
-    <string name="_bx_orgs_option_autoapproval"><![CDATA[Activate all organizations after creation automatically]]></string>
+    <string name="_bx_orgs_option_autoapproval"><![CDATA[Activate all organizations automatically]]></string>
     <string name="_bx_orgs_option_default_acl_level"><![CDATA[Default level to assign after organizations's profile creation]]></string>
     <string name="_bx_orgs_option_enable_profile_activation_letter"><![CDATA[Enable profile activation letter]]></string>
     <string name="_bx_orgs_option_redirect_aadd"><![CDATA[Redirect after organizations's profile creation to]]></string>
@@ -12,6 +12,10 @@
     <string name="_bx_orgs_option_redirect_aadd_last"><![CDATA[Last Page]]></string>
     <string name="_bx_orgs_option_redirect_aadd_custom"><![CDATA[Custom Page]]></string>
     <string name="_bx_orgs_option_redirect_aadd_custom_url"><![CDATA[URL for 'Custom Page' redirect]]></string>
+    <string name="_bx_orgs_option_activation_on"><![CDATA[After creation & after editing]]></string>
+    <string name="_bx_orgs_option_activation_add"><![CDATA[After creation only]]></string>
+    <string name="_bx_orgs_option_activation_edit"><![CDATA[After editing only]]></string>
+    <string name="_bx_orgs_option_activation_off"><![CDATA[Manual activation]]></string>
     <string name="_bx_orgs_option_num_connections_quick"><![CDATA[Number of items in friends block]]></string>
     <string name="_bx_orgs_option_per_page_browse"><![CDATA[Number of items per page]]></string>
     <string name="_bx_orgs_option_num_rss"><![CDATA[Number of items in RSS feed]]></string>
@@ -48,7 +52,7 @@
     <string name="_bx_orgs_form_profile_input_initial_members"><![CDATA[Invite Members]]></string>
     <string name="_bx_orgs_form_profile_input_sys_org_name"><![CDATA[Organization Name]]></string>
     <string name="_bx_orgs_form_profile_input_org_name"><![CDATA[Organization Name]]></string>
-    <string name="_bx_orgs_form_profile_input_org_name_err"><![CDATA[This information is essential. Please, fill in this field.]]></string>
+    <string name="_bx_orgs_form_profile_input_org_name_err"><![CDATA[Profile name is mandatory and and cannot start with @]]></string>
     <string name="_bx_orgs_form_profile_input_sys_org_desc"><![CDATA[Description]]></string>
     <string name="_bx_orgs_form_profile_input_org_desc"><![CDATA[Description]]></string>
     <string name="_bx_orgs_form_profile_input_sys_org_cat"><![CDATA[Category]]></string>
@@ -228,6 +232,8 @@
     <string name="_bx_orgs_menu_item_title_delete"><![CDATA[Delete]]></string>
     <string name="_bx_orgs_menu_item_title_system_delete_with_content"><![CDATA[Delete With Content]]></string>
     <string name="_bx_orgs_menu_item_title_delete_with_content"><![CDATA[Delete With Content]]></string>
+    <string name="_bx_orgs_menu_item_title_system_clear_reports"><![CDATA[Clear reports]]></string>
+    <string name="_bx_orgs_menu_item_title_clear_reports"><![CDATA[Clear reports]]></string>
     <string name="_bx_orgs_menu_item_title_system_edit_cover"><![CDATA[Edit Cover]]></string>
     <string name="_bx_orgs_menu_item_title_edit_cover"><![CDATA[Edit Cover]]></string>
     <string name="_bx_orgs_menu_item_title_system_edit_profile"><![CDATA[Edit Organization]]></string>
@@ -337,6 +343,7 @@
     <string name="_bx_orgs_grid_column_title_adm_account"><![CDATA[Account]]></string>
     <string name="_bx_orgs_grid_action_title_adm_delete"><![CDATA[Delete]]></string>
     <string name="_bx_orgs_grid_action_title_adm_delete_with_content"><![CDATA[Delete With Content]]></string>
+    <string name="_bx_orgs_grid_action_title_adm_clear_reports"><![CDATA[Clear reports]]></string>
     <string name="_bx_orgs_grid_action_title_adm_set_acl_level"><![CDATA[Set Membership]]></string>
     <string name="_bx_orgs_grid_action_title_adm_more_actions"><![CDATA[More Actions]]></string>
     <string name="_bx_orgs_grid_action_title_adm_audit_content"><![CDATA[Audit actions with profile]]></string>

+ 5 - 3
modules/boonex/organizations/install/sql/enable.sql

@@ -11,7 +11,7 @@ VALUES (@iTypeId, 'bx_organizations', '_bx_orgs', 1);
 SET @iCategId = LAST_INSERT_ID();
 
 INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `extra`, `check`, `check_error`, `order`) VALUES
-('bx_organizations_autoapproval', 'on', @iCategId, '_bx_orgs_option_autoapproval', 'checkbox', '', '', '', 1),
+('bx_organizations_autoapproval', 'on', @iCategId, '_bx_orgs_option_autoapproval', 'select', 'a:2:{s:6:"module";s:16:"bx_organizations";s:6:"method";s:22:"get_options_activation";}', '', '', 1),
 ('bx_organizations_default_acl_level', '3', @iCategId, '_bx_orgs_option_default_acl_level', 'select', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:15:"get_memberships";s:6:"params";a:4:{s:11:"purchasable";b:0;s:6:"active";b:1;s:9:"translate";b:1;s:22:"filter_out_auto_levels";b:1;}s:5:"class";s:16:"TemplAclServices";}', '', '', 2),
 ('bx_organizations_redirect_aadd', 'profile', @iCategId, '_bx_orgs_option_redirect_aadd', 'select', 'a:2:{s:6:"module";s:16:"bx_organizations";s:6:"method";s:30:"get_options_redirect_after_add";}', '', '', 3),
 ('bx_organizations_redirect_aadd_custom_url', '', @iCategId, '_bx_orgs_option_redirect_aadd_custom_url', 'digit', '', '', '', 4),
@@ -429,7 +429,8 @@ INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES
 
 INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
 ('bx_organizations_menu_manage_tools', 'bx_organizations', 'delete', '_bx_orgs_menu_item_title_system_delete', '_bx_orgs_menu_item_title_delete', 'javascript:void(0)', 'javascript:{js_object}.onClickDelete({content_id});', '_self', 'far trash-alt', '', 2147483647, 1, 0, 1),
-('bx_organizations_menu_manage_tools', 'bx_organizations', 'delete-with-content', '_bx_orgs_menu_item_title_system_delete_with_content', '_bx_orgs_menu_item_title_delete_with_content', 'javascript:void(0)', 'javascript:{js_object}.onClickDeleteWithContent({content_id});', '_self', 'far trash-alt', '', 2147483647, 1, 0, 2);
+('bx_organizations_menu_manage_tools', 'bx_organizations', 'delete-with-content', '_bx_orgs_menu_item_title_system_delete_with_content', '_bx_orgs_menu_item_title_delete_with_content', 'javascript:void(0)', 'javascript:{js_object}.onClickDeleteWithContent({content_id});', '_self', 'far trash-alt', '', 2147483647, 1, 0, 2),
+('bx_organizations_menu_manage_tools', 'bx_organizations', 'clear-reports', '_bx_corgs_menu_item_title_system_clear_reports', '_bx_orgs_menu_item_title_clear_reports', 'javascript:void(0)', 'javascript:{js_object}.onClickClearReports({content_id});', '_self', 'eraser', '', 2147483647, 1, 0, 3);
 
 -- MENU: dashboard manage tools
 SET @iManageMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name`='sys_account_dashboard_manage_tools' LIMIT 1);
@@ -555,7 +556,7 @@ INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `conf
 
 -- GRIDS: administration
 INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
-('bx_organizations_administration', 'Sql', 'SELECT `td`.*, `td`.`org_name` AS `fullname`, `ta`.`email` AS `account`, `ta`.`logged` AS `last_online`, `tp`.`status` AS `status` FROM `bx_organizations_data` AS `td` LEFT JOIN `sys_profiles` AS `tp` ON `td`.`id`=`tp`.`content_id` AND `tp`.`type`=''bx_organizations'' LEFT JOIN `sys_accounts` AS `ta` ON `tp`.`account_id`=`ta`.`id` WHERE 1 ', 'bx_organizations_data', 'id', 'last_online', 'status', '', 20, NULL, 'start', '', 'org_name,email', '', 'like', 'reports', '', 192, 'BxOrgsGridAdministration', 'modules/boonex/organizations/classes/BxOrgsGridAdministration.php'),
+('bx_organizations_administration', 'Sql', 'SELECT `td`.*, `td`.`org_name` AS `fullname`, `ta`.`email` AS `account`, `ta`.`logged` AS `last_online`, `tp`.`status` AS `status`, `tp`.`id` as `profile_id` FROM `bx_organizations_data` AS `td` LEFT JOIN `sys_profiles` AS `tp` ON `td`.`id`=`tp`.`content_id` AND `tp`.`type`=''bx_organizations'' LEFT JOIN `sys_accounts` AS `ta` ON `tp`.`account_id`=`ta`.`id` WHERE 1 ', 'bx_organizations_data', 'id', 'last_online', 'status', '', 20, NULL, 'start', '', 'org_name,email', '', 'like', 'reports', '', 192, 'BxOrgsGridAdministration', 'modules/boonex/organizations/classes/BxOrgsGridAdministration.php'),
 ('bx_organizations_common', 'Sql', 'SELECT `td`.*, `td`.`org_name` AS `fullname`, `ta`.`email` AS `account`, `ta`.`logged` AS `last_online`, `tp`.`status` AS `status` FROM `bx_organizations_data` AS `td` LEFT JOIN `sys_profiles` AS `tp` ON `td`.`id`=`tp`.`content_id` AND `tp`.`type`=''bx_organizations'' LEFT JOIN `sys_accounts` AS `ta` ON `tp`.`account_id`=`ta`.`id` WHERE 1 ', 'bx_organizations_data', 'id', 'last_online', 'status', '', 20, NULL, 'start', '', 'org_name', '', 'like', '', '', 2147483647, 'BxOrgsGridCommon', 'modules/boonex/organizations/classes/BxOrgsGridCommon.php');
 
 INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
@@ -574,6 +575,7 @@ INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable
 INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
 ('bx_organizations_administration', 'bulk', 'set_acl_level', '_bx_orgs_grid_action_title_adm_set_acl_level', '', 0, 0, 1),
 ('bx_organizations_administration', 'bulk', 'delete', '_bx_orgs_grid_action_title_adm_delete', '', 0, 1, 2),
+('bx_organizations_administration', 'bulk', 'clear_reports', '_bx_orgs_grid_action_title_adm_clear_reports', '', 0, 1, 3),
 ('bx_organizations_administration', 'bulk', 'delete_with_content', '_bx_orgs_grid_action_title_adm_delete_with_content', '', 0, 1, 3),
 ('bx_organizations_administration', 'single', 'set_acl_level', '_bx_orgs_grid_action_title_adm_set_acl_level', 'certificate', 1, 0, 1),
 ('bx_organizations_administration', 'single', 'settings', '_bx_orgs_grid_action_title_adm_more_actions', 'cog', 1, 0, 2),

+ 1 - 1
modules/boonex/organizations/install/sql/install.sql

@@ -290,7 +290,7 @@ INSERT INTO `sys_form_inputs`(`object`, `module`, `name`, `value`, `values`, `ch
 ('bx_organization', 'bx_organizations', 'org_desc', '', '', 0, 'textarea', '_bx_orgs_form_profile_input_sys_org_desc', '_bx_orgs_form_profile_input_org_desc', '', 0, 0, 1, '', '', '', '', '', '', 'XssHtml', '', 1, 1),
 ('bx_organization', 'bx_organizations', 'org_cat', '', '#!bx_organizations_cats', 0, 'select', '_bx_orgs_form_profile_input_sys_org_cat', '_bx_orgs_form_profile_input_org_cat', '', 1, 0, 0, '', '', '', 'avail', '', '_bx_orgs_form_profile_input_org_cat_err', 'Xss', '', 1, 1),
 ('bx_organization', 'bx_organizations', 'multicat', '', '', 0, 'custom', '_bx_orgs_form_entry_input_sys_multicat', '_bx_orgs_form_entry_input_multicat', '', 1, 0, 0, '', '', '', 'avail', '', '_bx_orgs_form_entry_input_multicat_err', 'Xss', '', 1, 0),
-('bx_organization', 'bx_organizations', 'org_name', '', '', 0, 'text', '_bx_orgs_form_profile_input_sys_org_name', '_bx_orgs_form_profile_input_org_name', '', 1, 0, 0, '', '', '', 'avail', '', '_bx_orgs_form_profile_input_org_name_err', 'Xss', '', 1, 0),
+('bx_organization', 'bx_organizations', 'org_name', '', '', 0, 'text', '_bx_orgs_form_profile_input_sys_org_name', '_bx_orgs_form_profile_input_org_name', '', 1, 0, 0, '', '', '', 'ProfileName', '', '_bx_orgs_form_profile_input_org_name_err', 'Xss', '', 1, 0),
 ('bx_organization', 'bx_organizations', 'initial_members', '', '', 0, 'custom', '_bx_orgs_form_profile_input_sys_initial_members', '_bx_orgs_form_profile_input_initial_members', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 1),
 ('bx_organization', 'bx_organizations', 'join_confirmation', 1, '', 1, 'switcher', '_bx_orgs_form_profile_input_sys_join_confirm', '_bx_orgs_form_profile_input_join_confirm', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
 ('bx_organization', 'bx_organizations', 'cover', 'a:1:{i:0;s:27:"bx_organizations_cover_crop";}', 'a:1:{s:27:"bx_organizations_cover_crop";s:24:"_sys_uploader_crop_title";}', 0, 'files', '_bx_orgs_form_profile_input_sys_cover', '_bx_orgs_form_profile_input_cover', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),

+ 5 - 0
modules/boonex/organizations/js/manage_tools.js

@@ -62,4 +62,9 @@ BxOrgsManageTools.prototype.onClickDeleteWithContent = function(iContentId) {
 	glGrids[this._sObjNameGrid].actionWithId(iContentId, 'delete_with_content', {}, '', false, 1);
 };
 
+BxOrgsManageTools.prototype.onClickClearReports = function (iContentId) {
+    $('.bx-popup-applied:visible').dolPopupHide();
+
+    glGrids[this._sObjNameGrid].actionWithId(iContentId, 'clear_reports', {}, '', false, 1);
+};
 /** @} */

+ 8 - 0
modules/boonex/payment/classes/BxPaymentModule.php

@@ -692,6 +692,14 @@ class BxPaymentModule extends BxBaseModPaymentModule
                 $this->_oConfig->putCustom($sCartItem, $aCartItemCustom, $aCustoms);
             }
         }
+        
+        bx_alert($this->getName(), 'before_pending', 0, bx_get_logged_profile_id(), array(
+            'client_id' => $this->_iUserId,
+            'type' => &$sType,
+            'provider' => &$sProvider,
+            'cart_info' => &$aInfo,
+			'custom' => &$aCustoms,
+        ));
 
         $iPendingId = $this->_oDb->insertOrderPending($this->_iUserId, $sType, $sProvider, $aInfo, $aCustoms);
         if(empty($iPendingId))

+ 1 - 1
modules/boonex/persons/classes/BxPersonsMenuViewActionsAll.php

@@ -33,7 +33,7 @@ class BxPersonsMenuViewActionsAll extends BxBaseModProfileMenuViewActionsAll
 
     protected function _getMenuItemDeletePersonsProfile($aItem)
     {
-        return $this->_getMenuItemByNameActionsMore($aItem);
+        return $this->_getMenuItemByNameActionDelete($aItem);
     }
 
     protected function _getMenuItemDeletePersonsAccount($aItem)

+ 4 - 1
modules/boonex/persons/install/langs/en.xml

@@ -36,7 +36,7 @@
     <string name="_bx_persons_form_profile_input_allow_contact_to"><![CDATA[Who can contact me]]></string>
 	<string name="_bx_persons_form_profile_input_sys_fullname"><![CDATA[Full Name]]></string>
 	<string name="_bx_persons_form_profile_input_fullname"><![CDATA[Full Name]]></string>
-	<string name="_bx_persons_form_profile_input_fullname_err"><![CDATA[This information is essential. Please, fill in this field.]]></string>
+	<string name="_bx_persons_form_profile_input_fullname_err"><![CDATA[Profile name is mandatory and and cannot start with @]]></string>
 	<string name="_bx_persons_form_profile_input_sys_desc"><![CDATA[Description]]></string>
 	<string name="_bx_persons_form_profile_input_desc"><![CDATA[Description]]></string>
 	<string name="_bx_persons_form_profile_input_sys_picture"><![CDATA[Profile Picture]]></string>
@@ -198,6 +198,8 @@
     <string name="_bx_persons_menu_item_title_delete"><![CDATA[Delete]]></string>
     <string name="_bx_persons_menu_item_title_system_delete_with_content"><![CDATA[Delete With Content]]></string>
     <string name="_bx_persons_menu_item_title_delete_with_content"><![CDATA[Delete With Content]]></string>
+    <string name="_bx_persons_menu_item_title_system_clear_reports"><![CDATA[Clear reports]]></string>
+    <string name="_bx_persons_menu_item_title_clear_reports"><![CDATA[Clear reports]]></string>
     <string name="_bx_persons_menu_item_title_system_edit_cover"><![CDATA[Edit Cover]]></string>
     <string name="_bx_persons_menu_item_title_edit_cover"><![CDATA[Edit Cover]]></string>
     <string name="_bx_persons_menu_item_title_system_edit_profile"><![CDATA[Edit Person]]></string>
@@ -291,6 +293,7 @@
     <string name="_bx_persons_grid_column_title_adm_account"><![CDATA[Account]]></string>
     <string name="_bx_persons_grid_action_title_adm_delete"><![CDATA[Delete]]></string>
     <string name="_bx_persons_grid_action_title_adm_delete_with_content"><![CDATA[Delete With Content]]></string>
+    <string name="_bx_persons_grid_action_title_adm_clear_reports"><![CDATA[Clear reports]]></string>
     <string name="_bx_persons_grid_action_title_adm_set_acl_level"><![CDATA[Set Membership]]></string>
     <string name="_bx_persons_grid_action_title_adm_more_actions"><![CDATA[More Actions]]></string>
     <string name="_bx_persons_grid_action_title_adm_audit_content"><![CDATA[Audit actions with profile]]></string>

+ 4 - 2
modules/boonex/persons/install/sql/enable.sql

@@ -367,7 +367,8 @@ INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES
 
 INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
 ('bx_persons_menu_manage_tools', 'bx_persons', 'delete', '_bx_persons_menu_item_title_system_delete', '_bx_persons_menu_item_title_delete', 'javascript:void(0)', 'javascript:{js_object}.onClickDelete({content_id});', '_self', 'far trash-alt', '', 2147483647, 1, 0, 1),
-('bx_persons_menu_manage_tools', 'bx_persons', 'delete-with-content', '_bx_persons_menu_item_title_system_delete_with_content', '_bx_persons_menu_item_title_delete_with_content', 'javascript:void(0)', 'javascript:{js_object}.onClickDeleteWithContent({content_id});', '_self', 'far trash-alt', '', 2147483647, 1, 0, 2);
+('bx_persons_menu_manage_tools', 'bx_persons', 'delete-with-content', '_bx_persons_menu_item_title_system_delete_with_content', '_bx_persons_menu_item_title_delete_with_content', 'javascript:void(0)', 'javascript:{js_object}.onClickDeleteWithContent({content_id});', '_self', 'far trash-alt', '', 2147483647, 1, 0, 2),
+('bx_persons_menu_manage_tools', 'bx_persons', 'clear-reports', '_bx_persons_menu_item_title_system_clear_reports', '_bx_persons_menu_item_title_clear_reports', 'javascript:void(0)', 'javascript:{js_object}.onClickClearReports({content_id});', '_self', 'eraser', '', 2147483647, 1, 0, 3);
 
 -- MENU: dashboard manage tools
 SET @iManageMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name`='sys_account_dashboard_manage_tools' LIMIT 1);
@@ -461,7 +462,7 @@ INSERT INTO `sys_objects_chart` (`object`, `title`, `table`, `field_date_ts`, `f
 
 -- GRIDS: administration
 INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
-('bx_persons_administration', 'Sql', 'SELECT `td`.*, `ta`.`email` AS `account`, `ta`.`logged` AS `last_online`, `tp`.`status` AS `status` FROM `bx_persons_data` AS `td` LEFT JOIN `sys_profiles` AS `tp` ON `td`.`id`=`tp`.`content_id` AND `tp`.`type`=''bx_persons'' LEFT JOIN `sys_accounts` AS `ta` ON `tp`.`account_id`=`ta`.`id` WHERE 1 ', 'bx_persons_data', 'id', 'last_online', 'status', '', 20, NULL, 'start', '', 'fullname,email', '', 'like', 'reports', '', 192, 'BxPersonsGridAdministration', 'modules/boonex/persons/classes/BxPersonsGridAdministration.php'),
+('bx_persons_administration', 'Sql', 'SELECT `td`.*, `ta`.`email` AS `account`, `ta`.`logged` AS `last_online`, `tp`.`status` AS `status`, `tp`.`id` as `profile_id` FROM `bx_persons_data` AS `td` LEFT JOIN `sys_profiles` AS `tp` ON `td`.`id`=`tp`.`content_id` AND `tp`.`type`=''bx_persons'' LEFT JOIN `sys_accounts` AS `ta` ON `tp`.`account_id`=`ta`.`id` WHERE 1 ', 'bx_persons_data', 'id', 'last_online', 'status', '', 20, NULL, 'start', '', 'fullname,email', '', 'like', 'reports', '', 192, 'BxPersonsGridAdministration', 'modules/boonex/persons/classes/BxPersonsGridAdministration.php'),
 ('bx_persons_common', 'Sql', 'SELECT `td`.*, `ta`.`email` AS `account`, `ta`.`logged` AS `last_online`, `tp`.`status` AS `status` FROM `bx_persons_data` AS `td` LEFT JOIN `sys_profiles` AS `tp` ON `td`.`id`=`tp`.`content_id` AND `tp`.`type`=''bx_persons'' LEFT JOIN `sys_accounts` AS `ta` ON `tp`.`account_id`=`ta`.`id` WHERE 1 ', 'bx_persons_data', 'id', 'last_online', 'status', '', 20, NULL, 'start', '', 'fullname', '', 'like', '', '', 2147483647, 'BxPersonsGridCommon', 'modules/boonex/persons/classes/BxPersonsGridCommon.php');
 
 INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
@@ -481,6 +482,7 @@ INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon
 ('bx_persons_administration', 'bulk', 'set_acl_level', '_bx_persons_grid_action_title_adm_set_acl_level', '', 0, 0, 1),
 ('bx_persons_administration', 'bulk', 'delete', '_bx_persons_grid_action_title_adm_delete', '', 0, 1, 2),
 ('bx_persons_administration', 'bulk', 'delete_with_content', '_bx_persons_grid_action_title_adm_delete_with_content', '', 0, 1, 3),
+('bx_persons_administration', 'bulk', 'clear_reports', '_bx_forum_grid_action_title_adm_clear_reports', '', 0, 1, 4),
 ('bx_persons_administration', 'single', 'set_acl_level', '_bx_persons_grid_action_title_adm_set_acl_level', 'certificate', 1, 0, 1),
 ('bx_persons_administration', 'single', 'settings', '_bx_persons_grid_action_title_adm_more_actions', 'cog', 1, 0, 2),
 ('bx_persons_administration', 'single', 'audit_content', '_bx_persons_grid_action_title_adm_audit_content', 'search', 1, 0, 3),

+ 1 - 1
modules/boonex/persons/install/sql/install.sql

@@ -263,7 +263,7 @@ INSERT INTO `sys_form_inputs`(`object`, `module`, `name`, `value`, `values`, `ch
 ('bx_person', 'bx_persons', 'delete_confirm', 1, '', 0, 'checkbox', '_bx_persons_form_profile_input_sys_delete_confirm', '_bx_persons_form_profile_input_delete_confirm', '_bx_persons_form_profile_input_delete_confirm_info', 1, 0, 0, '', '', '', 'Avail', '', '_bx_persons_form_profile_input_delete_confirm_error', '', '', 1, 0, ''),
 ('bx_person', 'bx_persons', 'do_submit', '_bx_persons_form_profile_input_submit', '', 0, 'submit', '_bx_persons_form_profile_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0, ''),
 ('bx_person', 'bx_persons', 'description', '', '', 0, 'textarea', '_bx_persons_form_profile_input_sys_desc', '_bx_persons_form_profile_input_desc', '', 0, 0, 1, '', '', '', '', '', '', 'XssHtml', '', 1, 1, ''),
-('bx_person', 'bx_persons', 'fullname', '', '', 0, 'text', '_bx_persons_form_profile_input_sys_fullname', '_bx_persons_form_profile_input_fullname', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_persons_form_profile_input_fullname_err', 'Xss', '', 1, 0, ''),
+('bx_person', 'bx_persons', 'fullname', '', '', 0, 'text', '_bx_persons_form_profile_input_sys_fullname', '_bx_persons_form_profile_input_fullname', '', 1, 0, 0, '', '', '', 'ProfileName', '', '_bx_persons_form_profile_input_fullname_err', 'Xss', '', 1, 0, ''),
 ('bx_person', 'bx_persons', 'cover', 'a:1:{i:0;s:21:"bx_persons_cover_crop";}', 'a:1:{s:21:"bx_persons_cover_crop";s:24:"_sys_uploader_crop_title";}', 0, 'files', '_bx_persons_form_profile_input_sys_cover', '_bx_persons_form_profile_input_cover', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0, ''),
 ('bx_person', 'bx_persons', 'picture', 'a:1:{i:0;s:23:"bx_persons_picture_crop";}', 'a:1:{s:23:"bx_persons_picture_crop";s:24:"_sys_uploader_crop_title";}', 0, 'files', '_bx_persons_form_profile_input_sys_picture', '_bx_persons_form_profile_input_picture', '', 0, 0, 0, '', '', '', '', '', '_bx_persons_form_profile_input_picture_err', '', '', 1, 0, ''),
 ('bx_person', 'bx_persons', 'location', '', '', 0, 'location', '_sys_form_input_sys_location', '_sys_form_input_location', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0, ''),

+ 6 - 0
modules/boonex/persons/js/manage_tools.js

@@ -62,4 +62,10 @@ BxPersonsManageTools.prototype.onClickDeleteWithContent = function(iContentId) {
 	glGrids[this._sObjNameGrid].actionWithId(iContentId, 'delete_with_content', {}, '', false, 1);
 };
 
+BxPersonsManageTools.prototype.onClickClearReports = function (iContentId) {
+    $('.bx-popup-applied:visible').dolPopupHide();
+
+    glGrids[this._sObjNameGrid].actionWithId(iContentId, 'clear_reports', {}, '', false, 1);
+};
+
 /** @} */

+ 1 - 0
modules/boonex/photos/install/langs/en.xml

@@ -213,6 +213,7 @@
     <string name="_bx_photos_grid_action_title_adm_delete"><![CDATA[Delete]]></string>
     <string name="_bx_photos_grid_action_title_adm_more_actions"><![CDATA[More Actions]]></string>
     <string name="_bx_photos_grid_action_title_adm_audit_content"><![CDATA[Audit actions]]></string>
+    <string name="_bx_photos_grid_action_title_adm_clear_reports"><![CDATA[Clear reports]]></string>
     <string name="_bx_photos_grid_action_err_delete"><![CDATA[Cannot delete the selected photo(s).]]></string>
     <string name="_bx_photos_grid_txt_account_manager"><![CDATA[Redirect to manage account.]]></string>
 

+ 2 - 0
modules/boonex/photos/install/sql/enable.sql

@@ -435,10 +435,12 @@ INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable
 
 INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
 ('bx_photos_administration', 'bulk', 'delete', '_bx_photos_grid_action_title_adm_delete', '', 0, 1, 1),
+('bx_photos_administration', 'bulk', 'clear_reports', '_bx_photos_grid_action_title_adm_clear_reports', '', 0, 1, 2),
 ('bx_photos_administration', 'single', 'edit', '_bx_photos_grid_action_title_adm_edit', 'pencil-alt', 1, 0, 1),
 ('bx_photos_administration', 'single', 'delete', '_bx_photos_grid_action_title_adm_delete', 'remove', 1, 1, 2),
 ('bx_photos_administration', 'single', 'settings', '_bx_photos_grid_action_title_adm_more_actions', 'cog', 1, 0, 3),
 ('bx_photos_administration', 'single', 'audit_content', '_bx_photos_grid_action_title_adm_audit_content', 'search', 1, 0, 4),
+('bx_photos_administration', 'single', 'clear_reports', '_bx_photos_grid_action_title_adm_clear_reports', 'eraser', 1, 0, 5),
 ('bx_photos_common', 'bulk', 'delete', '_bx_photos_grid_action_title_adm_delete', '', 0, 1, 1),
 ('bx_photos_common', 'single', 'edit', '_bx_photos_grid_action_title_adm_edit', 'pencil-alt', 1, 0, 1),
 ('bx_photos_common', 'single', 'delete', '_bx_photos_grid_action_title_adm_delete', 'remove', 1, 1, 2),

+ 1 - 0
modules/boonex/polls/install/langs/en.xml

@@ -208,6 +208,7 @@
     <string name="_bx_polls_grid_action_title_adm_delete"><![CDATA[Delete]]></string>
     <string name="_bx_polls_grid_action_title_adm_more_actions"><![CDATA[More Actions]]></string>
     <string name="_bx_polls_grid_action_title_adm_audit_content"><![CDATA[Audit actions]]></string>
+    <string name="_bx_polls_grid_action_title_adm_clear_reports"><![CDATA[Clear reports]]></string>
     <string name="_bx_polls_grid_action_err_delete"><![CDATA[Cannot delete the selected poll(s).]]></string>
     <string name="_bx_polls_grid_txt_account_manager"><![CDATA[Redirect to manage account.]]></string>
 

+ 2 - 0
modules/boonex/polls/install/sql/enable.sql

@@ -442,10 +442,12 @@ INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable
 
 INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
 ('bx_polls_administration', 'bulk', 'delete', '_bx_polls_grid_action_title_adm_delete', '', 0, 1, 1),
+('bx_polls_administration', 'bulk', 'clear_reports', '_bx_polls_grid_action_title_adm_clear_reports', '', 0, 1, 2),
 ('bx_polls_administration', 'single', 'edit', '_bx_polls_grid_action_title_adm_edit', 'pencil-alt', 1, 0, 1),
 ('bx_polls_administration', 'single', 'delete', '_bx_polls_grid_action_title_adm_delete', 'remove', 1, 1, 2),
 ('bx_polls_administration', 'single', 'settings', '_bx_polls_grid_action_title_adm_more_actions', 'cog', 1, 0, 3),
 ('bx_polls_administration', 'single', 'audit_content', '_bx_polls_grid_action_title_adm_audit_content', 'search', 1, 0, 4),
+('bx_polls_administration', 'single', 'clear_reports', '_bx_polls_grid_action_title_adm_clear_reports', 'eraser', 1, 0, 5),
 ('bx_polls_common', 'bulk', 'delete', '_bx_polls_grid_action_title_adm_delete', '', 0, 1, 1),
 ('bx_polls_common', 'single', 'edit', '_bx_polls_grid_action_title_adm_edit', 'pencil-alt', 1, 0, 1),
 ('bx_polls_common', 'single', 'delete', '_bx_polls_grid_action_title_adm_delete', 'remove', 1, 1, 2),

+ 3 - 0
modules/boonex/posts/classes/BxPostsConfig.php

@@ -46,6 +46,7 @@ class BxPostsConfig extends BxBaseModTextConfig
             'FIELD_COVER' => 'covers',
             'FIELD_PHOTO' => 'pictures',
             'FIELD_VIDEO' => 'videos',
+            'FIELD_SOUND' => 'sounds',
             'FIELD_FILE' => 'files',
             'FIELD_POLL' => 'polls',
             'FIELD_THUMB' => 'thumb',
@@ -95,6 +96,7 @@ class BxPostsConfig extends BxBaseModTextConfig
             'OBJECT_STORAGE_FILES' => 'bx_posts_files',
             'OBJECT_STORAGE_PHOTOS' => 'bx_posts_photos',
             'OBJECT_STORAGE_VIDEOS' => 'bx_posts_videos',
+            'OBJECT_STORAGE_SOUNDS' => 'bx_classes_sounds',
             'OBJECT_IMAGES_TRANSCODER_PREVIEW' => 'bx_posts_preview',
             'OBJECT_IMAGES_TRANSCODER_GALLERY' => 'bx_posts_gallery',
             'OBJECT_IMAGES_TRANSCODER_COVER' => 'bx_posts_cover',
@@ -102,6 +104,7 @@ class BxPostsConfig extends BxBaseModTextConfig
             'OBJECT_IMAGES_TRANSCODER_GALLERY_FILES' => 'bx_posts_gallery_files',
             'OBJECT_IMAGES_TRANSCODER_PREVIEW_PHOTOS' => 'bx_posts_preview_photos',
             'OBJECT_IMAGES_TRANSCODER_GALLERY_PHOTOS' => 'bx_posts_gallery_photos',
+            'OBJECT_SOUNDS_TRANSCODER' => 'bx_classes_sounds_mp3',
             'OBJECT_VIDEOS_TRANSCODERS' => array(
                 'poster' => 'bx_posts_videos_poster', 
             	'poster_preview' => 'bx_posts_videos_poster_preview',

+ 11 - 0
modules/boonex/posts/classes/BxPostsFormEntry.php

@@ -62,6 +62,17 @@ class BxPostsFormEntry extends BxBaseModTextFormEntry
             $this->aInputs[$CNF['FIELD_VIDEO']]['ghost_template'] = '';
             $this->aInputs[$CNF['FIELD_VIDEO']]['tr_attrs'] = array('class'=> 'bx-base-text-attachment-item');
         }
+        
+        if(isset($this->aInputs[$CNF['FIELD_SOUND']])) {
+            $this->aInputs[$CNF['FIELD_SOUND']]['storage_object'] = $CNF['OBJECT_STORAGE_SOUNDS'];
+            $this->aInputs[$CNF['FIELD_SOUND']]['uploaders'] = !empty($this->aInputs[$CNF['FIELD_SOUND']]['value']) ? unserialize($this->aInputs[$CNF['FIELD_SOUND']]['value']) : $CNF['OBJECT_UPLOADERS'];
+            $this->aInputs[$CNF['FIELD_SOUND']]['images_transcoder'] = $CNF['OBJECT_IMAGES_TRANSCODER_PREVIEW_FILES'];
+            $this->aInputs[$CNF['FIELD_SOUND']]['storage_private'] = 0;
+            $this->aInputs[$CNF['FIELD_SOUND']]['multiple'] = true;
+            $this->aInputs[$CNF['FIELD_SOUND']]['content_id'] = 0;
+            $this->aInputs[$CNF['FIELD_SOUND']]['ghost_template'] = '';
+            $this->aInputs[$CNF['FIELD_SOUND']]['tr_attrs'] = array('class'=> 'bx-base-text-attachment-item');
+        }
 
         if (isset($CNF['FIELD_FILE']) && isset($this->aInputs[$CNF['FIELD_FILE']])) {
             $this->aInputs[$CNF['FIELD_FILE']]['storage_object'] = $CNF['OBJECT_STORAGE_FILES'];

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio