Browse Source

Merge branch 'master' into RC/2.x

# Conflicts:
#	composer.lock
#	modules/Utils/ChainedSelect/ChainedSelectCommon_0.php
#	modules/Utils/ChainedSelect/cs.js
#	modules/Utils/LeightboxPrompt/LeightboxPrompt_0.php
#	modules/Utils/RecordBrowser/RecordBrowser_0.php
#	modules/Utils/Tooltip/TooltipCommon_0.php
experimental
Paweł Jedwabny 3 years ago
parent
commit
4a64a95c32
100 changed files with 3472 additions and 737 deletions
  1. +4
    -2
      composer.json
  2. +643
    -479
      composer.lock
  3. +4
    -0
      console.php
  4. +3
    -3
      console/Develop/CreateModuleCommand.php
  5. +140
    -0
      console/Develop/CreateTestModuleCommand.php
  6. +32
    -0
      console/Modules/RebuildModulesDatabaseCommand.php
  7. +33
    -0
      console/RebuildAllCommand.php
  8. +57
    -0
      console/RemoveAllCommand.php
  9. +30
    -0
      docs/CHANGELOG.md
  10. +1
    -1
      include/version.php
  11. +125
    -7
      modules/Applets/Clock/ClockCommon_0.php
  12. +22
    -3
      modules/Applets/Clock/Clock_0.php
  13. +1
    -1
      modules/Base/Dashboard/Dashboard_0.php
  14. +1
    -1
      modules/Base/Notify/NotifyCommon_0.php
  15. +302
    -201
      modules/Base/Notify/js/desktop-notify.js
  16. +27
    -27
      modules/Base/Notify/js/main.js
  17. +3
    -0
      modules/Base/Notify/refresh.php
  18. +7
    -1
      modules/CRM/Calendar/Calendar_0.php
  19. +10
    -2
      modules/CRM/Filters/Filters_0.php
  20. +3
    -0
      modules/CRM/Meeting/MeetingCommon_0.php
  21. +16
    -9
      modules/CRM/PhoneCall/PhoneCallCommon_0.php
  22. +399
    -0
      modules/CRM/Roundcube/RC/SQL/mssql.initial.sql
  23. +87
    -0
      modules/CRM/Roundcube/RC/SQL/mssql/2009103100.sql
  24. +9
    -0
      modules/CRM/Roundcube/RC/SQL/mssql/2010100600.sql
  25. +10
    -0
      modules/CRM/Roundcube/RC/SQL/mssql/2011011200.sql
  26. +127
    -0
      modules/CRM/Roundcube/RC/SQL/mssql/2011092800.sql
  27. +4
    -0
      modules/CRM/Roundcube/RC/SQL/mssql/2011111600.sql
  28. +9
    -0
      modules/CRM/Roundcube/RC/SQL/mssql/2011121400.sql
  29. +18
    -0
      modules/CRM/Roundcube/RC/SQL/mssql/2012051800.sql
  30. +8
    -0
      modules/CRM/Roundcube/RC/SQL/mssql/2012080700.sql
  31. +14
    -0
      modules/CRM/Roundcube/RC/SQL/mssql/2013011000.sql
  32. +1
    -0
      modules/CRM/Roundcube/RC/SQL/mssql/2013042700.sql
  33. +17
    -0
      modules/CRM/Roundcube/RC/SQL/mssql/2013052500.sql
  34. +44
    -0
      modules/CRM/Roundcube/RC/SQL/mssql/2013061000.sql
  35. +1
    -0
      modules/CRM/Roundcube/RC/SQL/mssql/2014042900.sql
  36. +1
    -0
      modules/CRM/Roundcube/RC/SQL/mssql/2015030800.sql
  37. +4
    -0
      modules/CRM/Roundcube/RC/SQL/mssql/2015111100.sql
  38. +2
    -0
      modules/CRM/Roundcube/RC/SQL/mssql/2016081200.sql
  39. +2
    -0
      modules/CRM/Roundcube/RC/SQL/mssql/2016100900.sql
  40. +36
    -0
      modules/CRM/Roundcube/RC/SQL/mssql/2016112200.sql
  41. +16
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2008030300.sql
  42. +9
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2008040500.sql
  43. +4
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2008060900.sql
  44. +20
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2008092100.sql
  45. +12
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2009090400.sql
  46. +52
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2009103100.sql
  47. +4
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2010042300.sql
  48. +8
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2010100600.sql
  49. +8
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2011011200.sql
  50. +67
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2011092800.sql
  51. +3
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2011111600.sql
  52. +22
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2011121400.sql
  53. +5
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2012080700.sql
  54. +7
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2013011000.sql
  55. +1
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2013042700.sql
  56. +7
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2013052500.sql
  57. +24
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2013061000.sql
  58. +1
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2014042900.sql
  59. +1
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2015030800.sql
  60. +3
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2015111100.sql
  61. +1
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2016081200.sql
  62. +1
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2016100900.sql
  63. +24
    -0
      modules/CRM/Roundcube/RC/SQL/mysql/2016112200.sql
  64. +220
    -0
      modules/CRM/Roundcube/RC/SQL/oracle.initial.sql
  65. +1
    -0
      modules/CRM/Roundcube/RC/SQL/oracle/2015030800.sql
  66. +2
    -0
      modules/CRM/Roundcube/RC/SQL/oracle/2015111100.sql
  67. +1
    -0
      modules/CRM/Roundcube/RC/SQL/oracle/2016081200.sql
  68. +1
    -0
      modules/CRM/Roundcube/RC/SQL/oracle/2016100900.sql
  69. +23
    -0
      modules/CRM/Roundcube/RC/SQL/oracle/2016112200.sql
  70. +18
    -0
      modules/CRM/Roundcube/RC/SQL/postgres/2008030300.sql
  71. +3
    -0
      modules/CRM/Roundcube/RC/SQL/postgres/2008060900.sql
  72. +14
    -0
      modules/CRM/Roundcube/RC/SQL/postgres/2008092100.sql
  73. +6
    -0
      modules/CRM/Roundcube/RC/SQL/postgres/2009090400.sql
  74. +32
    -0
      modules/CRM/Roundcube/RC/SQL/postgres/2009103100.sql
  75. +4
    -0
      modules/CRM/Roundcube/RC/SQL/postgres/2010042300.sql
  76. +7
    -0
      modules/CRM/Roundcube/RC/SQL/postgres/2010100600.sql
  77. +7
    -0
      modules/CRM/Roundcube/RC/SQL/postgres/2011011200.sql
  78. +64
    -0
      modules/CRM/Roundcube/RC/SQL/postgres/2011092800.sql
  79. +3
    -0
      modules/CRM/Roundcube/RC/SQL/postgres/2011111600.sql
  80. +5
    -0
      modules/CRM/Roundcube/RC/SQL/postgres/2011121400.sql
  81. +7
    -0
      modules/CRM/Roundcube/RC/SQL/postgres/2012080700.sql
  82. +4
    -0
      modules/CRM/Roundcube/RC/SQL/postgres/2013011000.sql
  83. +14
    -0
      modules/CRM/Roundcube/RC/SQL/postgres/2013042700.sql
  84. +8
    -0
      modules/CRM/Roundcube/RC/SQL/postgres/2013052500.sql
  85. +24
    -0
      modules/CRM/Roundcube/RC/SQL/postgres/2013061000.sql
  86. +1
    -0
      modules/CRM/Roundcube/RC/SQL/postgres/2014042900.sql
  87. +1
    -0
      modules/CRM/Roundcube/RC/SQL/postgres/2015030800.sql
  88. +2
    -0
      modules/CRM/Roundcube/RC/SQL/postgres/2015111100.sql
  89. +1
    -0
      modules/CRM/Roundcube/RC/SQL/postgres/2016081200.sql
  90. +1
    -0
      modules/CRM/Roundcube/RC/SQL/postgres/2016100900.sql
  91. +21
    -0
      modules/CRM/Roundcube/RC/SQL/postgres/2016112200.sql
  92. +203
    -0
      modules/CRM/Roundcube/RC/SQL/sqlite.initial.sql
  93. +25
    -0
      modules/CRM/Roundcube/RC/SQL/sqlite/2008030300.sql
  94. +3
    -0
      modules/CRM/Roundcube/RC/SQL/sqlite/2008060900.sql
  95. +4
    -0
      modules/CRM/Roundcube/RC/SQL/sqlite/2008092100.sql
  96. +8
    -0
      modules/CRM/Roundcube/RC/SQL/sqlite/2009090400.sql
  97. +61
    -0
      modules/CRM/Roundcube/RC/SQL/sqlite/2009103100.sql
  98. +35
    -0
      modules/CRM/Roundcube/RC/SQL/sqlite/2010042300.sql
  99. +40
    -0
      modules/CRM/Roundcube/RC/SQL/sqlite/2010100600.sql
  100. +41
    -0
      modules/CRM/Roundcube/RC/SQL/sqlite/2011011200.sql

+ 4
- 2
composer.json View File

@@ -13,12 +13,14 @@
"anahkiasen/underscore-php": "^2.0",
"moneyphp/money": "^3.0",
"enyo/dropzone": "@stable",
"ezyang/htmlpurifier": "^4.9"
"ezyang/htmlpurifier": "^4.9",
"moneyphp/money": "^3.0"
},
"scripts": {
"post-install-cmd": [
"@composer -d=\"modules/CRM/Mail\" install",
"@composer -d=\"modules/Libs/PHPExcel\" install"
"@composer -d=\"modules/Libs/PHPExcel\" install",
"@composer -d=\"modules/Libs/TCPDF\" install"
]
},
"autoload": {


+ 643
- 479
composer.lock
File diff suppressed because it is too large
View File


+ 4
- 0
console.php View File

@@ -27,6 +27,7 @@ $application->add(new \Epesi\Console\Modules\EnableModuleCommand());
$application->add(new \Epesi\Console\Modules\InstallModuleCommand());
$application->add(new \Epesi\Console\Modules\UninstallModuleCommand());
$application->add(new \Epesi\Console\Modules\EnableAllModuleCommand());
$application->add(new \Epesi\Console\Modules\RebuildModulesDatabaseCommand());
$application->add(new \Epesi\Console\CacheRebuildCommand());
$application->add(new \Epesi\Console\ThemeRebuildCommand());
$application->add(new \Epesi\Console\Maintenance\MaintenanceStatusCommand());
@@ -41,5 +42,8 @@ $application->add(new \Epesi\Console\Backup\ListBackupsCommand());
$application->add(new \Epesi\Console\Demo\GenerateContactsCommand());
$application->add(new \Epesi\Console\Develop\CreateModuleCommand());
$application->add(new \Epesi\Console\Develop\CreatePatchCommand());
$application->add(new \Epesi\Console\Develop\CreateTestModuleCommand());
$application->add(new \Epesi\Console\ShellCommand());
$application->add(new \Epesi\Console\RebuildAllCommand());
$application->add(new \Epesi\Console\RemoveAllCommand());
$application->run($input);

+ 3
- 3
console/Develop/CreateModuleCommand.php View File

@@ -20,7 +20,7 @@ class CreateModuleCommand extends Command
{
$this
->setName('dev:module:create')
->setDescription('Create EPESI module files')
->setDescription('Create EPESI empty module files')
->addArgument(
'name',
InputArgument::REQUIRED,
@@ -125,10 +125,10 @@ class CreateModuleCommand extends Command
Method::make('requires')
->addArgument(
Argument::make('mixed', 'v'))
->setBody("{$t}{$t}return array(\n$required_modules_str\n{$t}{$t});"))
->setBody("{$t}{$t}return [\n$required_modules_str\n{$t}{$t}];"))
->addMethod(
Method::make('version')
->setBody("{$t}{$t}return array('0.1');"))
->setBody("{$t}{$t}return ['0.1'];"))
);

if (file_put_contents($file_install, $prettyPrinter->generateCode($myFile)) !== false) {


+ 140
- 0
console/Develop/CreateTestModuleCommand.php View File

@@ -0,0 +1,140 @@
<?php

namespace Epesi\Console\Develop;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

use Memio\Memio\Config\Build;
use Memio\Model\File;
use Memio\Model\Object;
use Memio\Model\Method;
use Memio\Model\Argument;

class CreateTestModuleCommand extends Command
{
protected function configure()
{
$this
->setName('dev:module:test')
->setDescription('Create EPESI Custom Test module files with menu and simple setup')
->addOption(
'require', 'r',
InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Define required modules'
);
}

protected function execute(InputInterface $input, OutputInterface $output)
{
$module_name = 'Testing';
$requires = $input->getOption('require');

$module_type = 'Custom_'.$module_name;

//region Add defined("_VALID_ACCESS") to file.twig if not found
$current = file_get_contents(EPESI_LOCAL_DIR . '/vendor/memio/twig-template-engine/templates/file.twig');
if (!preg_match('/defined\(\"\_VALID\_ACCESS\"\)/', $current)) {
file_put_contents(
EPESI_LOCAL_DIR . '/vendor/memio/twig-template-engine/templates/file.twig',
str_replace(
'<?php',
'<?php' . PHP_EOL . 'defined("_VALID_ACCESS") || die(\'Direct access forbidden\');' . PHP_EOL,
$current
)
);
}
//endregion

$prettyPrinter = Build::prettyPrinter();

//region Create module dir
$module_dir = EPESI_LOCAL_DIR . '/modules/Custom/' . $module_name;
if (file_exists($module_dir)) {
$msg = "File or directory: $module_dir already exists";
$output->writeln($msg);
} else {
mkdir($module_dir, 0777, true);
$output->writeln("Created module directory: $module_dir");
}
//endregion

//region Main File
$file_main = $module_dir . '/' . $module_name . '_0.php';
$myFile = File::make($file_main)
->setStructure(
Object::make($module_type)
->extend(
Object::make('Module'))
->addMethod(
Method::make('body')
)
);

if (file_put_contents($file_main, $prettyPrinter->generateCode($myFile)) !== false) {
$output->writeln("Created file: $file_main");
}
//endregion

//region Common File
$t = ' ';
$file_common = $module_dir . '/' . $module_name . 'Common_0.php';
$myFile = File::make($file_common)
->setStructure(
Object::make($module_type . 'Common')
->extend(
Object::make('ModuleCommon')
)
->addMethod(
Method::make('menu')
->setBody("{$t}{$t}return ['_Testing' => []];"))
);

if (file_put_contents($file_common, $prettyPrinter->generateCode($myFile)) !== false) {
$output->writeln("Created file: $file_common");
}
//endregion

//region Install File

$closure = function ($m) use ($t) {
$m = preg_replace('#^modules/#', '', $m);
return "{$t}{$t}{$t}array('name' => '$m', 'version' => 0)";
};
$required_modules_str = implode(",\n", array_map($closure, $requires));
$file_install = $module_dir . '/' . $module_name . 'Install.php';
$myFile = File::make($file_install)
->setStructure(
Object::make($module_type . 'Install')
->extend(
Object::make('ModuleInstall'))
->addMethod(
Method::make('install')
->setBody("{$t}{$t}return true;"))
->addMethod(
Method::make('uninstall')
->setBody("{$t}{$t}return true;"))
->addMethod(
Method::make('requires')
->addArgument(
Argument::make('mixed', 'v'))
->setBody("{$t}{$t}return [\n$required_modules_str\n{$t}{$t}];"))
->addMethod(
Method::make('version')
->setBody("{$t}{$t}return ['0.1'];"))
->addMethod(
Method::make('simple_setup')
->setBody("{$t}{$t}return ['package' => '_Test module'];")
)
);

if (file_put_contents($file_install, $prettyPrinter->generateCode($myFile)) !== false) {
$output->writeln("Created file: $file_install");
}
//endregion

}
}

+ 32
- 0
console/Modules/RebuildModulesDatabaseCommand.php View File

@@ -0,0 +1,32 @@
<?php

namespace Epesi\Console\Modules;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class RebuildModulesDatabaseCommand extends Command
{
protected function configure()
{
$this
->setName('module:rebuild')
->setDescription('Rebuild modules database')
;
}

protected function execute(InputInterface $input, OutputInterface $output)
{
$modules = \Base_SetupCommon::refresh_available_modules();

$table = new Table($output);
$table->setHeaders(array('<fg=white;options=bold>Name</fg=white;options=bold>', '<fg=white;options=bold>Version</fg=white;options=bold>'));
foreach ($modules as $name => $module) {
$table->addRow(array($name, $module[0]));
}

$table->render();
}
}

+ 33
- 0
console/RebuildAllCommand.php View File

@@ -0,0 +1,33 @@
<?php

/**
* Created by PhpStorm.
* User: pjedwabny
* Date: 08.09.15
* Time: 21:10
*/
namespace Epesi\Console;
use Cache;
use ModuleManager;
use Base_ThemeCommon;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class RebuildAllCommand extends Command
{
protected function configure(){
$this
->setName('rebuild:all')
->setDescription('Rebuild EPESI default theme and cache')
;
}
protected function execute(InputInterface $input, OutputInterface $output) {
$output->writeln('Rebuilding themes...');
Base_ThemeCommon::themeup();
$output->writeln('Theme rebuilt! Rebuilding cache...');
Cache::clear();
ModuleManager::create_common_cache();
$output->writeln('Cache rebuilt!');
}
}

+ 57
- 0
console/RemoveAllCommand.php View File

@@ -0,0 +1,57 @@
<?php

namespace Epesi\Console;
use DB;
use Cache;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;


class RemoveAllCommand extends Command
{
protected function configure()
{
$this
->setName('remove:all')
->setDescription('Truncate database and remove all data from EPESI')
;
}

protected function execute(InputInterface $input, OutputInterface $output)
{
$dir = __DIR__ . '/../data/';

if(is_dir($dir)) {
$this->truncateDir($output, $dir);
}
else {
$output->writeln('Data directory doesn\'t exist or you don\'t have required permissions');
}
$this->truncateDb($output);
$output->writeln('EPESI removed. Please install application again');
}

private function truncateDir($output, $dir) {
$files = array_diff(scandir($dir), ['.', '..']);
foreach ($files as $file) {
if(!is_dir("$dir/$file")) unlink("$dir/$file");
else {
$handle = opendir("$dir/$file");
closedir($handle);
exec('rm -rf '."$dir/$file");
}
}
$output->writeln('Data folder cleared!');
}

private function truncateDb ($output) {
Cache::clear();
DB::Execute('SET FOREIGN_KEY_CHECKS=0;');
foreach(DB::MetaTables() as $k => $v) {
DB::DropTable($v);
}
DB::Execute('SET FOREIGN_KEY_CHECKS=1;');
$output->writeln('Database truncated!');
}
}

+ 30
- 0
docs/CHANGELOG.md View File

@@ -3,6 +3,36 @@ EPESI CHANGELOG

(Dev) means that this change is significant only for developers.

RELEASE 1.8.2-20180413
----------------------

### Fixed
- Error: "Undefined index: name" in dashboard
- BBcode display
- watchdog applet caused epesi error

### Feature
- Double clock
- HTML5 Notification
- rebuild:all command
- remove:all command
- create test module command
- possibility to set tooltip for leightbox buttons
- TCPDF from composer (dev)


RELEASE 1.8.2-20171019
----------------------

### Fixed
- fixed to use openssl if mcrypt is not loaded
- fixed charset exporting
- fixed XSS vulnerabilities in Attachments, Meetings, Calendar, Perspective - display
- added XSS purifier on recordset update

### Feature
- added phpmoney library

RELEASE 1.8.2-20170830
----------------------



+ 1
- 1
include/version.php View File

@@ -1,6 +1,6 @@
<?php
define("EPESI_VERSION", '1.8.2');
define("EPESI_REVISION", 20170830);
define("EPESI_REVISION", 20180413);

function epesi_requires_update()
{


+ 125
- 7
modules/Applets/Clock/ClockCommon_0.php View File

@@ -14,6 +14,42 @@
defined("_VALID_ACCESS") || die('Direct access forbidden');

class Applets_ClockCommon extends ModuleCommon {
public static function get_timezones() {
return array(
'-12.0' => __('(GMT-12:00) Eniwetok, Kwajalein'),
'-11.0' => __('(GMT-11:00) Midway Island, Samoa'),
'-10.0' => __('(GMT-10:00) Hawaii'),
'-9.0' => __('(GMT-9:00) Alaska'),
'-8.0' => __('(GMT-8:00) Pacific Time (US &amp; Canada)'),
'-7.0' => __('(GMT-7:00) Mountain Time (US &amp; Canada)'),
'-6.0' => __('(GMT-6:00) Central Time (US &amp; Canada), Mexico City'),
'-5.0' => __('(GMT-5:00) Eastern Time (US &amp; Canada), Bogota, Lima'),
'-4.0' => __('(GMT-4:00) Atlantic Time (Canada), Caracas, La Paz'),
'-3.5' => __('(GMT-3:30) Newfoundland'),
'-3.0' => __('(GMT-3:00) Brazil, Buenos Aires, Georgetown'),
'-2.0' => __('(GMT-2:00) Mid-Atlantic'),
'-1.0' => __('(GMT-1:00 hour) Azores, Cape Verde Islands'),
'0.0' => __('(GMT) Western Europe Time, London, Lisbon, Casablanca'),
'1.0' => __('(GMT+1:00 hour) Hamburg, Berlin, Brussels, Madrid, Paris'),
'2.0' => __('(GMT+2:00) Kaliningrad, South Africa'),
'3.0' => __('(GMT+3:00) Baghdad, Riyadh, Moscow, St. Petersburg'),
'3.5' => __('(GMT+3:30) Tehran'),
'4.0' => __('(GMT+4:00) Abu Dhabi, Muscat, Baku, Tbilisi'),
'4.5' => __('(GMT+4:30) Kabul'),
'5.0' => __('(GMT+5:00) Ekaterinburg, Islamabad, Karachi, Tashkent'),
'5.5' => __('(GMT+5:30) Bombay, Calcutta, Madras, New Delhi'),
'5.75' => __('(GMT+5:45) Kathmandu'),
'6.0' => __('(GMT+6:00) Almaty, Dhaka, Colombo'),
'7.0' => __('(GMT+7:00) Bangkok, Hanoi, Jakarta'),
'8.0' => __('(GMT+8:00) Beijing, Perth, Singapore, Hong Kong'),
'9.0' => __('(GMT+9:00) Tokyo, Seoul, Osaka, Sapporo, Yakutsk'),
'9.5' => __('(GMT+9:30) Adelaide, Darwin'),
'10.0' => __('(GMT+10:00) Eastern Australia, Guam, Vladivostok'),
'11.0' => __('(GMT+11:00) Magadan, Solomon Islands, New Caledonia'),
'12.0' => __('(GMT+12:00) Auckland, Wellington, Fiji, Kamchatka')
);
}
public static function applet_caption() {
return __('Clock');
}
@@ -23,15 +59,97 @@ class Applets_ClockCommon extends ModuleCommon {
}

public static function applet_settings() {
$browser = stripos($_SERVER['HTTP_USER_AGENT'],'msie');
if($browser!==false)
return array(
array('name'=>'skin','label'=>__('Clock configurable on non-IE browsers only.'),'type'=>'static','values'=>'')
$browser = stripos($_SERVER['HTTP_USER_AGENT'], 'msie');
if ($browser !== false) return array(
array(
'name' => 'skin',
'label' => __('Clock configurable on non-IE browsers only.'),
'type' => 'static',
'values' => ''
)
);
else {
$hide_mapping = array(
array(
'values' => 'single',
'mode' => 'hide',
'fields' => array(
'second_clock_timezone',
'second_clock_label'
)
)
);
else
return array(
array('name'=>'skin','label'=>__('Clock skin'),'type'=>'select','default'=>'swissRail','rule'=>array(array('message'=>__('Field required'), 'type'=>'required')),'values'=>array('swissRail'=>'swissRail','chunkySwiss'=>'chunkySwiss','chunkySwissOnBlack'=>'chunkySwissOnBlack','fancy'=>'fancy','machine'=>'machine','classic'=>'classic','modern'=>'modern','simple'=>'simple','securephp'=>'securephp','Tes2'=>'Tes2','Lev'=>'Lev','Sand'=>'Sand','Sun'=>'Sun','Tor'=>'Tor','Babosa'=>'Babosa','Tumb'=>'Tumb','Stone'=>'Stone','Disc'=>'Disc','flash'=>'flash'))
Libs_QuickFormCommon::autohide_fields('type', $hide_mapping);
return array(
array(
'name' => 'skin',
'label' => __('Clock skin'),
'type' => 'select',
'default' => 'swissRail',
'rule' => array(
array(
'message' => __('Field required'),
'type' => 'required'
)
),
'values' => array(
'swissRail' => 'swissRail',
'chunkySwiss' => 'chunkySwiss',
'chunkySwissOnBlack' => 'chunkySwissOnBlack',
'fancy' => 'fancy',
'machine' => 'machine',
'classic' => 'classic',
'modern' => 'modern',
'simple' => 'simple',
'securephp' => 'securephp',
'Tes2' => 'Tes2',
'Lev' => 'Lev',
'Sand' => 'Sand',
'Sun' => 'Sun',
'Tor' => 'Tor',
'Babosa' => 'Babosa',
'Tumb' => 'Tumb',
'Stone' => 'Stone',
'Disc' => 'Disc',
'flash' => 'flash'
)
),
array(
'name' => 'type',
'label' => __('Type'),
'type' => 'select',
'default' => 'single',
'values' => array(
'single' => __('Single Clock'),
'double' => __('Double Clock')
),
'param' => array(
'id' => 'type'
)
),
array(
'name' => 'second_clock_timezone',
'label' => __('Second clock timezone'),
'type' => 'select',
'default' => '8.0',
'values' => self::get_timezones(),
'param' => array(
'id' => 'second_clock_timezone'
)
),
array(
'name' => 'second_clock_label',
'label' => __('Second clock label'),
'type' => 'text',
'default' => __('Singapore / China'),
'param' => array(
'id' => 'second_clock_label'
)
)
);
}
}
}


+ 22
- 3
modules/Applets/Clock/Clock_0.php View File

@@ -15,7 +15,7 @@ defined("_VALID_ACCESS") || die('Direct access forbidden');

class Applets_Clock extends Module {
public function body($skin, $size=200) {
public function body($skin, $size=200, $conf = []) {
print('<center'.($skin=='chunkySwissOnBlack'?' style="background-color:black; color:white;"':'').'>');
$browser = stripos($_SERVER['HTTP_USER_AGENT'],'msie');
if($browser!==false || $skin=='flash') {
@@ -32,7 +32,23 @@ class Applets_Clock extends Module {
} else {
load_js($this->get_module_dir().'coolclock.js');
eval_js('CoolClock.findAndCreateClocks()');
print('<canvas id="'.$this->get_path().'canvas" class="CoolClock:'.$skin.':'.$size.'"></canvas>');
if ($conf && $conf['type'] == 'double') {
$timezone = $conf['second_clock_timezone'] ?: '0.0';
$label = $conf['second_clock_label'] ?: Applets_ClockCommon::get_timezones()[$timezone];
$offset = $timezone * 60 * 60;
print('<table style="width: 100%"><tr><td style="width: 100px;text-align:center;"><canvas id="' . $this->get_path() . '1_canvas" class="CoolClock:' . $skin . ':' . $size . '"></canvas>');
print('<br>Local Time<br><span class="local_time">' . Base_RegionalSettingsCommon::time2reg(null, false) . '</span></td>');
print('<td style="width: 100px;text-align:center;"><canvas id="' . $this->get_path() . '2_canvas" class="CoolClock:' . $skin . ':' . $size . ':noSeconds:' . $timezone . '"></canvas>');
print('<br>' . $label . '<br>' . gmdate('d F Y', time() + $offset) . '</td></tr></table>');
eval_js('jq(".local_time").html(function() {return jq.datepicker.formatDate("d MM yy", new Date());});');
print('</center>');
return;
}
else
print('<canvas id="' . $this->get_path() . 'canvas" class="CoolClock:' . $skin . ':' . $size . '"></canvas>');
}
print('<BR>'.Base_RegionalSettingsCommon::time2reg(null,false).'</center>');
}
@@ -42,7 +58,10 @@ class Applets_Clock extends Module {
$opts['go'] = true;
$skin = isset($conf['skin'])?$conf['skin']:null;
$opts['go_arguments'] = array($skin);
$this->body($skin,100);
$size = $conf['type'] == 'double'? 60: 100;
$this->body($skin, $size, $conf);
}

}


+ 1
- 1
modules/Base/Dashboard/Dashboard_0.php View File

@@ -472,7 +472,7 @@ class Base_Dashboard extends Module {
if(isset($v['rule']['message']) && isset($v['rule']['type'])) $v['rule'] = array($v['rule']);
}

if (isset($values[$v['name']])) {
if (isset($v['name']) && isset($values[$v['name']])) {
if ($v['type'] == "crits") {
$v['default'] = $values[$v['name']];
} else {


+ 1
- 1
modules/Base/Notify/NotifyCommon_0.php View File

@@ -34,7 +34,7 @@ class Base_NotifyCommon extends ModuleCommon {
load_js('modules/Base/Notify/js/desktop-notify.js');
load_js('modules/Base/Notify/js/main.js');

$disabled_message = __('Notifications disabled or not supported!').'\n'.__('Check your browser settings and allow notifications to use this feature...');
$disabled_message = __('Notifications disabled!')."\n".__('Check your browser settings and allow notifications to use this feature...');
$disabled_message = json_encode($disabled_message);
eval_js_once("Base_Notify.init (".(self::refresh_rate*1000).", $disabled_message);");



+ 302
- 201
modules/Base/Notify/js/desktop-notify.js View File

@@ -1,202 +1,303 @@
/**
* Copyright 2012 Tsvetan Tsvetkov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Author: Tsvetan Tsvetkov (tsekach@gmail.com)
/*! HTML5 Notification - v3.0.0 - 2016-09-19
Copyright 2016 Tsvetan Tsvetkov
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function (win) {
/*
Safari native methods required for Notifications do NOT run in strict mode.
*/
//"use strict";
var PERMISSION_DEFAULT = "default",
PERMISSION_GRANTED = "granted",
PERMISSION_DENIED = "denied",
PERMISSION = [PERMISSION_GRANTED, PERMISSION_DEFAULT, PERMISSION_DENIED],
defaultSetting = {
pageVisibility: false,
autoClose: 0
},
empty = {},
emptyString = "",
isSupported = (function () {
var isSupported = false;
/*
* Use try {} catch() {} because the check for IE may throws an exception
* if the code is run on browser that is not Safar/Chrome/IE or
* Firefox with html5notifications plugin.
*
* Also, we canNOT detect if msIsSiteMode method exists, as it is
* a method of host object. In IE check for existing method of host
* object returns undefined. So, we try to run it - if it runs
* successfully - then it is IE9+, if not - an exceptions is thrown.
*/
try {
isSupported = !!(/* Safari, Chrome */win.Notification || /* Chrome & ff-html5notifications plugin */win.webkitNotifications || /* Firefox Mobile */navigator.mozNotification || /* IE9+ */(win.external && win.external.msIsSiteMode() !== undefined));
} catch (e) {}
return isSupported;
}()),
ieVerification = Math.floor((Math.random() * 10) + 1),
isFunction = function (value) { return (value && (value).constructor === Function); },
isString = function (value) {return (value && (value).constructor === String); },
isObject = function (value) {return (value && (value).constructor === Object); },
/**
* Dojo Mixin
*/
mixin = function (target, source) {
var name, s;
for (name in source) {
s = source[name];
if (!(name in target) || (target[name] !== s && (!(name in empty) || empty[name] !== s))) {
target[name] = s;
}
}
return target; // Object
},
noop = function () {},
settings = defaultSetting;
function getNotification(title, options) {
var notification;
if (win.Notification) { /* Safari 6, Chrome (23+) */
notification = new win.Notification(title, {
/* The notification's icon - For Chrome in Windows, Linux & Chrome OS */
icon: isString(options.icon) ? options.icon : options.icon.x32,
/* The notification�s subtitle. */
body: options.body || emptyString,
/*
The notification�s unique identifier.
This prevents duplicate entries from appearing if the user has multiple instances of your website open at once.
*/
tag: options.tag || emptyString
});
} else if (win.webkitNotifications) { /* FF with html5Notifications plugin installed */
notification = win.webkitNotifications.createNotification(options.icon, title, options.body);
notification.show();
} else if (navigator.mozNotification) { /* Firefox Mobile */
notification = navigator.mozNotification.createNotification(title, options.body, options.icon);
notification.show();
} else if (win.external && win.external.msIsSiteMode()) { /* IE9+ */
//Clear any previous notifications
win.external.msSiteModeClearIconOverlay();
win.external.msSiteModeSetIconOverlay((isString(options.icon) ? options.icon : options.icon.x16), title);
win.external.msSiteModeActivate();
notification = {
"ieVerification": ieVerification + 1
};
}
return notification;
}
function getWrapper(notification) {
return {
close: function () {
if (notification) {
if (notification.close) {
//http://code.google.com/p/ff-html5notifications/issues/detail?id=58
notification.close();
}
else if (notification.cancel) {
notification.cancel();
} else if (win.external && win.external.msIsSiteMode()) {
if (notification.ieVerification === ieVerification) {
win.external.msSiteModeClearIconOverlay();
}
}
}
}
};
}
function requestPermission(callback) {
if (!isSupported) { return; }
var callbackFunction = isFunction(callback) ? callback : noop;
if (win.webkitNotifications && win.webkitNotifications.checkPermission) {
/*
* Chrome 23 supports win.Notification.requestPermission, but it
* breaks the browsers, so use the old-webkit-prefixed
* win.webkitNotifications.checkPermission instead.
*
* Firefox with html5notifications plugin supports this method
* for requesting permissions.
*/
win.webkitNotifications.requestPermission(callbackFunction);
} else if (win.Notification && win.Notification.requestPermission) {
win.Notification.requestPermission(callbackFunction);
}
}
function permissionLevel() {
var permission;
if (!isSupported) { return; }
if (win.Notification && win.Notification.permissionLevel) {
//Safari 6
permission = win.Notification.permissionLevel();
} else if (win.webkitNotifications && win.webkitNotifications.checkPermission) {
//Chrome & Firefox with html5-notifications plugin installed
permission = PERMISSION[win.webkitNotifications.checkPermission()];
} else if (win.Notification && win.Notification.permission) {
// Firefox 23+
permission = win.Notification.permission;
} else if (navigator.mozNotification) {
//Firefox Mobile
permission = PERMISSION_GRANTED;
} else if (win.external && (win.external.msIsSiteMode() !== undefined)) { /* keep last */
//IE9+
permission = win.external.msIsSiteMode() ? PERMISSION_GRANTED : PERMISSION_DEFAULT;
}
return permission;
}
/**
*
*/
function config(params) {
if (params && isObject(params)) {
mixin(settings, params);
}
return settings;
}
function createNotification(title, options) {
var notification,
notificationWrapper;
/*
Return undefined if notifications are not supported.
Return undefined if no permissions for displaying notifications.
Title and icons are required. Return undefined if not set.
*/
if (isSupported && isString(title) && (options && (isString(options.icon) || isObject(options.icon))) && (permissionLevel() === PERMISSION_GRANTED)) {
notification = getNotification(title, options);
}
notificationWrapper = getWrapper(notification);
//Auto-close notification
if (settings.autoClose && notification && !notification.ieVerification && notification.addEventListener) {
notification.addEventListener("show", function () {
var notification = notificationWrapper;
win.setTimeout(function () {
notification.close();
}, settings.autoClose);
});
}
return notificationWrapper;
}
win.notify = {
PERMISSION_DEFAULT: PERMISSION_DEFAULT,
PERMISSION_GRANTED: PERMISSION_GRANTED,
PERMISSION_DENIED: PERMISSION_DENIED,
isSupported: isSupported,
config: config,
createNotification: createNotification,
permissionLevel: permissionLevel,
requestPermission: requestPermission
};
if (isFunction(Object.seal)) {
Object.seal(win.notify);
}
}(window));

/** @namespace window */
/** @namespace window.webkitNotifications */
/** @namespace window.external */
(function() {
/*
Safari native methods required for Notifications do NOT run in strict mode.
*/
//'use strict';
// local variables
var PERMISSION_DEFAULT = "default";
// The user decision is unknown; in this case the application will act as if permission was denied.
var PERMISSION_GRANTED = "granted";
// The user has explicitly granted permission for the current origin to display system notifications.
var PERMISSION_DENIED = "denied";
// The user has explicitly denied permission for the current origin to display system notifications.
var PERMISSION_NOTSUPPORTED = "notsupported";
// The Notification API is not supported on current environment
// map for the old permission values
var PERMISSIONS = [ PERMISSION_GRANTED, PERMISSION_DEFAULT, PERMISSION_DENIED, PERMISSION_NOTSUPPORTED ];
var DIRESCTIONS = [ "auto", "ltr", "rtl" ];
/*
IE does not support Notifications in the same meaning as other modern browsers.
On the other side, IE9+(except MS Edge) implement flashing pinned site taskbar buttons.
Each time new IE Notification is create, previous flashing and icon overlay is cleared.
So, we need to keep track of the notification that calls close method.
*/
var IENotificationIndex = -1;
var IECloseNotificationEvents = [ "click", "scroll", "focus" ];
var getIco = function(icon) {
var lastIndex = icon.lastIndexOf(".");
return (lastIndex !== -1 ? icon.substr(0, lastIndex) : icon) + ".ico";
};
/*
* Internal Notificaiton constructor. Keeps the original Notification
* constructor if any or use empty function constructor for browsers
* that do not support Notifications
*/
var _Notification = window.Notification || /* Opera Mobile/Android Browser */
window.webkitNotifications && WebKitNotification || /* IE9+ pinned site */
"external" in window && "msIsSiteMode" in window.external && window.external.msIsSiteMode() !== undefined && IENotification || /* Notifications Not supported. Return dummy constructor */
DummyNotification;
/**
* @constructor DummyNotification
*/
function DummyNotification() {
var dummyElement = document.createElement("div");
this.addEventListener = function(eventName, callback) {
dummyElement.addEventListener(eventName, callback.bind(this));
};
this.removeEventListener = function(eventName, callback) {
dummyElement.removeEventListener(eventName, callback.bind(this));
};
this.dispatchEvent = function(eventName) {
if (typeof eventName !== "string") {
return;
}
try {
dummyElement.dispatchEvent(new Event(eventName));
} catch (e) {
var event = document.createEvent("Event");
event.initEvent(eventName, false, true);
dummyElement.dispatchEvent(event);
}
};
}
Object.defineProperty(DummyNotification, "permission", {
enumerable: true,
get: function() {
return PERMISSION_NOTSUPPORTED;
}
});
Object.defineProperty(DummyNotification, "requestPermission", {
enumerable: true,
writable: true,
value: function(callback) {
callback(this.permission);
}
});
/**
* @constructor IENotification
*/
function IENotification(title, options) {
DummyNotification.call(this);
var notificationIndex = IENotificationIndex;
Object.defineProperties(this, {
close: {
value: function(event) {
if (notificationIndex === IENotificationIndex) {
window.external.msSiteModeClearIconOverlay();
// Remove close events
IECloseNotificationEvents.forEach(function(event) {
window.removeEventListener(event, this.close);
}.bind(this));
this.dispatchEvent("click");
this.dispatchEvent("close");
notificationIndex = null;
}
}.bind(this)
}
});
// Clear any previous icon overlay
this.close();
// Set icon
if (this.icon) {
window.external.msSiteModeSetIconOverlay(getIco(this.icon), this.description || this.title);
}
// Blink icon
window.external.msSiteModeActivate();
// Trigger show event
this.dispatchEvent("show");
// Attach close event to window
IECloseNotificationEvents.forEach(function(event) {
window.addEventListener(event, this.close);
}.bind(this));
// Increment notification index
notificationIndex = ++IENotificationIndex;
}
Object.defineProperty(IENotification, "permission", {
enumerable: true,
get: function() {
var isTabPinned = window.external.msIsSiteMode();
return isTabPinned ? PERMISSION_GRANTED : PERMISSION_DENIED;
}
});
Object.defineProperty(IENotification, "requestPermission", {
enumerable: true,
writable: true,
value: function(callback) {
return new Promise(function(resolve, reject) {
if (this.permission === PERMISSION_DENIED) {
alert(this.PERMISSION_REQUEST_MESSAGE);
}
resolve(this.permission);
}.bind(this));
}
});
Object.defineProperty(IENotification, "PERMISSION_REQUEST_MESSAGE", {
writable: true,
value: "IE supports notifications in pinned mode only. Pin this page on your taskbar to receive notifications."
});
/**
* @constructor WebKitNotification
*/
function WebKitNotification() {}
Object.defineProperty(WebKitNotification, "permission", {
enumerable: true,
get: function() {
return PERMISSIONS[window.webkitNotifications.checkPermission()];
}
});
Object.defineProperty(WebKitNotification, "requestPermission", {
enumerable: true,
writable: true,
value: function(callback) {
return new Promise(function(resolve, reject) {
window.webkitNotifications.requestPermission(function(permission) {
resolve(permission);
});
});
}
});
/*
[Safari] Safari6 do not support Notification.permission.
Instead, it support Notification.permissionLevel()
*/
if (!_Notification.permission) {
Object.defineProperty(_Notification, "permission", {
enumerable: true,
get: function() {
return _Notification.permissionLevel && _Notification.permissionLevel();
}
});
}
/**
* @constructor Notification
*/
function Notification(title, options) {
var dir;
var notification;
if (!arguments.length) {
throw TypeError('Failed to construct "Notification": 1 argument required, but only 0 present.');
}
/*
Chrome display notifications when title is empty screen, but
Safari do NOT.
Set title to non-display characted in order to display notifications
in Safari as well when title is empty.
*/
if (title === "") {
title = "\b";
}
if (arguments.length > 1 && "object" !== typeof options) {
throw TypeError('Failed to construct "Notification": parameter 2 ("options") is not an object.');
}
dir = Object(options).dir;
if (dir !== undefined && DIRESCTIONS.indexOf(dir) === -1) {
throw TypeError('Failed to construct "Notification": The provided value "' + dir + '" is not a valid enum value of type NotificationDirection.');
}
options = Object(options);
notification = new _Notification(title, options);
/* TODO: actions property */
/* TODO: badge property */
if (!notification.body) {
Object.defineProperty(notification, "body", {
value: String(options.body || "")
});
}
if (!notification.data) {
Object.defineProperty(notification, "data", {
value: options.data || null
});
}
if (!notification.dir) {
Object.defineProperty(notification, "dir", {
value: dir || DIRESCTIONS[0]
});
}
if (!notification.icon) {
Object.defineProperty(notification, "icon", {
value: String(options.icon || "")
});
}
if (!notification.lang) {
Object.defineProperty(notification, "lang", {
value: String(options.lang || "")
});
}
/* TODO: noscreen property */
/* TODO: renotify property */
if (!notification.requireInteraction) {
Object.defineProperty(notification, "requireInteraction", {
value: Boolean(options.requireInteraction)
});
}
/* TODO: sound property */
if (!notification.silent) {
Object.defineProperty(notification, "silent", {
value: Boolean(options.silent)
});
}
if (!notification.tag) {
Object.defineProperty(notification, "tag", {
value: String(options.tag || "")
});
}
if (!notification.title) {
Object.defineProperty(notification, "title", {
value: String(title)
});
}
if (!notification.timestamp) {
Object.defineProperty(notification, "timestamp", {
value: new Date().getTime()
});
}
/* TODO: vibrate property */
return notification;
}
Object.defineProperty(Notification, "permission", {
enumerable: true,
get: function() {
return _Notification.permission;
}
});
/*
Notification.requestPermission should return a Promise(by spec).
Keep the original method and replace it with a custom one that
checks if the call of Notification.requestPermission returns a promise
and if not, then return a custom object that simulates the Promise object.
Specification:
Notification.requestPermission().then(callback);
Old Spec:
Notification.requestPermission(callback);
*/
Object.defineProperty(Notification, "requestPermission", {
enumerable: true,
value: function() {
return new Promise(function(resolve, reject) {
var promise = _Notification.requestPermission(function(permission) {
resolve(permission);
});
if (!(promise instanceof Promise)) {
return;
}
resolve(promise);
});
}
});
window.Notification = Notification;
})();

+ 27
- 27
modules/Base/Notify/js/main.js View File

@@ -21,11 +21,11 @@ var Base_Notify = {
refresh: function () {
if (!this.is_active()) return;

if(this.working) return;
this.working = 1;
if(Base_Notify.working) return;
Base_Notify.working = 1;

jq.getJSON('modules/Base/Notify/refresh.php', function(json){
this.working = 0;
Base_Notify.working = 0;

if (typeof json === 'undefined' || jq.isEmptyObject(json)) return;
if (typeof json.disable !== 'undefined') {
@@ -37,47 +37,47 @@ var Base_Notify = {
jq.each(json.messages, function(i, m) {
setTimeout(function(){
if (typeof m.timeout !== 'undefined') notify.config({pageVisibility: false, autoClose: m.timeout});
Base_Notify.notify(m.title, m.opts);
Base_Notify.notify(m.title, m.opts, m.timeout);
}, i*500);
});
});
},
notify: function (title, opts) {
notify: function (title, opts, timeout) {
if (!this.is_active(true)) return;
if (notify.permissionLevel() === notify.PERMISSION_DEFAULT) {
notify.requestPermission(function (permission) {
if (permission === notify.PERMISSION_GRANTED) {
var n = notify.createNotification(title, opts);
}
var n;
if (Notification.permission === 'default') {
Notification.requestPermission().then(function (permission) {
Base_Notify.notify(title, opts, timeout);
});
}
else if (notify.permissionLevel() === notify.PERMISSION_GRANTED) {
var n = notify.createNotification(title, opts);
else if (Notification.permission === 'granted') {
n = new Notification(title, opts);
}

if (n && jq.isNumeric(timeout) && timeout > 5000) {
setInterval(n.close.bind(n), timeout);
}
},
is_active: function (alert) {
is_active: function (show_alert) {
if (this.disabled) return false;

if (Notification.permission === 'granted' || Notification.permission === 'default') return true;
if (!this.is_supported(alert)) {
this.disable();
return false;
if (show_alert) {
var message = this.disabled_message
if (Notification.permission === 'notsupported') {
message = 'Notifications not supported';
}
alert(message);
}
return true;
return false;
},
is_supported: function (alert) {
supported = notify.isSupported && (notify.permissionLevel() !== notify.PERMISSION_DENIED);
if (!supported && alert) alert(this.disabled_message);
return supported;
},

disable: function () {
clearInterval(this.interval);
this.interval = 0;


+ 3
- 0
modules/Base/Notify/refresh.php View File

@@ -14,6 +14,7 @@ define('READ_ONLY_SESSION', true);
require_once('../../../include.php');
ModuleManager::load_modules();

ob_start();
$token = Base_NotifyCommon::get_session_token(); // will check is user logged

if ($token === false) {
@@ -72,6 +73,8 @@ foreach ($notifications as $module => $module_new_notifications) {

Base_NotifyCommon::set_notified_cache($notified_cache, $token, $all_notified ? $refresh_time : Base_NotifyCommon::get_last_refresh($token));

ob_end_clean();

if (count($ret)) {
echo json_encode(array('messages' => $ret));
}


+ 7
- 1
modules/CRM/Calendar/Calendar_0.php View File

@@ -209,8 +209,14 @@ class CRM_Calendar extends Module {
///////////////////
// right column

$config = HTMLPurifier_Config::createDefault();
$config->set('HTML.AllowedElements','span');// $config->set('HTML.AllowedElements','span');
$purifier = new HTMLPurifier($config);
$row['title'] = $purifier->purify($row['title']);

$title = Utils_TooltipCommon::create($row['title'],$row['custom_tooltip']);
$day = (isset($row['timeless']) && $row['timeless'])?$row['timeless']:Base_RegionalSettingsCommon::time2reg($row['start'], false, true, true, false);
if ($day<date('Y-m-d')) $class = 'past';
elseif ($day==date('Y-m-d')) $class = 'today';


+ 10
- 2
modules/CRM/Filters/Filters_0.php View File

@@ -119,7 +119,11 @@ class CRM_Filters extends Module {
$users = array();
foreach ($cids as $v)
$users[] = CRM_ContactsCommon::contact_format_no_company(CRM_ContactsCommon::get_contact($v),true);
$gb_row->add_data($row['name'], $row['description'], implode(', ',$users));
Utils_SafeHtml_SafeHtml::setSafeHtml(new Utils_SafeHtml_HtmlPurifier());
$gb_row->add_data(
Utils_SafeHtml_SafeHtml::outputSafeHtml($row['name']),
Utils_SafeHtml_SafeHtml::outputSafeHtml($row['description']),
implode(', ',$users));
}

print('<div class="panel panel-default"><div class="panel panel-body">');
@@ -167,7 +171,11 @@ class CRM_Filters extends Module {
$form->addElement('automulti','contacts',__('Records of'),array('CRM_ContactsCommon','automulti_contact_suggestbox'), array(array(), array('CRM_ContactsCommon', 'contact_format_no_company')), array('CRM_ContactsCommon', 'contact_format_no_company'));
if ($form->validate()) {
$v = $form->exportValues();
if(isset($id)) {
Utils_SafeHtml_SafeHtml::setSafeHtml(new Utils_SafeHtml_HtmlPurifier());
foreach($v as $key => $value) {
$v[$key] = Utils_SafeHtml_SafeHtml::outputSafeHtml($value);
}
if(isset($id)) {
DB::Execute('UPDATE crm_filters_group SET name=%s,description=%s WHERE id=%d',array($v['name'],$v['description'],$id));
DB::Execute('DELETE FROM crm_filters_contacts WHERE group_id=%d',array($id));
} else {


+ 3
- 0
modules/CRM/Meeting/MeetingCommon_0.php View File

@@ -320,6 +320,9 @@ class CRM_MeetingCommon extends ModuleCommon {
return CRM_ContactsCommon::display_contacts_with_notification('crm_meeting', $record, $nolink, $desc);
}
public static function display_title($record, $nolink=false) {
Utils_SafeHtml_SafeHtml::setSafeHtml(new Utils_SafeHtml_HtmlPurifier());
$record['title'] = Utils_SafeHtml_SafeHtml::outputSafeHtml($record['title']);
$record['description'] = Utils_SafeHtml_SafeHtml::outputSafeHtml($record['description']);
$ret = Utils_RecordBrowserCommon::create_linked_label_r('crm_meeting', 'Title', $record, $nolink);
if (isset($record['description']) && $record['description']!='') $ret = '<span '.Utils_TooltipCommon::open_tag_attrs(Utils_RecordBrowserCommon::format_long_text($record['description']), false).'>'.$ret.'</span>';
return $ret;


+ 16
- 9
modules/CRM/PhoneCall/PhoneCallCommon_0.php View File

@@ -131,6 +131,13 @@ class CRM_PhoneCallCommon extends ModuleCommon {
}
}
public static function display_subject($record, $nolink = false) {
$config = HTMLPurifier_Config::createDefault();
$purifier = new HTMLPurifier($config);
$config->set('HTML.ForbiddenElements','a, i, script');
$record['subject'] = $purifier->purify($record['subject']);
$record['description'] = $purifier->purify($record['description']);
$ret = Utils_RecordBrowserCommon::create_linked_label_r('phonecall', 'Subject', $record, $nolink);
if (!$nolink && isset($record['description']) && $record['description']!='') $ret = '<span '.Utils_TooltipCommon::open_tag_attrs(Utils_RecordBrowserCommon::format_long_text($record['description']), false).'>'.$ret.'</span>';
return $ret;
@@ -231,14 +238,14 @@ class CRM_PhoneCallCommon extends ModuleCommon {
return Utils_RecordBrowserCommon::record_bbcode('phonecall', array('subject'), $text, $param, $opt);
}
public static function subscribed_employees($v) {
if (!is_array($v)) return;
foreach ($v['employees'] as $k) {
$user = Utils_RecordBrowserCommon::get_value('contact',$k,'Login');
if ($user!==false && $user!==null && is_numeric($user) && $user>0) Utils_WatchdogCommon::user_subscribe($user, 'crm_meeting', $v['id']);
}
}
public static function subscribed_employees($v) {
if (!is_array($v)) return;
foreach ($v['employees'] as $k) {
$user = Utils_RecordBrowserCommon::get_value('contact',$k,'Login');
if ($user!==false && $user!==null && is_numeric($user) && $user>0) Utils_WatchdogCommon::user_subscribe($user, 'crm_meeting', $v['id']);
}
}
public static function submit_phonecall($values, $mode) {
switch ($mode) {
case 'display':
@@ -296,7 +303,7 @@ class CRM_PhoneCallCommon extends ModuleCommon {
case 'added':
if (isset($values['follow_up']))
CRM_FollowupCommon::add_tracing_notes($values['follow_up'][0], $values['follow_up'][1], $values['follow_up'][2], 'phonecall', $values['id'], $values['subject']);
self::subscribed_employees($values);
self::subscribed_employees($values);
$related = $values['employees'];
if (!isset($values['other_customer'])) $related[] = $values['customer'];
foreach ($related as $v) {


+ 399
- 0
modules/CRM/Roundcube/RC/SQL/mssql.initial.sql View File

@@ -0,0 +1,399 @@
CREATE TABLE [dbo].[cache] (
[user_id] [int] NOT NULL ,
[cache_key] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
[expires] [datetime] NULL ,
[data] [text] COLLATE Latin1_General_CI_AI NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE TABLE [dbo].[cache_shared] (
[cache_key] [varchar] (255) COLLATE Latin1_General_CI_AI NOT NULL ,
[expires] [datetime] NULL ,
[data] [text] COLLATE Latin1_General_CI_AI NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE TABLE [dbo].[cache_index] (
[user_id] [int] NOT NULL ,
[mailbox] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
[expires] [datetime] NULL ,
[valid] [char] (1) COLLATE Latin1_General_CI_AI NOT NULL ,
[data] [text] COLLATE Latin1_General_CI_AI NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE TABLE [dbo].[cache_thread] (
[user_id] [int] NOT NULL ,
[mailbox] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
[expires] [datetime] NULL ,
[data] [text] COLLATE Latin1_General_CI_AI NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE TABLE [dbo].[cache_messages] (
[user_id] [int] NOT NULL ,
[mailbox] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
[uid] [int] NOT NULL ,
[expires] [datetime] NULL ,
[data] [text] COLLATE Latin1_General_CI_AI NOT NULL ,
[flags] [int] NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE TABLE [dbo].[contacts] (
[contact_id] [int] IDENTITY (1, 1) NOT NULL ,
[user_id] [int] NOT NULL ,
[changed] [datetime] NOT NULL ,
[del] [char] (1) COLLATE Latin1_General_CI_AI NOT NULL ,
[name] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
[email] [varchar] (8000) COLLATE Latin1_General_CI_AI NOT NULL ,
[firstname] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
[surname] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
[vcard] [text] COLLATE Latin1_General_CI_AI NULL ,
[words] [text] COLLATE Latin1_General_CI_AI NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE TABLE [dbo].[contactgroups] (
[contactgroup_id] [int] IDENTITY (1, 1) NOT NULL ,
[user_id] [int] NOT NULL ,
[changed] [datetime] NOT NULL ,
[del] [char] (1) COLLATE Latin1_General_CI_AI NOT NULL ,
[name] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[contactgroupmembers] (
[contactgroup_id] [int] NOT NULL ,
[contact_id] [int] NOT NULL ,
[created] [datetime] NOT NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[identities] (
[identity_id] [int] IDENTITY (1, 1) NOT NULL ,
[user_id] [int] NOT NULL ,
[changed] [datetime] NOT NULL ,
[del] [char] (1) COLLATE Latin1_General_CI_AI NOT NULL ,
[standard] [char] (1) COLLATE Latin1_General_CI_AI NOT NULL ,
[name] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
[organization] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
[email] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
[reply-to] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
[bcc] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
[signature] [text] COLLATE Latin1_General_CI_AI NULL,
[html_signature] [char] (1) COLLATE Latin1_General_CI_AI NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE TABLE [dbo].[session] (
[sess_id] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
[changed] [datetime] NULL ,
[ip] [varchar] (40) COLLATE Latin1_General_CI_AI NOT NULL ,
[vars] [text] COLLATE Latin1_General_CI_AI NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE TABLE [dbo].[users] (
[user_id] [int] IDENTITY (1, 1) NOT NULL ,
[username] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
[mail_host] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
[created] [datetime] NOT NULL ,
[last_login] [datetime] NULL ,
[failed_login] [datetime] NULL ,
[failed_login_counter] [int] NULL ,
[language] [varchar] (5) COLLATE Latin1_General_CI_AI NULL ,
[preferences] [text] COLLATE Latin1_General_CI_AI NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE TABLE [dbo].[dictionary] (
[user_id] [int] ,
[language] [varchar] (5) COLLATE Latin1_General_CI_AI NOT NULL ,
[data] [text] COLLATE Latin1_General_CI_AI NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE TABLE [dbo].[searches] (
[search_id] [int] IDENTITY (1, 1) NOT NULL ,
[user_id] [int] NOT NULL ,
[type] [tinyint] NOT NULL ,
[name] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
[data] [text] COLLATE Latin1_General_CI_AI NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE TABLE [dbo].[system] (
[name] [varchar] (64) COLLATE Latin1_General_CI_AI NOT NULL ,
[value] [text] COLLATE Latin1_General_CI_AI NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[cache] WITH NOCHECK ADD
PRIMARY KEY CLUSTERED
(
[user_id],[cache_key]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[cache_shared] WITH NOCHECK ADD
PRIMARY KEY CLUSTERED
(
[cache_key]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[cache_index] WITH NOCHECK ADD
PRIMARY KEY CLUSTERED
(
[user_id],[mailbox]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[cache_thread] WITH NOCHECK ADD
PRIMARY KEY CLUSTERED
(
[user_id],[mailbox]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[cache_messages] WITH NOCHECK ADD
PRIMARY KEY CLUSTERED
(
[user_id],[mailbox],[uid]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[contacts] WITH NOCHECK ADD
CONSTRAINT [PK_contacts_contact_id] PRIMARY KEY CLUSTERED
(
[contact_id]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[contactgroups] WITH NOCHECK ADD
CONSTRAINT [PK_contactgroups_contactgroup_id] PRIMARY KEY CLUSTERED
(
[contactgroup_id]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[contactgroupmembers] WITH NOCHECK ADD
CONSTRAINT [PK_contactgroupmembers_id] PRIMARY KEY CLUSTERED
(
[contactgroup_id], [contact_id]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[identities] WITH NOCHECK ADD
PRIMARY KEY CLUSTERED
(
[identity_id]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[session] WITH NOCHECK ADD
CONSTRAINT [PK_session_sess_id] PRIMARY KEY CLUSTERED
(
[sess_id]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[users] WITH NOCHECK ADD
CONSTRAINT [PK_users_user_id] PRIMARY KEY CLUSTERED
(
[user_id]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[searches] WITH NOCHECK ADD
CONSTRAINT [PK_searches_search_id] PRIMARY KEY CLUSTERED
(
[search_id]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[system] WITH NOCHECK ADD
CONSTRAINT [PK_system_name] PRIMARY KEY CLUSTERED
(
[name]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[cache] ADD
CONSTRAINT [DF_cache_user_id] DEFAULT ('0') FOR [user_id],
CONSTRAINT [DF_cache_cache_key] DEFAULT ('') FOR [cache_key],
GO
ALTER TABLE [dbo].[cache_index] ADD
CONSTRAINT [DF_cache_index_valid] DEFAULT ('0') FOR [valid]
GO
ALTER TABLE [dbo].[cache_messages] ADD
CONSTRAINT [DF_cache_messages_flags] DEFAULT (0) FOR [flags]
GO
CREATE INDEX [IX_cache_shared_cache_key] ON [dbo].[cache_shared]([cache_key]) ON [PRIMARY]
GO
CREATE INDEX [IX_cache_index_user_id] ON [dbo].[cache_index]([user_id]) ON [PRIMARY]
GO
CREATE INDEX [IX_cache_thread_user_id] ON [dbo].[cache_thread]([user_id]) ON [PRIMARY]
GO
CREATE INDEX [IX_cache_messages_user_id] ON [dbo].[cache_messages]([user_id]) ON [PRIMARY]
GO
CREATE INDEX [IX_cache_expires] ON [dbo].[cache]([expires]) ON [PRIMARY]
GO
CREATE INDEX [IX_cache_shared_expires] ON [dbo].[cache_shared]([expires]) ON [PRIMARY]
GO
CREATE INDEX [IX_cache_index_expires] ON [dbo].[cache_index]([expires]) ON [PRIMARY]
GO
CREATE INDEX [IX_cache_thread_expires] ON [dbo].[cache_thread]([expires]) ON [PRIMARY]
GO
CREATE INDEX [IX_cache_messages_expires] ON [dbo].[cache_messages]([expires]) ON [PRIMARY]
GO
ALTER TABLE [dbo].[contacts] ADD
CONSTRAINT [DF_contacts_user_id] DEFAULT (0) FOR [user_id],
CONSTRAINT [DF_contacts_changed] DEFAULT (getdate()) FOR [changed],
CONSTRAINT [DF_contacts_del] DEFAULT ('0') FOR [del],
CONSTRAINT [DF_contacts_name] DEFAULT ('') FOR [name],
CONSTRAINT [DF_contacts_email] DEFAULT ('') FOR [email],
CONSTRAINT [DF_contacts_firstname] DEFAULT ('') FOR [firstname],
CONSTRAINT [DF_contacts_surname] DEFAULT ('') FOR [surname],
CONSTRAINT [CK_contacts_del] CHECK ([del] = '1' or [del] = '0')
GO
CREATE INDEX [IX_contacts_user_id] ON [dbo].[contacts]([user_id]) ON [PRIMARY]
GO
ALTER TABLE [dbo].[contactgroups] ADD
CONSTRAINT [DF_contactgroups_user_id] DEFAULT (0) FOR [user_id],
CONSTRAINT [DF_contactgroups_changed] DEFAULT (getdate()) FOR [changed],
CONSTRAINT [DF_contactgroups_del] DEFAULT ('0') FOR [del],
CONSTRAINT [DF_contactgroups_name] DEFAULT ('') FOR [name],
CONSTRAINT [CK_contactgroups_del] CHECK ([del] = '1' or [del] = '0')
GO
CREATE INDEX [IX_contactgroups_user_id] ON [dbo].[contactgroups]([user_id]) ON [PRIMARY]
GO
ALTER TABLE [dbo].[contactgroupmembers] ADD
CONSTRAINT [DF_contactgroupmembers_contactgroup_id] DEFAULT (0) FOR [contactgroup_id],
CONSTRAINT [DF_contactgroupmembers_contact_id] DEFAULT (0) FOR [contact_id],
CONSTRAINT [DF_contactgroupmembers_created] DEFAULT (getdate()) FOR [created]
GO
CREATE INDEX [IX_contactgroupmembers_contact_id] ON [dbo].[contactgroupmembers]([contact_id]) ON [PRIMARY]
GO
ALTER TABLE [dbo].[identities] ADD
CONSTRAINT [DF_identities_user] DEFAULT ('0') FOR [user_id],
CONSTRAINT [DF_identities_del] DEFAULT ('0') FOR [del],
CONSTRAINT [DF_identities_standard] DEFAULT ('0') FOR [standard],
CONSTRAINT [DF_identities_name] DEFAULT ('') FOR [name],
CONSTRAINT [DF_identities_organization] DEFAULT ('') FOR [organization],
CONSTRAINT [DF_identities_email] DEFAULT ('') FOR [email],
CONSTRAINT [DF_identities_reply] DEFAULT ('') FOR [reply-to],
CONSTRAINT [DF_identities_bcc] DEFAULT ('') FOR [bcc],
CONSTRAINT [DF_identities_html_signature] DEFAULT ('0') FOR [html_signature],
CHECK ([standard] = '1' or [standard] = '0'),
CHECK ([del] = '1' or [del] = '0')
GO
CREATE INDEX [IX_identities_user_id] ON [dbo].[identities]([user_id]) ON [PRIMARY]
GO
CREATE INDEX [IX_identities_email] ON [dbo].[identities]([email],[del]) ON [PRIMARY]
GO
ALTER TABLE [dbo].[session] ADD
CONSTRAINT [DF_session_sess_id] DEFAULT ('') FOR [sess_id],
CONSTRAINT [DF_session_ip] DEFAULT ('') FOR [ip]
GO
CREATE INDEX [IX_session_changed] ON [dbo].[session]([changed]) ON [PRIMARY]
GO
ALTER TABLE [dbo].[users] ADD
CONSTRAINT [DF_users_username] DEFAULT ('') FOR [username],
CONSTRAINT [DF_users_mail_host] DEFAULT ('') FOR [mail_host],
CONSTRAINT [DF_users_created] DEFAULT (getdate()) FOR [created]
GO
CREATE UNIQUE INDEX [IX_users_username] ON [dbo].[users]([username],[mail_host]) ON [PRIMARY]
GO
CREATE UNIQUE INDEX [IX_dictionary_user_language] ON [dbo].[dictionary]([user_id],[language]) ON [PRIMARY]
GO
ALTER TABLE [dbo].[searches] ADD
CONSTRAINT [DF_searches_user] DEFAULT (0) FOR [user_id],
CONSTRAINT [DF_searches_type] DEFAULT (0) FOR [type]
GO
CREATE UNIQUE INDEX [IX_searches_user_type_name] ON [dbo].[searches]([user_id],[type],[name]) ON [PRIMARY]
GO
ALTER TABLE [dbo].[identities] ADD CONSTRAINT [FK_identities_user_id]
FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])
ON DELETE CASCADE ON UPDATE CASCADE
GO
ALTER TABLE [dbo].[contacts] ADD CONSTRAINT [FK_contacts_user_id]
FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])
ON DELETE CASCADE ON UPDATE CASCADE
GO
ALTER TABLE [dbo].[contactgroups] ADD CONSTRAINT [FK_contactgroups_user_id]
FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])
ON DELETE CASCADE ON UPDATE CASCADE
GO
ALTER TABLE [dbo].[cache] ADD CONSTRAINT [FK_cache_user_id]
FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])
ON DELETE CASCADE ON UPDATE CASCADE
GO
ALTER TABLE [dbo].[cache_index] ADD CONSTRAINT [FK_cache_index_user_id]
FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])
ON DELETE CASCADE ON UPDATE CASCADE
GO
ALTER TABLE [dbo].[cache_thread] ADD CONSTRAINT [FK_cache_thread_user_id]
FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])
ON DELETE CASCADE ON UPDATE CASCADE
GO
ALTER TABLE [dbo].[cache_messages] ADD CONSTRAINT [FK_cache_messages_user_id]
FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])
ON DELETE CASCADE ON UPDATE CASCADE
GO
ALTER TABLE [dbo].[contactgroupmembers] ADD CONSTRAINT [FK_contactgroupmembers_contactgroup_id]
FOREIGN KEY ([contactgroup_id]) REFERENCES [dbo].[contactgroups] ([contactgroup_id])
ON DELETE CASCADE ON UPDATE CASCADE
GO
ALTER TABLE [dbo].[searches] ADD CONSTRAINT [FK_searches_user_id]
FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])
ON DELETE CASCADE ON UPDATE CASCADE
GO
-- Use trigger instead of foreign key (#1487112)
-- "Introducing FOREIGN KEY constraint ... may cause cycles or multiple cascade paths."
CREATE TRIGGER [contact_delete_member] ON [dbo].[contacts]
AFTER DELETE AS
DELETE FROM [dbo].[contactgroupmembers]
WHERE [contact_id] IN (SELECT [contact_id] FROM deleted)
GO
INSERT INTO [dbo].[system] ([name], [value]) VALUES ('roundcube-version', '2016112200')
GO

+ 87
- 0
modules/CRM/Roundcube/RC/SQL/mssql/2009103100.sql View File

@@ -0,0 +1,87 @@
-- Updates from version 0.3.1

ALTER TABLE [dbo].[messages] ADD CONSTRAINT [FK_messages_user_id]
FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])
ON DELETE CASCADE ON UPDATE CASCADE
GO

ALTER TABLE [dbo].[cache] ADD CONSTRAINT [FK_cache_user_id]
FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])
ON DELETE CASCADE ON UPDATE CASCADE
GO

ALTER TABLE [dbo].[contacts] ADD CONSTRAINT [FK_contacts_user_id]
FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])
ON DELETE CASCADE ON UPDATE CASCADE
GO

ALTER TABLE [dbo].[identities] ADD CONSTRAINT [FK_identities_user_id]
FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])
ON DELETE CASCADE ON UPDATE CASCADE
GO

ALTER TABLE [dbo].[identities] ADD [changed] [datetime] NULL
GO

CREATE TABLE [dbo].[contactgroups] (
[contactgroup_id] [int] IDENTITY (1, 1) NOT NULL ,
[user_id] [int] NOT NULL ,
[changed] [datetime] NOT NULL ,
[del] [char] (1) COLLATE Latin1_General_CI_AI NOT NULL ,
[name] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[contactgroupmembers] (
[contactgroup_id] [int] NOT NULL ,
[contact_id] [int] NOT NULL ,
[created] [datetime] NOT NULL
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[contactgroups] WITH NOCHECK ADD
CONSTRAINT [PK_contactgroups_contactgroup_id] PRIMARY KEY CLUSTERED
(
[contactgroup_id]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[contactgroupmembers] WITH NOCHECK ADD
CONSTRAINT [PK_contactgroupmembers_id] PRIMARY KEY CLUSTERED
(
[contactgroup_id], [contact_id]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[contactgroups] ADD
CONSTRAINT [DF_contactgroups_user_id] DEFAULT (0) FOR [user_id],
CONSTRAINT [DF_contactgroups_changed] DEFAULT (getdate()) FOR [changed],
CONSTRAINT [DF_contactgroups_del] DEFAULT ('0') FOR [del],
CONSTRAINT [DF_contactgroups_name] DEFAULT ('') FOR [name],
CONSTRAINT [CK_contactgroups_del] CHECK ([del] = '1' or [del] = '0')
GO

CREATE INDEX [IX_contactgroups_user_id] ON [dbo].[contacts]([user_id]) ON [PRIMARY]
GO

ALTER TABLE [dbo].[contactgroupmembers] ADD
CONSTRAINT [DF_contactgroupmembers_contactgroup_id] DEFAULT (0) FOR [contactgroup_id],
CONSTRAINT [DF_contactgroupmembers_contact_id] DEFAULT (0) FOR [contact_id],
CONSTRAINT [DF_contactgroupmembers_created] DEFAULT (getdate()) FOR [created]
GO

ALTER TABLE [dbo].[contactgroupmembers] ADD CONSTRAINT [FK_contactgroupmembers_contactgroup_id]
FOREIGN KEY ([contactgroup_id]) REFERENCES [dbo].[contactgroups] ([contactgroup_id])
ON DELETE CASCADE ON UPDATE CASCADE
GO

CREATE TRIGGER [contact_delete_member] ON [dbo].[contacts]
AFTER DELETE AS
DELETE FROM [dbo].[contactgroupmembers]
WHERE [contact_id] IN (SELECT [contact_id] FROM deleted)
GO

ALTER TABLE [dbo].[contactgroups] ADD CONSTRAINT [FK_contactgroups_user_id]
FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])
ON DELETE CASCADE ON UPDATE CASCADE
GO

+ 9
- 0
modules/CRM/Roundcube/RC/SQL/mssql/2010100600.sql View File

@@ -0,0 +1,9 @@
-- Updates from version 0.4.2
DROP INDEX [IX_users_username]
GO
CREATE UNIQUE INDEX [IX_users_username] ON [dbo].[users]([username],[mail_host]) ON [PRIMARY]
GO
ALTER TABLE [dbo].[contacts] ALTER COLUMN [email] [varchar] (255) COLLATE Latin1_General_CI_AI NOT NULL
GO

+ 10
- 0
modules/CRM/Roundcube/RC/SQL/mssql/2011011200.sql View File

@@ -0,0 +1,10 @@
-- Updates from version 0.5.x

ALTER TABLE [dbo].[contacts] ADD [words] [text] COLLATE Latin1_General_CI_AI NULL
GO
CREATE INDEX [IX_contactgroupmembers_contact_id] ON [dbo].[contactgroupmembers]([contact_id]) ON [PRIMARY]
GO
DELETE FROM [dbo].[messages]
GO
DELETE FROM [dbo].[cache]
GO

+ 127
- 0
modules/CRM/Roundcube/RC/SQL/mssql/2011092800.sql View File

@@ -0,0 +1,127 @@
-- Updates from version 0.6

CREATE TABLE [dbo].[dictionary] (
[user_id] [int] ,
[language] [varchar] (5) COLLATE Latin1_General_CI_AI NOT NULL ,
[data] [text] COLLATE Latin1_General_CI_AI NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE UNIQUE INDEX [IX_dictionary_user_language] ON [dbo].[dictionary]([user_id],[language]) ON [PRIMARY]
GO

CREATE TABLE [dbo].[searches] (
[search_id] [int] IDENTITY (1, 1) NOT NULL ,
[user_id] [int] NOT NULL ,
[type] [tinyint] NOT NULL ,
[name] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
[data] [text] COLLATE Latin1_General_CI_AI NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

ALTER TABLE [dbo].[searches] WITH NOCHECK ADD
CONSTRAINT [PK_searches_search_id] PRIMARY KEY CLUSTERED
(
[search_id]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[searches] ADD
CONSTRAINT [DF_searches_user] DEFAULT (0) FOR [user_id],
CONSTRAINT [DF_searches_type] DEFAULT (0) FOR [type],
GO

CREATE UNIQUE INDEX [IX_searches_user_type_name] ON [dbo].[searches]([user_id],[type],[name]) ON [PRIMARY]
GO

ALTER TABLE [dbo].[searches] ADD CONSTRAINT [FK_searches_user_id]
FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])
ON DELETE CASCADE ON UPDATE CASCADE
GO

DROP TABLE [dbo].[messages]
GO
CREATE TABLE [dbo].[cache_index] (
[user_id] [int] NOT NULL ,
[mailbox] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
[changed] [datetime] NOT NULL ,
[valid] [char] (1) COLLATE Latin1_General_CI_AI NOT NULL ,
[data] [text] COLLATE Latin1_General_CI_AI NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

CREATE TABLE [dbo].[cache_thread] (
[user_id] [int] NOT NULL ,
[mailbox] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
[changed] [datetime] NOT NULL ,
[data] [text] COLLATE Latin1_General_CI_AI NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

CREATE TABLE [dbo].[cache_messages] (
[user_id] [int] NOT NULL ,
[mailbox] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
[uid] [int] NOT NULL ,
[changed] [datetime] NOT NULL ,
[data] [text] COLLATE Latin1_General_CI_AI NOT NULL ,
[flags] [int] NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

ALTER TABLE [dbo].[cache_index] WITH NOCHECK ADD
PRIMARY KEY CLUSTERED
(
[user_id],[mailbox]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[cache_thread] WITH NOCHECK ADD
PRIMARY KEY CLUSTERED
(
[user_id],[mailbox]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[cache_messages] WITH NOCHECK ADD
PRIMARY KEY CLUSTERED
(
[user_id],[mailbox],[uid]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[cache_index] ADD
CONSTRAINT [DF_cache_index_changed] DEFAULT (getdate()) FOR [changed],
CONSTRAINT [DF_cache_index_valid] DEFAULT ('0') FOR [valid]
GO

CREATE INDEX [IX_cache_index_user_id] ON [dbo].[cache_index]([user_id]) ON [PRIMARY]
GO

ALTER TABLE [dbo].[cache_thread] ADD
CONSTRAINT [DF_cache_thread_changed] DEFAULT (getdate()) FOR [changed]
GO

CREATE INDEX [IX_cache_thread_user_id] ON [dbo].[cache_thread]([user_id]) ON [PRIMARY]
GO

ALTER TABLE [dbo].[cache_messages] ADD
CONSTRAINT [DF_cache_messages_changed] DEFAULT (getdate()) FOR [changed],
CONSTRAINT [DF_cache_messages_flags] DEFAULT (0) FOR [flags]
GO

CREATE INDEX [IX_cache_messages_user_id] ON [dbo].[cache_messages]([user_id]) ON [PRIMARY]
GO

ALTER TABLE [dbo].[cache_index] ADD CONSTRAINT [FK_cache_index_user_id]
FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])
ON DELETE CASCADE ON UPDATE CASCADE
GO

ALTER TABLE [dbo].[cache_thread] ADD CONSTRAINT [FK_cache_thread_user_id]
FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])
ON DELETE CASCADE ON UPDATE CASCADE
GO

ALTER TABLE [dbo].[cache_messages] ADD CONSTRAINT [FK_cache_messages_user_id]
FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])
ON DELETE CASCADE ON UPDATE CASCADE
GO